-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
889 additions
and
376 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import * as UCAN from "../ucan.js" | ||
import * as CBOR from "@ipld/dag-cbor" | ||
import * as Parser from "../parser.js" | ||
import * as View from "../view.js" | ||
import * as DID from "../did.js" | ||
import { CID } from "multiformats/cid" | ||
|
||
export const name = "dag-ucan" | ||
export const code = CBOR.code | ||
|
||
/** | ||
* Encodes given UCAN (in either IPLD or JWT representation) and encodes it into | ||
* corresponding bytes representation. UCAN in IPLD representation is encoded as | ||
* DAG-CBOR which JWT representation is encoded as raw bytes of JWT string. | ||
* | ||
* @template {UCAN.Capability} C | ||
* @param {UCAN.Model<C>} ucan | ||
* @returns {UCAN.ByteView<UCAN.Model<C>>} | ||
*/ | ||
export const encode = ucan => { | ||
const { facts, nonce, notBefore, ...rest } = match(ucan) | ||
return CBOR.encode({ | ||
...rest, | ||
// leave out optionals unless they are set | ||
...(facts.length > 0 && { facts }), | ||
...(ucan.nonce && { nonce }), | ||
...(ucan.notBefore && { notBefore: ucan.notBefore }), | ||
signature: Parser.parseBytes(ucan.signature, "signature"), | ||
}) | ||
} | ||
|
||
/** | ||
* Decodes UCAN in primary CBOR representation. It does not validate UCAN, it's | ||
* signature or proof chain. This is to say decoded UCAN may be invalid. | ||
* | ||
* @template {UCAN.Capability} C | ||
* @param {UCAN.ByteView<UCAN.Model<C>>} bytes | ||
* @returns {UCAN.View<C>} | ||
*/ | ||
export const decode = bytes => View.cbor(match(CBOR.decode(bytes))) | ||
|
||
/** | ||
* @template {UCAN.Capability} C | ||
* @param {{[key in PropertyKey]: unknown}|UCAN.Model<C>} data | ||
* @returns {UCAN.Model<C>} | ||
*/ | ||
export const match = data => ({ | ||
version: Parser.parseVersion(data.version, "version"), | ||
issuer: parseDID(data.issuer, "issuer"), | ||
audience: parseDID(data.audience, "audience"), | ||
capabilities: /** @type {C[]} */ ( | ||
Parser.parseCapabilities(data.capabilities, "capabilities") | ||
), | ||
expiration: Parser.parseInt(data.expiration, "expiration"), | ||
proofs: Parser.parseOptionalArray(data.proofs, parseProof, "proofs") || [], | ||
signature: Parser.parseBytes(data.signature, "signature"), | ||
nonce: Parser.parseOptionalString(data.nonce, "nonce"), | ||
facts: Parser.parseOptionalArray(data.facts, Parser.parseFact, "facts") || [], | ||
notBefore: Parser.parseOptionalInt(data.notBefore, "notBefore"), | ||
}) | ||
|
||
/** | ||
* @template {UCAN.Capability} C | ||
* @param {unknown} cid | ||
* @param {string} context | ||
*/ | ||
const parseProof = (cid, context) => | ||
/** @type {UCAN.Proof<C>} */ (CID.asCID(cid)) || | ||
Parser.ParseError.throw( | ||
`Expected ${context} to be CID, instead got ${JSON.stringify(cid)}` | ||
) | ||
|
||
/** | ||
* | ||
* @param {unknown} input | ||
* @param {string} context | ||
*/ | ||
const parseDID = (input, context) => | ||
input instanceof Uint8Array | ||
? DID.decode(input) | ||
: Parser.ParseError.throw( | ||
`Expected ${context} to be Uint8Array, instead got ${JSON.stringify( | ||
input | ||
)}` | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import * as UCAN from "../ucan.js" | ||
import * as RAW from "multiformats/codecs/raw" | ||
import * as View from "../view.js" | ||
import * as UTF8 from "../utf8.js" | ||
import * as Parser from "../parser.js" | ||
|
||
export const name = "dag-ucan" | ||
export const code = RAW.code | ||
|
||
/** | ||
* Encodes given UCAN (in either JWT representation) and encodes it into | ||
* corresponding bytes representation. | ||
* | ||
* @template {UCAN.Capability} C | ||
* @param {UCAN.RAW<C>} ucan | ||
* @returns {UCAN.ByteView<UCAN.JWT<C>>} | ||
*/ | ||
export const encode = ucan => | ||
new Uint8Array(ucan.buffer, ucan.byteOffset, ucan.byteLength) | ||
|
||
/** | ||
* @template {UCAN.Capability} C | ||
* @param {UCAN.ByteView<UCAN.JWT<C>>} bytes | ||
* @returns {UCAN.View<C>} | ||
*/ | ||
export const decode = bytes => View.jwt(Parser.parse(UTF8.decode(bytes)), bytes) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import * as UCAN from "./ucan.js" | ||
import { base58btc } from "multiformats/bases/base58" | ||
import { varint } from "multiformats" | ||
|
||
const DID_KEY_PREFIX = `did:key:` | ||
export const ED25519 = 0xed | ||
export const RSA = 0x1205 | ||
|
||
/** | ||
* @param {Uint8Array} key | ||
* @returns {Code} | ||
*/ | ||
export const algorithm = key => { | ||
const [code] = varint.decode(key) | ||
switch (code) { | ||
case ED25519: | ||
case RSA: | ||
return code | ||
default: | ||
throw new RangeError( | ||
`Unsupported key algorithm with multicode 0x${code.toString(16)}.` | ||
) | ||
} | ||
} | ||
|
||
/** | ||
* @typedef {typeof ED25519|typeof RSA} Code | ||
*/ | ||
|
||
/** | ||
* @param {UCAN.DID} did | ||
* @returns {UCAN.DIDView} | ||
*/ | ||
export const parse = did => { | ||
if (!did.startsWith(DID_KEY_PREFIX)) { | ||
throw new RangeError(`Invalid DID "${did}", must start with 'did:key:'`) | ||
} | ||
return decode(base58btc.decode(did.slice(DID_KEY_PREFIX.length))) | ||
} | ||
|
||
/** | ||
* @param {UCAN.ByteView<UCAN.DID>} key | ||
* @returns {UCAN.DID} | ||
*/ | ||
export const format = key => | ||
/** @type {UCAN.DID} */ (`${DID_KEY_PREFIX}${base58btc.encode(encode(key))}`) | ||
|
||
/** | ||
* @param {Uint8Array} bytes | ||
* @returns {UCAN.DIDView} | ||
*/ | ||
export const decode = bytes => { | ||
const _ = algorithm(bytes) | ||
return new DID(bytes.buffer, bytes.byteOffset, bytes.byteLength) | ||
} | ||
|
||
/** | ||
* @param {Uint8Array} bytes | ||
* @returns {UCAN.ByteView<UCAN.DID>} | ||
*/ | ||
export const encode = bytes => { | ||
const _ = algorithm(bytes) | ||
return bytes | ||
} | ||
|
||
class DID extends Uint8Array { | ||
did() { | ||
return format(this) | ||
} | ||
} |
Oops, something went wrong.