import { sm2Curve } from './curve'; import { LdKeyPairStatic, LdKeyPairInstance, staticImplements, } from '@transmute/ld-key-pair'; import { base58 } from './encoding'; import { SM2_MULTICODEC_IDENTIFIER, VARIABLE_INTEGER_TRAILING_BYTE, } from './constants'; import { getMultibaseFingerprintFromPublicKeyBytes } from './getMultibaseFingerprintFromPublicKeyBytes'; import { JsonWebKey2020, Sm2VerificationKey2020, Sm2VerificationKey2019, } from './types'; import { importableTypes } from './importFrom'; import { exportableTypes } from './exportAs'; import { suiteTypes } from './suites'; const _generate = (secureRandom: () => Uint8Array) => { const privateKey = secureRandom(); return { privateKey, publicKey: sm2Curve.getPublicKey(secureRandom()), }; }; @staticImplements() export class Sm2KeyPair implements LdKeyPairInstance { public id: string; public type: string = 'JsonWebKey2020'; public controller: string; public publicKey: Uint8Array; public privateKey?: Uint8Array; static generate = async ({ secureRandom, }: { secureRandom: () => Uint8Array; }) => { const { publicKey, privateKey } = await _generate(secureRandom); const fingerprint = getMultibaseFingerprintFromPublicKeyBytes(publicKey); const controller = `did:key:${fingerprint}`; const id = `${controller}#${fingerprint}`; return new Sm2KeyPair({ id: id, type: 'JsonWebKey2020', controller: controller, publicKey: publicKey, privateKey: privateKey, }); }; static from = async ( k: JsonWebKey2020 | Sm2VerificationKey2019 | Sm2VerificationKey2020 ) => { const { publicKey, privateKey } = importableTypes[k.type](k as any); const sm2KeyPair = new Sm2KeyPair({ id: k.id, type: k.type, controller: k.controller, publicKey, privateKey, }); return sm2KeyPair; }; static async fromFingerprint({ fingerprint }: { fingerprint: string }) { const buffer = base58.decode(fingerprint.substring(1)); if ( buffer[0] === (SM2_MULTICODEC_IDENTIFIER & 0xff) && buffer[1] === SM2_MULTICODEC_IDENTIFIER >> 8 && buffer[2] === VARIABLE_INTEGER_TRAILING_BYTE ) { let kp = await Sm2KeyPair.from({ id: '', controller: '', type: 'Sm2VerificationKey2019', publicKeyBase58: base58.encode(buffer.slice(3)), }); const f = await kp.fingerprint(); kp.id = `did:key:${f}#${f}`; kp.controller = `did:key:${f}`; return kp; } throw new Error('Unsupported fingerprint type: ' + fingerprint); } constructor(opts: { id: string; type: string; controller: string; publicKey: Uint8Array; privateKey?: Uint8Array; }) { this.id = opts.id; this.type = opts.type || 'JsonWebKey2020'; this.controller = opts.controller; this.publicKey = opts.publicKey; this.privateKey = opts.privateKey; } async fingerprint() { return getMultibaseFingerprintFromPublicKeyBytes(this.publicKey); } signer(type: 'Sm2' = 'Sm2') { if (!this.privateKey) { throw new Error('No private key to sign with.'); } if (suiteTypes[type]) { return suiteTypes[type].signer(this.privateKey); } throw new Error('Unsupported suite type ' + type); } verifier(type: 'Sm2' = 'Sm2') { if (!this.publicKey) { throw new Error('No public key to verify with.'); } if (suiteTypes[type]) { return suiteTypes[type].verifier(this.publicKey); } throw new Error('Unsupported suite type ' + type); } async export( options: { privateKey?: boolean; type: | 'JsonWebKey2020' | 'Sm2VerificationKey2020' | 'Sm2VerificationKey2019'; } = { privateKey: false, type: 'JsonWebKey2020', } ): Promise { if (exportableTypes[options.type]) { return exportableTypes[options.type]( this.id, this.controller, this.publicKey, options.privateKey ? this.privateKey : undefined ); } throw new Error('Unsupported export options: ' + JSON.stringify(options)); } }