UNPKG

wallet-storage

Version:

BRC100 conforming wallet, wallet storage and wallet signer components

123 lines 7.52 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const sdk_1 = require("@bsv/sdk"); const index_all_1 = require("../../index.all"); describe('CertificateLifeCycle tests', () => { jest.setTimeout(99999999); test('0 encrypt decrypt sign verify', async () => { const subjectWallet = new sdk_1.CompletedProtoWallet(sdk_1.PrivateKey.fromRandom()); const { cert, certifier, subject } = makeSampleCert(subjectWallet.keyDeriver.rootKey.toString()); const c = new sdk_1.Certificate(cert.type, cert.serialNumber, cert.subject, cert.certifier, cert.revocationOutpoint, cert.fields); const certifierWallet = new sdk_1.CompletedProtoWallet(certifier); const imc = await sdk_1.MasterCertificate.issueCertificateForSubject(certifierWallet, c.subject, c.fields, c.type, async () => c.revocationOutpoint); const imcSignature = imc.signature; await expect(imc.sign(certifierWallet)).rejects.toThrow('Certificate has already been signed'); expect(imcSignature).toBeTruthy(); expect(imcSignature).toBe(imc.signature); const imcVerified = await imc.verify(); expect(imcVerified).toBe(true); const dfs = await imc.decryptFields(subjectWallet); for (const fn of Object.keys(cert.fields)) { // decrypted fields should be original un-encrypted fields expect(cert.fields[fn]).toBe(dfs[fn]); // issued certificate fields should encrypted, not the original un-encrypted fields expect(cert.fields[fn]).not.toBe(imc.fields[fn]); } await c.sign(certifierWallet); const verified = await c.verify(); expect(verified).toBe(true); const co = new index_all_1.sdk.CertOps(certifierWallet, cert); expect(co.signature).toBe(''); await expect(co.verify()).rejects.toThrow('Signature DER must start with 0x30'); await co.sign(new sdk_1.CompletedProtoWallet(new sdk_1.KeyDeriver(certifier))); expect(await co.verify()).toBe(true); { await co.encryptFields(subject.toPublicKey().toString()); await expect(co.verify()).rejects.toThrow('Signature is not valid'); co.signature = undefined; await co.sign(new sdk_1.CompletedProtoWallet(new sdk_1.KeyDeriver(certifier))); expect(await co.verify()).toBe(true); } await co.decryptFields(); for (const n of Object.keys(co.fields)) { expect(co.fields[n]).toBe(co._decryptedFields[n]); } { await co.encryptFields(); const crypto2 = new sdk_1.CompletedProtoWallet(new sdk_1.KeyDeriver(sdk_1.PrivateKey.fromHex('2'.repeat(64)))); const co2 = new index_all_1.sdk.CertOps(crypto2, co.toWalletCertificate()); // even with the keyring, without the right crypto root key decryption will fail. co2._keyring = co._keyring; await expect(co2.decryptFields()).rejects.toThrow('Decryption failed!'); } }); test('1 createKeyringForVerifier', async () => { const { cert, certifier, subject } = makeSampleCert(); const crypto = new sdk_1.CompletedProtoWallet(subject); const co = new index_all_1.sdk.CertOps(crypto, cert); }); test('2 complete flow', async () => { // Issuer beging with an un-encrypted (decrypted) raw certificate template: // The public keys of both the certifier (the authority issuing the certificate), // and the subject (who the certificate pertains to) are included in the certificate. const { cert, certifier, subject } = makeSampleCert(); // Next the certifier must encrypt the field values for privacy and sign the certificate // such that the values it contains can be attributed to the certifier through its public key. // Encryption is done with random symmetric keys and the keys are then encrypted by the certifier // such that each key can also be decrypted by the subject: const certifierWallet = new sdk_1.CompletedProtoWallet(certifier); const co = new index_all_1.sdk.CertOps(certifierWallet, cert); await co.encryptAndSignNewCertificate(); // Grab a copy of the certificate to send to the subject const exportForSubject = co.exportForSubject(); // The subject imports their copy of the new certificate: const subjectWallet = new sdk_1.CompletedProtoWallet(subject); const cs = await index_all_1.sdk.CertOps.fromCertifier(subjectWallet, exportForSubject); // The subject's imported certificate should verify expect(await cs.verify()).toBe(true); // Confirm subject can decrypt the certifier's copy of the cert: await co.decryptFields(subject.toPublicKey().toString()); // Confirm subject can decrypt their own copy of the cert: await cs.decryptFields(cs.certifier, co._keyring); // Restore the encrypted field values. cs.fields = cs._encryptedFields; // Prepare to send certificate to third party veifier of the 'name' and 'email' fields. // The verifier must be able to confirm the signature on the original certificate's encrypted values. // And then use a keyRing that their public key will work to reveal decrypted values for 'name' and 'email' only. const verifier = sdk_1.PrivateKey.fromRandom(); // subject makes a keyring for the verifier const exportForVerifier = await cs.exportForCounterparty(verifier.toPublicKey().toString(), ['name', 'email']); // The verifier uses their own wallet to import the certificate, verify it, and decrypt their designated fields. const verifierWallet = new sdk_1.CompletedProtoWallet(verifier); const cv = await index_all_1.sdk.CertOps.fromCounterparty(verifierWallet, exportForVerifier); // verifier must check that the certifier's public key generates a matching signature over all the encrypted // certificate field values before using their keyring to decrypt the fields they've been authorized to see. expect(await cv.verify()).toBe(true); // The wallet's private key is the verifier's, so the counterparty is the certificate subject (who sent the cert to verifier). // This decrypt is using the keyring provided for verification by the subject. await cv.decryptFields(); expect(cv.fields['name']).toBe('Alice'); expect(cv.fields['email']).toBe('alice@example.com'); expect(cv.fields['organization']).not.toBe('Example Corp'); }); }); function makeSampleCert(subjectRootKeyHex) { const subject = subjectRootKeyHex ? sdk_1.PrivateKey.fromString(subjectRootKeyHex) : sdk_1.PrivateKey.fromRandom(); const certifier = sdk_1.PrivateKey.fromRandom(); const verifier = sdk_1.PrivateKey.fromRandom(); const cert = { type: sdk_1.Utils.toBase64(new Array(32).fill(1)), serialNumber: sdk_1.Utils.toBase64(new Array(32).fill(2)), revocationOutpoint: 'deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef.1', subject: subject.toPublicKey().toString(), certifier: certifier.toPublicKey().toString(), fields: { name: 'Alice', email: 'alice@example.com', organization: 'Example Corp' }, signature: "", }; return { cert, subject, certifier }; } //# sourceMappingURL=CertificateLifeCycle.test.js.map