import { base64pad } from "multiformats/bases/base64"; import { Ed25519CreateConfig, Ed25519Signer, Ed25519Verifier, } from "./ed25519/index.ts"; import { AsBytes, DIDKey, KeyPairRaw, Signer, Verifier } from "./interface.ts"; import { hash } from "./utils.ts"; const textEncoder = new TextEncoder(); // Creation options used in `Identity` instantiation. export interface IdentityCreateConfig extends Ed25519CreateConfig {} // An `Identity` represents a public/private key pair. // // Additional keys can be deterministically derived from an identity. export class Identity implements Signer { #keypair: Ed25519Signer; #verifier: VerifierIdentity | null = null; constructor(keypair: Ed25519Signer) { this.#keypair = keypair; } did() { return this.verifier.did(); } get verifier(): VerifierIdentity { if (!this.#verifier) { this.#verifier = new VerifierIdentity(this.#keypair.verifier); } return this.#verifier; } // Sign `data` with this identity. sign(payload: AsBytes) { return this.#keypair.sign(payload); } // Serialize this identity for storage. serialize(): KeyPairRaw { return this.#keypair.serialize(); } // Derive a new `Identity` given a seed string. async derive( name: string, config: IdentityCreateConfig = {}, ): Promise> { const seed = textEncoder.encode(name); const { ok: signed, error } = await this.sign(seed); if (error) { throw error; } const signedHash = await hash(signed); return await Identity.fromRaw(new Uint8Array(signedHash), config); } // Derive PKCS8/PEM bytes from this identity. // Implementations other than "noble" throw as private key // material is needed to create the PKCS8/PEM bytes. toPkcs8(): Uint8Array { return this.#keypair.toPkcs8(); } // Generate a new identity from raw ed25519 key material. static async fromRaw( rawPrivateKey: Uint8Array, config: IdentityCreateConfig = {}, ): Promise> { return new Identity(await Ed25519Signer.fromRaw(rawPrivateKey, config)); } // Generate a new identity. static async generate( config: IdentityCreateConfig = {}, ): Promise> { return new Identity(await Ed25519Signer.generate(config)); } static async generateMnemonic( config: IdentityCreateConfig = {}, ): Promise< [Identity, string] > { const [signer, mnemonic] = await Ed25519Signer.generateMnemonic(config); return [new Identity(signer), mnemonic]; } // Generate a new keypair in PKCS8, PEM encoded form. // // Due to hiding access to private key material // in the JS environment (via WebCrypto), we cannot // simply "export" existing keys. If a key should // be stored as PKCS8, generate it with this method. static async generatePkcs8(): Promise { // Not a promise, but force it for consistent interface return await Ed25519Signer.generatePkcs8(); } // Read a PKCS8/PEM key. static async fromPkcs8( pkcs8: Uint8Array, config: IdentityCreateConfig = {}, ): Promise> { const signer = await Ed25519Signer.fromPkcs8(pkcs8, config); return new Identity(signer); } static async fromMnemonic( mnemonic: string, config: IdentityCreateConfig = {}, ): Promise> { const signer = await Ed25519Signer.fromMnemonic(mnemonic, config); return new Identity(signer); } static async fromPassphrase( passphrase: string, config: IdentityCreateConfig = {}, ): Promise> { const rawPrivateKey = await hash(new TextEncoder().encode(passphrase)); return new Identity(await Ed25519Signer.fromRaw(rawPrivateKey, config)); } static fromString( stringKey: string, config: IdentityCreateConfig = {}, ): Promise> { return Identity.fromRaw(base64pad.decode(stringKey), config); } // Deserialize `input` from storage into an `Identity`. static async deserialize( input: KeyPairRaw, ): Promise> { return new Identity(await Ed25519Signer.deserialize(input)); } } export class VerifierIdentity implements Verifier { #inner: Verifier; constructor(inner: Verifier) { this.#inner = inner; } verify(auth: { payload: Uint8Array; signature: Uint8Array }) { return this.#inner.verify(auth); } did(): ID { return this.#inner.did(); } static async fromDid( did: ID, ): Promise> { return new VerifierIdentity(await Ed25519Verifier.fromDid(did)); } }