Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: did prinicipal #149

Merged
merged 7 commits into from
Dec 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 183 additions & 25 deletions packages/interface/src/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ import {
Phantom,
Resource,
Signature,
Principal,
Verifier,
Signer as UCANSigner,
} from '@ipld/dag-ucan'
import { Link, Block as IPLDBlock } from 'multiformats'
import * as UCAN from '@ipld/dag-ucan'
Expand All @@ -35,7 +32,6 @@ export * from './capability.js'
export * from './transport.js'
export type {
Transport,
Principal,
Phantom,
Tuple,
DID,
Expand Down Expand Up @@ -67,6 +63,10 @@ export type Proof<C extends Capabilities = Capabilities> =
| UCANLink<C>
| Delegation<C>

export interface Principal<ID extends DID = DID> {
did(): ID
}

export interface UCANOptions {
audience: Principal
lifetimeInSeconds?: number
Expand Down Expand Up @@ -390,51 +390,209 @@ export interface PrincipalParser {
parse(did: UCAN.DID): Verifier
}

/**
* Integer corresponding to the byteprefix of the VarSig. It is used to tag
* signature with a registered multicodec code making it self describing.
* @see https://github.com/ucan-wg/ucan-ipld/#25-signature
*/
export type SigAlg = number

/**
* Represents component that can create a signer from it's archive. Usually
* signer module would provide `from` function and therefor be implementation
* signer module would provide `from` function and therefor be an implementation
* of this interface.
*
* Library also provides utility functions for combining multiple
* SignerImporters into one.
*
* @template ID - DID that can be imported, which may be a type union.
* @template Alg - Multicodec code corresponding to signature algorithm.
*/
export interface SignerImporter<Self extends Signer = Signer> {
from(archive: SignerArchive<Self>): Self
export interface SignerImporter<
ID extends DID = DID,
Alg extends SigAlg = SigAlg
> {
from(archive: SignerArchive<ID, Alg>): Signer<ID, Alg>
}

export interface Signer<M extends string = string, A extends number = number>
extends UCANSigner<M, A> {
/**
* Principal that can issue UCANs (and sign payloads). While it's primary role
* is to sign payloads it also extends `Verifier` interface so it could be used
* to verifying signed payloads as well.
*/
export interface Signer<ID extends DID = DID, Alg extends SigAlg = SigAlg>
extends Principal<ID>,
Verifier<ID, Alg> {
/**
* Returns archive of this signer which is byte encoded form when signer key
* is extractable and is {@link SignerInfo} form otherwise. This allows a user
* to store non extractable archives in indexedDB and store extractable
* archives on disk, which matches general expectation that in browsers
* unextratable keys should be used and extractable keys in node.
* Integer corresponding to the byteprefix of the {@link VarSig}. It is used
* to tag [signature] so it can self describe what algorithm was used.
*
* [signature]:https://github.com/ucan-wg/ucan-ipld/#25-signature
*/
signatureCode: Alg

/**
* Name of the signature algorithm. It is a human readable equivalent of
* the {@link signatureCode}, however it is also used as last segment in
* [Nonstandard Signatures], which is used as an `alg` field of JWT header
* when UCANs are serialized to JWT.
*
* [Nonstandard Signatures]:https://github.com/ucan-wg/ucan-ipld/#251-nonstandard-signatures
*/
signatureAlgorithm: string
Gozala marked this conversation as resolved.
Show resolved Hide resolved

/**
* The `signer` field is a self reference (usually a getter). It's sole
* purpose is to allow splitting signer and verifier through destructuring.
*
* @expample
* ```js
* import * as Principal from "@ucanto/principal"
*
* const { signer, verifier } = Principal.from(archive)
* ```
*/
signer: Signer<ID, Alg>

/**
* The `verifier` field just like the `signer` exists to allow splitting
* them apart through destructuring.
*/
verifier: Verifier<ID, Alg>

/**
* @template T - Source data before it was byte encoding into payload.
*
* Takes byte encoded payload and produces a verifiable signature.
*/
sign<T>(payload: ByteView<T>): Await<Signature<T, Alg>>

/**
* Returns archive of this signer which will have keys byte encoded when
* underlying keys are extractable or in {@link CryptoKey} form otherwise.
*
* This allows a storing non extractable archives into indexedDB and storing
* extractable archives on disk ofter serializing them using IPLD code.
*
* This aligns with a best practice that in browsers unextratable keys should
* be used and extractable keys in node.
*
* @example
* ```ts
* import * as CBOR from '@ipld/dag-cbor'
*
* const save = async (signer: Signer) => {
* const archive = signer.toArchive()
* if (archive instanceof Uint8Array) {
* await fs.writeFile(KEY_PATH, archive)
* } else {
* if (globalThis.indexedDB) {
* await IDB_OBJECT_STORE.add(archive)
* } else {
* await fs.writeFile(KEY_PATH, CBOR.encode(archive))
* }
* }
* ```
*/
toArchive(): SignerArchive<Signer<M, A>>
toArchive(): SignerArchive<ID, Alg>

/**
* Wraps key of this signer into a signer with a different DID. This is
* primarily used to wrap {@link SignerKey} into a {@link Signer} that has
* {@link did} of different method.
*
* @example
*
* ```ts
* import { ed25519 } from "@ucanto/principal"
*
* const demo = async () => {
* const key = await ed25519.generate()
* key.did() // 'did:key:z6Mkqa4oY9Z5Pf5tUcjLHLUsDjKwMC95HGXdE1j22jkbhz6r'
* const gozala = key.withDID('did:web:gozala.io')
* gozala.did() // 'did:web:gozala.io'
* }
* ```
* [did:key]:https://w3c-ccg.github.io/did-method-key/
*/
withDID<ID extends DID>(id: ID): Signer<ID, Alg>
}

export interface SignerInfo<Self extends Signer = Signer> {
readonly did: ReturnType<Self['did']>
readonly key: CryptoKey
/**
* Principal that issued a UCAN. In usually represents remote principal and is
* used to verify that certain payloads were signed by it.
*/
export interface Verifier<ID extends DID = DID, Alg extends SigAlg = SigAlg>
extends Principal<ID> {
/**
* @template T - Source data before it was byte encoding into payload.
*
* Takes byte encoded payload and verifies that it is signed by corresponding
* signer.
*/
verify<T>(payload: ByteView<T>, signature: Signature<T, Alg>): Await<boolean>

/**
* Wraps key of this verifire into a verifiier with a different DID. This is
* primarily used to wrap {@link VerifierKey} into a {@link Verifier} that has
* {@link did} of different method.
*/
withDID<ID extends DID>(id: ID): Verifier<ID, Alg>
}

export type SignerArchive<Self extends Signer = Signer> =
| ByteView<Self>
| SignerInfo<Self>
/**
* Represents [`did:key`] identifier.
*
* [`did:key`]:https://w3c-ccg.github.io/did-method-key/
*/
export type DIDKey = DID<'key'>

export { Verifier }
/**
* {@link Signer} corresponding to [`did:key`] identified principal.
*
* [`did:key`]:https://w3c-ccg.github.io/did-method-key/
*/
export interface SignerKey<Alg extends SigAlg = SigAlg>
extends Signer<DIDKey, Alg> {}

/**
* {@link Verifier} corresponding to [`did:key`] identified principal.
*
* [`did:key`]:https://w3c-ccg.github.io/did-method-key/
*/
export interface VerifierKey<Alg extends SigAlg = SigAlg>
extends Verifier<DIDKey, Alg> {}

/**
* {@link Signer} keys and it's DID that can be used for persist and restore
* signer across sessions.
*/
export interface SignerArchive<
ID extends DID = DID,
Alg extends SigAlg = SigAlg
> {
/**
* [DID Subject](https://www.w3.org/TR/did-core/#did-subject) for this
* signer.
*/
id: ID

/**
* Set of private keys this signer uses keyed by corresponding [did:key][].
*
* ⚠️ At the moment signers only support single key use case, however we may
* change that in the future, which is why data model is forward designed to
* support multiple keys.
*
* [did:key]:https://w3c-ccg.github.io/did-method-key/
*/
keys: { [Key: DIDKey]: KeyArchive<Alg> }
}

/**
* Represents a private key which will be in `CryptoKey` format if it is
* non-extractable or is byte encoded when extractable.
*/
export type KeyArchive<Alg extends SigAlg = SigAlg> =
| CryptoKey
Gozala marked this conversation as resolved.
Show resolved Hide resolved
| ByteView<SignerKey<Alg> & CryptoKey>

export type InferInvokedCapability<
C extends CapabilityParser<Match<ParsedCapability>>
Expand Down
Loading