diff --git a/src/signing.ts b/src/signing.ts index 4348471..f497285 100644 --- a/src/signing.ts +++ b/src/signing.ts @@ -11,6 +11,7 @@ export interface DagJWS { payload: string signatures: Array link?: CID + pld?: Record } export interface EncodedSignature { @@ -52,11 +53,6 @@ function encodeSignature(signature: JWSSignature): EncodedSignature { export function encode(jws: DagJWS): EncodedJWS { const payload = fromBase64url(jws.payload) - try { - CID.decode(payload) - } catch (e) { - throw new Error('Not a valid DagJWS') - } return { payload, signatures: jws.signatures.map(encodeSignature), @@ -77,6 +73,41 @@ export function decode(encoded: EncodedJWS): DagJWS { payload: toBase64url(encoded.payload), signatures: encoded.signatures.map(decodeSignature), } - decoded.link = CID.decode(new Uint8Array(encoded.payload)) - return decoded + try { + decoded.pld = replaceCIDs(payloadToJSON(encoded.payload)) as Record + return decoded + } catch (e) { + try { + decoded.link = CID.decode(new Uint8Array(encoded.payload)) + return decoded + } catch (e) { + throw new Error('Invalid payload, must be either JSON or CID') + } + } +} + +function replaceCIDs(data: Record | any): Record | any { + if (typeof data === 'string') { + if (data.startsWith('ipfs://')) { + return CID.parse(data.slice(7)) + } + } else if (Array.isArray(data)) { + return data.map(replaceCIDs) as any + } else if (isObject(data)) { + const newObj = {} as Record + for (const key in data) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + newObj[key] = replaceCIDs(data[key]) + } + return newObj + } + return data +} + +function isObject(data: any): data is Record { + return typeof data === 'object' +} + +function payloadToJSON(data: Uint8Array): any { + return JSON.parse(new TextDecoder().decode(data)) } diff --git a/test/__fixtures__/signing.fixtures.ts b/test/__fixtures__/signing.fixtures.ts index 20c63f6..be65ef8 100644 --- a/test/__fixtures__/signing.fixtures.ts +++ b/test/__fixtures__/signing.fixtures.ts @@ -18,22 +18,38 @@ export const fixtures = { dagJws: { oneSig: [{ payload: 'AXASIN69ets85WVE0ipva5M5b2mAqAZ8LME08PeAG2MxCSuV', // signed by key 0 - link: CID.parse('bafybeig6xv5nwphfmvcnektpnojts33jqcuam7bmye2pb54adnrtccjlsu'), + link: CID.parse('bafybeig6xv5nwphfmvcnektpnojts33jqcuam7bmye2pb54adnrtccjlsu'), signatures: [ { protected: 'eyJhbGciOiJFUzI1NksifQ', signature: 'SiYGXW7Yi-KxbpIlLNmu0lEhrayV7ypaAC49GAcQ_qpTstZW89Mz6Cp8VlUEX-qVsgYjc-9-1zvLcDYlxOsr1g' } ] }, { payload: 'AXASIN69ets85WVE0ipva5M5b2mAqAZ8LME08PeAG2MxCSuV', // signed by key 1 - link: CID.parse('bafybeig6xv5nwphfmvcnektpnojts33jqcuam7bmye2pb54adnrtccjlsu'), + link: CID.parse('bafybeig6xv5nwphfmvcnektpnojts33jqcuam7bmye2pb54adnrtccjlsu'), signatures: [ { protected: 'eyJhbGciOiJFUzI1NksifQ', signature: 'Q8PdTE5A5N3a0ktO2wNdUymumHlSxNF9Si38IvzsMaSZC63yQw-bJNpKf-UeJFPH7cDzY7jLg2G_viejp7NqXg' } ] }], multipleSig: { payload: 'AXASIN69ets85WVE0ipva5M5b2mAqAZ8LME08PeAG2MxCSuV', - link: CID.parse('bafybeig6xv5nwphfmvcnektpnojts33jqcuam7bmye2pb54adnrtccjlsu'), + link: CID.parse('bafybeig6xv5nwphfmvcnektpnojts33jqcuam7bmye2pb54adnrtccjlsu'), signatures: [{ protected: 'eyJhbGciOiJFUzI1NksifQ', signature: 'SiYGXW7Yi-KxbpIlLNmu0lEhrayV7ypaAC49GAcQ_qpTstZW89Mz6Cp8VlUEX-qVsgYjc-9-1zvLcDYlxOsr1g' }, { protected: 'eyJhbGciOiJFUzI1NksifQ', signature: 'Q8PdTE5A5N3a0ktO2wNdUymumHlSxNF9Si38IvzsMaSZC63yQw-bJNpKf-UeJFPH7cDzY7jLg2G_viejp7NqXg' }] }, + withPayload: { + payload: 'eyJ0ZXN0IjoicGF5bG9hZCIsImFMaW5rIjoiaXBmczovL2JhZnliZWlnNnh2NW53cGhmbXZjbmVrdHBub2p0czMzanFjdWFtN2JteWUycGI1NGFkbnJ0Y2NqbHN1IiwiYXJyIjpbImlwZnM6Ly9iYWZ5YmVpZzZ4djVud3BoZm12Y25la3Rwbm9qdHMzM2pxY3VhbTdibXllMnBiNTRhZG5ydGNjamxzdSIsIml0ZW0xIiwiaXRlbTIiXSwibmVzdGVkIjp7ImFMaW5rIjoiaXBmczovL2JhZnliZWlnNnh2NW53cGhmbXZjbmVrdHBub2p0czMzanFjdWFtN2JteWUycGI1NGFkbnJ0Y2NqbHN1IiwiYXJyIjpbImlwZnM6Ly9iYWZ5YmVpZzZ4djVud3BoZm12Y25la3Rwbm9qdHMzM2pxY3VhbTdibXllMnBiNTRhZG5ydGNjamxzdSIsIml0ZW0xIiwiaXRlbTIiXX19', + signatures: [{ + protected: 'eyJhbGciOiJFUzI1NksifQ', + signature: 'IY8AHhQB0n5QpO1opse83d6HrXWfofPjW6if5vVB7Dky3wj2-daT8I1xHX5s4PbuC3owZo2rr8C-nGQsftHE-w' + }], + pld: { + test: 'payload', + aLink: CID.parse('bafybeig6xv5nwphfmvcnektpnojts33jqcuam7bmye2pb54adnrtccjlsu'), + arr: [ CID.parse('bafybeig6xv5nwphfmvcnektpnojts33jqcuam7bmye2pb54adnrtccjlsu'), 'item1', 'item2' ], + nested: { + aLink: CID.parse('bafybeig6xv5nwphfmvcnektpnojts33jqcuam7bmye2pb54adnrtccjlsu'), + arr: [ CID.parse('bafybeig6xv5nwphfmvcnektpnojts33jqcuam7bmye2pb54adnrtccjlsu'), 'item1', 'item2' ], + } + } + }, }, encodedJws: { oneSig: [{ @@ -59,6 +75,15 @@ export const fixtures = { protected: bytes.fromHex('7b22616c67223a2245533235364b227d'), }] }, + withPayload: { + payload: bytes.fromHex('7b2274657374223a227061796c6f6164222c22614c696e6b223a22697066733a2f2f6261667962656967367876356e777068666d76636e656b74706e6f6a747333336a716375616d37626d7965327062353461646e727463636a6c7375222c22617272223a5b22697066733a2f2f6261667962656967367876356e777068666d76636e656b74706e6f6a747333336a716375616d37626d7965327062353461646e727463636a6c7375222c226974656d31222c226974656d32225d2c226e6573746564223a7b22614c696e6b223a22697066733a2f2f6261667962656967367876356e777068666d76636e656b74706e6f6a747333336a716375616d37626d7965327062353461646e727463636a6c7375222c22617272223a5b22697066733a2f2f6261667962656967367876356e777068666d76636e656b74706e6f6a747333336a716375616d37626d7965327062353461646e727463636a6c7375222c226974656d31222c226974656d32225d7d7d'), + signatures: [ + { + protected: bytes.fromHex('7b22616c67223a2245533235364b227d'), + signature: bytes.fromHex('218f001e1401d27e50a4ed68a6c7bcddde87ad759fa1f3e35ba89fe6f541ec3932df08f6f9d693f08d711d7e6ce0f6ee0b7a30668dabafc0be9c642c7ed1c4fb') + } + ] + } }, blockEncoded: { oneSig: [ diff --git a/test/signing.test.ts b/test/signing.test.ts index 575d60c..bfb24ff 100644 --- a/test/signing.test.ts +++ b/test/signing.test.ts @@ -51,7 +51,7 @@ describe('Signing support', () => { }) describe('decode', () => { - it('Decodes general encoding, one signature', () => { + it('CID, Decodes general encoding, one signature', () => { let decoded decoded = signing.decode(fixtures.encodedJws.oneSig[0]) expect(decoded).toEqual(fixtures.dagJws.oneSig[0]) @@ -60,14 +60,19 @@ describe('Signing support', () => { expect(decoded).toEqual(fixtures.dagJws.oneSig[1]) }) - it('Decodes general encoding, multiple signatures', () => { + it('CID, Decodes general encoding, multiple signatures', () => { const decoded = signing.decode(fixtures.encodedJws.multipleSig) expect(decoded).toEqual(fixtures.dagJws.multipleSig) }) + + it('JSON, Decodes jws with payload', () => { + const decoded = signing.decode(fixtures.encodedJws.withPayload) + expect(decoded).toEqual(fixtures.dagJws.withPayload) + }) }) describe('encode', () => { - it('Encodes dag encoding, one signature', () => { + it('CID, Encodes dag encoding, one signature', () => { let encoded encoded = signing.encode(fixtures.dagJws.oneSig[0]) expect(encoded).toEqual(fixtures.encodedJws.oneSig[0]) @@ -76,15 +81,14 @@ describe('Signing support', () => { expect(encoded).toEqual(fixtures.encodedJws.oneSig[1]) }) - it('Encodes dag encoding, multiple signatures', () => { + it('CID, Encodes dag encoding, multiple signatures', () => { const encoded = signing.encode(fixtures.dagJws.multipleSig) expect(encoded).toEqual(fixtures.encodedJws.multipleSig) }) - it('Throws if payload is not a CID', async () => { - const payload = toBase64url(bytes.fromString(JSON.stringify({ json: 'payload' }))) - const notDagJws = Object.assign({}, fixtures.dagJws.oneSig[0], { payload }) - expect(() => signing.encode(notDagJws)).toThrow('Not a valid DagJWS') + it('JSON, Encodes jws with payload', () => { + const encoded = signing.encode(fixtures.dagJws.withPayload) + expect(encoded).toEqual(fixtures.encodedJws.withPayload) }) })