diff --git a/README.md b/README.md index 1968018..2037391 100644 --- a/README.md +++ b/README.md @@ -24,13 +24,80 @@ npm install '@transmute/cose' ``` ```ts -import cose from "@transmute/cose"; +import * as cose from "@transmute/cose"; ``` ```js const cose = require("@transmute/cose"); ``` +```ts +const issuerSecretKeyJwk = await cose.key.generate( + "ES256", + "application/jwk+json" +); +const issuerPublicKeyJwk = await cose.key.publicFromPrivate( + issuerSecretKeyJwk +); + +const notarySecretKeyJwk = await cose.key.generate( + "ES256", + "application/jwk+json" +); +const notaryPublicKeyJwk = await cose.key.publicFromPrivate( + notarySecretKeyJwk +); +const issuer = cose.detached.signer({ secretKeyJwk: issuerSecretKeyJwk }); +const notary = cose.detached.signer({ secretKeyJwk: notarySecretKeyJwk }); + +const content = fs.readFileSync("./examples/image.png"); +const signatureForImage = await issuer.sign({ + protectedHeader: new Map([ + [1, -7], // signing algorithm ES256 + [3, "image/png"], // content type image/png + [4, issuerPublicKeyJwk.kid], // issuer key identifier + ]), + unprotectedHeader: new Map(), + payload: content, +}); +const transparencyLogContainingImageSignatures = [ + await cose.receipt.leaf(signatureForImage), +]; +const receiptForImageSignature = await cose.receipt.inclusion.issue({ + protectedHeader: new Map([ + [1, -7], // signing algorithm ES256 + [-111, 1], // inclusion proof from RFC9162 + [4, notaryPublicKeyJwk.kid], // notary key identifier + ]), + entry: 0, + entries: transparencyLogContainingImageSignatures, + signer: notary, +}); +const transparentSignature = await cose.receipt.add( + signatureForImage, + receiptForImageSignature +); +const resolve = async ( + header: cose.ProtectedHeaderMap +): Promise => { + const kid = header.get(4); + if (kid === issuerPublicKeyJwk.kid) { + return issuerPublicKeyJwk; + } + if (kid === notaryPublicKeyJwk.kid) { + return notaryPublicKeyJwk; + } + throw new Error("No verification key found in trust store."); +}; +const verifier = await cose.receipt.verifier({ + resolve, +}); +const verified = await verifier.verify({ + coseSign1: transparentSignature, + payload: content, +}); +``` + ## Develop ```bash diff --git a/test/readme.test.ts b/test/readme.test.ts new file mode 100644 index 0000000..2bb1b76 --- /dev/null +++ b/test/readme.test.ts @@ -0,0 +1,56 @@ +import fs from 'fs' +import * as cose from '../src' + +it('readme', async () => { + const issuerSecretKeyJwk = await cose.key.generate('ES256', 'application/jwk+json') + const issuerPublicKeyJwk = await cose.key.publicFromPrivate(issuerSecretKeyJwk) + + const notarySecretKeyJwk = await cose.key.generate('ES256', 'application/jwk+json') + const notaryPublicKeyJwk = await cose.key.publicFromPrivate(notarySecretKeyJwk) + const issuer = cose.detached.signer({ secretKeyJwk: issuerSecretKeyJwk }) + const notary = cose.detached.signer({ secretKeyJwk: notarySecretKeyJwk }) + + const content = fs.readFileSync('./examples/image.png') + const signatureForImage = await issuer.sign({ + protectedHeader: new Map([ + [1, -7], // signing algorithm ES256 + [3, "image/png"], // content type image/png + [4, issuerPublicKeyJwk.kid] // issuer key identifier + ]), + unprotectedHeader: new Map(), + payload: content + }) + const transparencyLogContainingImageSignatures = [ + await cose.receipt.leaf(signatureForImage) + ] + const receiptForImageSignature = await cose.receipt.inclusion.issue({ + protectedHeader: new Map([ + [1, -7], // signing algorithm ES256 + [-111, 1], // inclusion proof from RFC9162 + [4, notaryPublicKeyJwk.kid] // notary key identifier + ]), + entry: 0, + entries: transparencyLogContainingImageSignatures, + signer: notary + }) + const transparentSignature = await cose.receipt.add(signatureForImage, receiptForImageSignature) + const resolve = async (header: cose.ProtectedHeaderMap): Promise => { + const kid = header.get(4); + if (kid === issuerPublicKeyJwk.kid) { + return issuerPublicKeyJwk + } + if (kid === notaryPublicKeyJwk.kid) { + return notaryPublicKeyJwk + } + throw new Error('No verification key found in trust store.') + } + const verifier = await cose.receipt.verifier({ + resolve + }) + const verified = await verifier.verify({ + coseSign1: transparentSignature, + payload: content + }) + expect(verified.payload).toBeDefined() + expect(verified.receipts.length).toBe(1) +}) \ No newline at end of file diff --git a/test/verifiers.test.ts b/test/verifiers.test.ts index cd5f264..20a9e50 100644 --- a/test/verifiers.test.ts +++ b/test/verifiers.test.ts @@ -22,7 +22,7 @@ it('verify multiple receipts', async () => { const content = fs.readFileSync('./examples/image.png') const signatureForImage = await issuerSigner.sign({ protectedHeader: new Map([ - [2, issuerCkt], // kid urn:ietf:params:oauth:ckt:sha-256:T6ixLT_utMNJ... + [4, issuerCkt], // kid urn:ietf:params:oauth:ckt:sha-256:T6ixLT_utMNJ... [1, -7], // alg ES256 [3, "image/png"], // content_type image/png ]), @@ -33,7 +33,7 @@ it('verify multiple receipts', async () => { // inclusion proof receipt for image signature const receiptForImageSignature1 = await transmute.receipt.inclusion.issue({ protectedHeader: new Map([ - [2, notary1Ckt], // kid urn:ietf:params:oauth:ckt:sha-256:T6ixLT_utMNJ... + [4, notary1Ckt], // kid urn:ietf:params:oauth:ckt:sha-256:T6ixLT_utMNJ... [1, -7], // alg ES256 [-111, 1] // vds RFC9162 ]), @@ -43,7 +43,7 @@ it('verify multiple receipts', async () => { }) const receiptForImageSignature2 = await transmute.receipt.inclusion.issue({ protectedHeader: new Map([ - [2, notary2Ckt], // kid urn:ietf:params:oauth:ckt:sha-256:T6ixLT_utMNJ... + [4, notary2Ckt], // kid urn:ietf:params:oauth:ckt:sha-256:T6ixLT_utMNJ... [1, -7], // alg ES256 [-111, 1] // vds RFC9162 ]), @@ -54,7 +54,7 @@ it('verify multiple receipts', async () => { const transparentSignature1 = await transmute.receipt.add(signatureForImage, receiptForImageSignature1) const transparentSignature = await transmute.receipt.add(transparentSignature1, receiptForImageSignature2) const resolve = async (header: transmute.ProtectedHeaderMap): Promise => { - const kid = header.get(2); + const kid = header.get(4); if (kid === issuerCkt) { return transmute.key.convertCoseKeyToJsonWebKey( await transmute.key.publicFromPrivate(issuerSecretKey)