From 0caa34afcbafb696b24ca2a358570c34beff3e0b Mon Sep 17 00:00:00 2001 From: haad Date: Mon, 20 Feb 2023 10:51:12 +0200 Subject: [PATCH] Change Identity's signatures structure --- src/identities/identities.js | 6 +- src/identities/identity.js | 107 +++---- src/identities/index.js | 6 +- ....spec.js => did-identity-provider.test.js} | 9 +- ....js => ethereum-identity-provider.test.js} | 9 +- ...ty-provider.spec.js => identities.test.js} | 39 ++- test/identities/identity.spec.js | 109 ------- test/identities/identity.test.js | 269 ++++++++++++++++++ 8 files changed, 374 insertions(+), 180 deletions(-) rename test/identities/{did-identity-provider.spec.js => did-identity-provider.test.js} (96%) rename test/identities/{ethereum-identity-provider.spec.js => ethereum-identity-provider.test.js} (96%) rename test/identities/{identity-provider.spec.js => identities.test.js} (91%) delete mode 100644 test/identities/identity.spec.js create mode 100644 test/identities/identity.test.js diff --git a/src/identities/identities.js b/src/identities/identities.js index 7ff146b..0bc0f7b 100644 --- a/src/identities/identities.js +++ b/src/identities/identities.js @@ -41,7 +41,11 @@ const Identities = async ({ keystore, signingKeyStore, identityKeysPath, signing const publicKey = keystore.getPublic(privateKey) const idSignature = await KeyStore.sign(privateKey, id) const publicKeyAndIdSignature = await identityProvider.signIdentity(publicKey + idSignature, options) - const identity = await Identity({ id, publicKey, idSignature, publicKeyAndIdSignature, type, sign, verify }) + const signatures = { + id: idSignature, + publicKey: publicKeyAndIdSignature + } + const identity = await Identity({ id, publicKey, signatures, type, sign, verify }) await storage.put(identity.hash, identity.bytes) diff --git a/src/identities/identity.js b/src/identities/identity.js index c11e22f..098ed72 100644 --- a/src/identities/identity.js +++ b/src/identities/identity.js @@ -8,53 +8,7 @@ const codec = dagCbor const hasher = sha256 const hashStringEncoding = base58btc -const isEqual = (a, b) => { - return a.id === b.id && - a.publicKey === b.publicKey && - a.signatures.id === b.signatures.id && - a.signatures.publicKey === b.signatures.publicKey -} - -const isIdentity = (identity) => { - return isDefined(identity.id) && - isDefined(identity.publicKey) && - isDefined(identity.signatures) && - isDefined(identity.signatures.id) && - isDefined(identity.signatures.publicKey) && - isDefined(identity.type) -} - -/** - * Encode an Identity to a serializable form - * @param {Identity} identity Identity to encode - * @returns {Object} Object with fields hash and bytes - */ -const encodeIdentity = async (identity) => { - const { id, publicKey, signatures, type } = identity - const value = { id, publicKey, signatures, type } - const { cid, bytes } = await Block.encode({ value, codec, hasher }) - const hash = cid.toString(hashStringEncoding) - return { hash, bytes } -} - -/** - * Decode an Identity from bytes - * @param {Uint8Array} bytes Bytes from which to decode an Identity from - * @returns {Identity} - */ -const decodeIdentity = async (bytes) => { - const { value } = await Block.decode({ bytes, codec, hasher }) - const { id, publicKey, signatures, type } = value - return Identity({ - id, - publicKey, - idSignature: signatures.id, - publicKeyAndIdSignature: signatures.publicKey, - type - }) -} - -const Identity = async ({ id, publicKey, idSignature, publicKeyAndIdSignature, type, sign, verify } = {}) => { +const Identity = async ({ id, publicKey, signatures, type, sign, verify } = {}) => { if (!isDefined(id)) { throw new Error('Identity id is required') } @@ -63,11 +17,15 @@ const Identity = async ({ id, publicKey, idSignature, publicKeyAndIdSignature, t throw new Error('Invalid public key') } - if (!isDefined(idSignature)) { - throw new Error('Signature of the id (idSignature) is required') + if (!isDefined(signatures)) { + throw new Error('Signatures is required') } - if (!isDefined(publicKeyAndIdSignature)) { + if (!isDefined(signatures.id)) { + throw new Error('Signature of the id is required') + } + + if (!isDefined(signatures.publicKey)) { throw new Error('Signature of (publicKey + idSignature) is required') } @@ -75,7 +33,7 @@ const Identity = async ({ id, publicKey, idSignature, publicKeyAndIdSignature, t throw new Error('Identity type is required') } - const signatures = Object.assign({}, { id: idSignature }, { publicKey: publicKeyAndIdSignature }) + signatures = Object.assign({}, signatures) const identity = { id, @@ -86,11 +44,54 @@ const Identity = async ({ id, publicKey, idSignature, publicKeyAndIdSignature, t verify } - const { hash, bytes } = await encodeIdentity(identity) + const { hash, bytes } = await _encodeIdentity(identity) identity.hash = hash identity.bytes = bytes return identity } -export { Identity as default, isEqual, isIdentity, encodeIdentity, decodeIdentity } +/** + * Encode an Identity to a serializable form + * @param {Identity} identity Identity to encode + * @returns {Object} Object with fields hash and bytes + */ +const _encodeIdentity = async (identity) => { + const { id, publicKey, signatures, type } = identity + const value = { id, publicKey, signatures, type } + const { cid, bytes } = await Block.encode({ value, codec, hasher }) + const hash = cid.toString(hashStringEncoding) + return { hash, bytes: Uint8Array.from(bytes) } +} + +/** + * Decode an Identity from bytes + * @param {Uint8Array} bytes Bytes from which to decode an Identity from + * @returns {Identity} + */ +const decodeIdentity = async (bytes) => { + const { value } = await Block.decode({ bytes, codec, hasher }) + return Identity({ ...value }) +} + +const isIdentity = (identity) => { + return isDefined(identity.id) && + isDefined(identity.hash) && + isDefined(identity.bytes) && + isDefined(identity.publicKey) && + isDefined(identity.signatures) && + isDefined(identity.signatures.id) && + isDefined(identity.signatures.publicKey) && + isDefined(identity.type) +} + +const isEqual = (a, b) => { + return a.id === b.id && + a.hash === b.hash && + a.type === b.type && + a.publicKey === b.publicKey && + a.signatures.id === b.signatures.id && + a.signatures.publicKey === b.signatures.publicKey +} + +export { Identity as default, isEqual, isIdentity, decodeIdentity } diff --git a/src/identities/index.js b/src/identities/index.js index c24f0bb..fb40316 100644 --- a/src/identities/index.js +++ b/src/identities/index.js @@ -5,4 +5,8 @@ export { isProviderSupported } from './identities.js' -export { default as Identity } from './identity.js' +export { + default as Identity, + isIdentity, + isEqual +} from './identity.js' diff --git a/test/identities/did-identity-provider.spec.js b/test/identities/did-identity-provider.test.js similarity index 96% rename from test/identities/did-identity-provider.spec.js rename to test/identities/did-identity-provider.test.js index ca42641..c7d842d 100644 --- a/test/identities/did-identity-provider.spec.js +++ b/test/identities/did-identity-provider.test.js @@ -87,8 +87,7 @@ describe('DID Identity Provider', function () { const identity2 = await Identity({ id: 'NotAnId', publicKey, - idSignature: signatures.id, - publicKeyAndIdSignature: signatures.publicKey, + signatures, type }) const verified = await identities.verifyIdentity(identity2) @@ -118,8 +117,10 @@ describe('DID Identity Provider', function () { const modifiedIdentity = await Identity({ id: 'this id does not exist', publicKey, - idSignature: '', - publicKeyAndIdSignature: signatures.publicKey, + signatures: { + id: '', + publicKey: signatures.publicKey + }, type }) let signature diff --git a/test/identities/ethereum-identity-provider.spec.js b/test/identities/ethereum-identity-provider.test.js similarity index 96% rename from test/identities/ethereum-identity-provider.spec.js rename to test/identities/ethereum-identity-provider.test.js index ac65a64..7af290a 100644 --- a/test/identities/ethereum-identity-provider.spec.js +++ b/test/identities/ethereum-identity-provider.test.js @@ -84,8 +84,7 @@ describe('Ethereum Identity Provider', function () { const identity2 = await Identity({ id: 'NotAnId', publicKey, - idSignature: signatures.id, - publicKeyAndIdSignature: signatures.publicKey, + signatures, type }) const verified = await identities.verifyIdentity(identity2) @@ -114,8 +113,10 @@ describe('Ethereum Identity Provider', function () { const modifiedIdentity = await Identity({ id: 'this id does not exist', publicKey, - idSignature: '', - publicKeyAndIdSignature: signatures.publicKey, + signatures: { + id: '', + publicKey: signatures.publicKey, + }, type }) let signature diff --git a/test/identities/identity-provider.spec.js b/test/identities/identities.test.js similarity index 91% rename from test/identities/identity-provider.spec.js rename to test/identities/identities.test.js index 4aca241..e967ceb 100644 --- a/test/identities/identity-provider.spec.js +++ b/test/identities/identities.test.js @@ -10,7 +10,7 @@ const signingKeysPath = path.resolve('./test/identities/signingKeys') const identityKeysPath = path.resolve('./test/identities/identityKeys') const type = 'orbitdb' -describe('Identity Provider', function () { +describe('Identities', function () { before(async () => { rmrf.sync(signingKeysPath) rmrf.sync(identityKeysPath) @@ -53,6 +53,35 @@ describe('Identity Provider', function () { }) }) + describe('Get Identity', () => { + const id = 'A' + + let identities + let identity + + afterEach(async () => { + if (identities) { + await identities.keystore.close() + } + if (identities) { + await identities.signingKeyStore.close() + } + }) + + it('gets the identity from storage', async () => { + identities = await Identities({ identityKeysPath, signingKeysPath }) + identity = await identities.createIdentity({ id }) + const result = await identities.getIdentity(identity.hash) + assert.strictEqual(result.id, identity.id) + assert.strictEqual(result.hash, identity.hash) + assert.strictEqual(result.publicKey, identity.publicKey) + assert.strictEqual(result.type, identity.type) + assert.deepStrictEqual(result.signatures, identity.signatures) + assert.strictEqual(result.sign, undefined) + assert.strictEqual(result.verify, undefined) + }) + }) + describe('Passing in custom keystore', async () => { const id = 'B' @@ -310,13 +339,7 @@ describe('Identity Provider', function () { it('throws an error if private key is not found from keystore', async () => { // Remove the key from the keystore (we're using a mock storage in these tests) const { publicKey, signatures, type } = identity - const modifiedIdentity = await Identity({ - id: 'this id does not exist', - publicKey, - idSignature: '', - publicKeyAndIdSignature: signatures, - type - }) + const modifiedIdentity = await Identity({ id: 'this id does not exist', publicKey, signatures, type }) let signature let err try { diff --git a/test/identities/identity.spec.js b/test/identities/identity.spec.js deleted file mode 100644 index e8224a2..0000000 --- a/test/identities/identity.spec.js +++ /dev/null @@ -1,109 +0,0 @@ -import assert from 'assert' -import { Identity } from '../../src/identities/index.js' - -describe('Identity', function () { - const id = '0x01234567890abcdefghijklmnopqrstuvwxyz' - const publicKey = '' - const idSignature = 'signature for ' - const publicKeyAndIdSignature = 'signature for ' - const type = 'orbitdb' - const provider = 'IdentityProviderInstance' - - let identity - - before(async () => { - identity = await Identity({ id, publicKey, idSignature, publicKeyAndIdSignature, type }) - }) - - it('has the correct id', async () => { - assert.strictEqual(identity.id, id) - }) - - it('has the correct publicKey', async () => { - assert.strictEqual(identity.publicKey, publicKey) - }) - - it('has the correct idSignature', async () => { - assert.strictEqual(identity.signatures.id, idSignature) - }) - - it('has the correct publicKeyAndIdSignature', async () => { - assert.strictEqual(identity.signatures.publicKey, publicKeyAndIdSignature) - }) - - // it('has the correct provider', async () => { - // assert.deepStrictEqual(identity.provider, provider) - // }) - - // it('converts identity to a JSON object', async () => { - // const expected = { - // id, - // publicKey, - // signatures: { id: idSignature, publicKey: publicKeyAndIdSignature }, - // type - // } - // assert.deepStrictEqual(identity.toJSON(), expected) - // }) - - describe('Constructor inputs', () => { - it('throws and error if id was not given in constructor', async () => { - let err - try { - identity = await Identity() - } catch (e) { - err = e.toString() - } - assert.strictEqual(err, 'Error: Identity id is required') - }) - - it('throws and error if publicKey was not given in constructor', async () => { - let err - try { - identity = await Identity({ id: 'abc'}) - } catch (e) { - err = e.toString() - } - assert.strictEqual(err, 'Error: Invalid public key') - }) - - it('throws and error if identity signature was not given in constructor', async () => { - let err - try { - identity = await Identity({ id: 'abc', publicKey }) - } catch (e) { - err = e.toString() - } - assert.strictEqual(err, 'Error: Signature of the id (idSignature) is required') - }) - - it('throws and error if identity signature was not given in constructor', async () => { - let err - try { - identity = await Identity({ id: 'abc', publicKey, idSignature }) - } catch (e) { - err = e.toString() - } - assert.strictEqual(err, 'Error: Signature of (publicKey + idSignature) is required') - }) - - // it('throws and error if identity provider was not given in constructor', async () => { - // let err - // try { - // identity = new Identity('abc', publicKey, idSignature, publicKeyAndIdSignature, type) - // } catch (e) { - // err = e.toString() - // } - // assert.strictEqual(err, 'Error: Identity provider is required') - // }) - - it('throws and error if identity type was not given in constructor', async () => { - let err - try { - identity = await Identity({ id: 'abc', publicKey, idSignature, publicKeyAndIdSignature }) - } catch (e) { - err = e.toString() - } - assert.strictEqual(err, 'Error: Identity type is required') - }) - }) -}) diff --git a/test/identities/identity.test.js b/test/identities/identity.test.js new file mode 100644 index 0000000..6e93994 --- /dev/null +++ b/test/identities/identity.test.js @@ -0,0 +1,269 @@ +import assert from 'assert' +import { Identity, isIdentity, isEqual } from '../../src/identities/index.js' +import { decodeIdentity } from '../../src/identities/identity.js' + +describe('Identity', function () { + const id = '0x01234567890abcdefghijklmnopqrstuvwxyz' + const publicKey = '' + const signatures = { + id: 'signature for ', + publicKey: 'signature for ' + } + const type = 'orbitdb' + // const provider = 'IdentityProviderInstance' + + const expectedHash = 'zdpuArx43BnXdDff5rjrGLYrxUomxNroc2uaocTgcWK76UfQT' + const expectedBytes = Uint8Array.from([ + 164,98,105,100,120,39,48,120,48,49,50,51,52,53,54,55,56,57,48,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,100,116,121,112,101,103,111,114,98,105,116,100,98,105,112,117,98,108,105,99,75,101,121,104,60,112,117,98,107,101,121,62,106,115,105,103,110,97,116,117,114,101,115,162,98,105,100,114,115,105,103,110,97,116,117,114,101,32,102,111,114,32,60,105,100,62,105,112,117,98,108,105,99,75,101,121,120,39,115,105,103,110,97,116,117,114,101,32,102,111,114,32,60,112,117,98,108,105,99,75,101,121,32,43,32,105,100,83,105,103,110,97,116,117,114,101,62 + ]) + + let identity + + before(async () => { + identity = await Identity({ id, publicKey, signatures, type }) + }) + + it('has the correct id', async () => { + assert.strictEqual(identity.id, id) + }) + + it('has the correct publicKey', async () => { + assert.strictEqual(identity.publicKey, publicKey) + }) + + it('has the correct idSignature', async () => { + assert.strictEqual(identity.signatures.id, signatures.id) + }) + + it('has the correct publicKeyAndIdSignature', async () => { + assert.strictEqual(identity.signatures.publicKey, signatures.publicKey) + }) + + // it('has the correct provider', async () => { + // assert.deepStrictEqual(identity.provider, provider) + // }) + + describe('Constructor inputs', () => { + it('throws and error if id was not given in constructor', async () => { + let err + try { + identity = await Identity() + } catch (e) { + err = e.toString() + } + assert.strictEqual(err, 'Error: Identity id is required') + }) + + it('throws and error if publicKey was not given in constructor', async () => { + let err + try { + identity = await Identity({ id: 'abc'}) + } catch (e) { + err = e.toString() + } + assert.strictEqual(err, 'Error: Invalid public key') + }) + + it('throws and error if identity signature was not given in constructor', async () => { + let err + try { + identity = await Identity({ id: 'abc', publicKey }) + } catch (e) { + err = e.toString() + } + assert.strictEqual(err, 'Error: Signatures is required') + }) + + it('throws and error if signature for id was not given in constructor', async () => { + let err + try { + identity = await Identity({ id: 'abc', publicKey, signatures: {} }) + } catch (e) { + err = e.toString() + } + assert.strictEqual(err, 'Error: Signature of the id is required') + }) + + it('throws and error if signature for publicKey was not given in constructor', async () => { + let err + try { + identity = await Identity({ id: 'abc', publicKey, signatures: { id: signatures.id } }) + } catch (e) { + err = e.toString() + } + assert.strictEqual(err, 'Error: Signature of (publicKey + idSignature) is required') + }) + + it('throws and error if signature for publicKey was not given in constructor', async () => { + let err + try { + identity = await Identity({ id: 'abc', publicKey, signatures: { publicKey: signatures.publicKey } }) + } catch (e) { + err = e.toString() + } + assert.strictEqual(err, 'Error: Signature of the id is required') + }) + + // it('throws and error if identity provider was not given in constructor', async () => { + // let err + // try { + // identity = new Identity('abc', publicKey, idSignature, publicKeyAndIdSignature, type) + // } catch (e) { + // err = e.toString() + // } + // assert.strictEqual(err, 'Error: Identity provider is required') + // }) + + it('throws and error if identity type was not given in constructor', async () => { + let err + try { + identity = await Identity({ id: 'abc', publicKey, signatures }) + } catch (e) { + err = e.toString() + } + assert.strictEqual(err, 'Error: Identity type is required') + }) + }) + + describe('isIdentity', () => { + describe('valid Identity', () => { + it('is a valid identity', async () => { + const identity = await Identity({ id, publicKey, signatures, type }) + const result = isIdentity(identity) + assert.strictEqual(result, true) + }) + }) + + describe('invalid Identity', () => { + beforeEach(async () => { + identity = await Identity({ id, publicKey, signatures, type }) + }) + + it('is not a valid identity if id is missing', async () => { + delete identity.id + const result = isIdentity(identity) + assert.strictEqual(result, false) + }) + + it('is not a valid identity if hash is missing', async () => { + delete identity.hash + const result = isIdentity(identity) + assert.strictEqual(result, false) + }) + + it('is not a valid identity if bytes are missing', async () => { + delete identity.bytes + const result = isIdentity(identity) + assert.strictEqual(result, false) + }) + + it('is not a valid identity if publicKey is missing', async () => { + delete identity.publicKey + const result = isIdentity(identity) + assert.strictEqual(result, false) + }) + + it('is not a valid identity if signatures is missing', async () => { + delete identity.signatures + const result = isIdentity(identity) + assert.strictEqual(result, false) + }) + + it('is not a valid identity if signature for id is missing', async () => { + delete identity.signatures.id + const result = isIdentity(identity) + assert.strictEqual(result, false) + }) + + it('is not a valid identity if signature for publicKey is missing', async () => { + delete identity.signatures.publicKey + const result = isIdentity(identity) + assert.strictEqual(result, false) + }) + + it('is not a valid identity if type is missing', async () => { + delete identity.type + const result = isIdentity(identity) + assert.strictEqual(result, false) + }) + }) + }) + + describe('isEqual', () => { + describe('equal identities', () => { + it('identities are equal', async () => { + const identity1 = await Identity({ id, publicKey, signatures, type }) + const identity2 = await Identity({ id, publicKey, signatures, type }) + const result = isEqual(identity1, identity2) + assert.strictEqual(result, true) + }) + }) + + describe('not equal identities', () => { + let identity1, identity2 + + before(async () => { + identity1 = await Identity({ id, publicKey, signatures, type }) + identity2 = await Identity({ id, publicKey, signatures, type }) + }) + + it('identities are not equal if id is different', async () => { + identity2 = await Identity({ id: 'X', publicKey, signatures, type }) + const result = isEqual(identity1, identity2) + assert.strictEqual(result, false) + }) + + it('identities are not equal if hash is different', async () => { + identity2 = await Identity({ id, publicKey, signatures, type }) + identity2.hash = 'notthesame' + const result = isEqual(identity1, identity2) + assert.strictEqual(result, false) + }) + + it('identities are not equal if type is different', async () => { + identity2 = await Identity({ id, publicKey, signatures, type }) + identity2.type = 'some other identity provider than orbitdb' + const result = isEqual(identity1, identity2) + assert.strictEqual(result, false) + }) + + it('identities are not equal if publicKey is different', async () => { + identity2 = await Identity({ id, publicKey: 'XYZ', signatures, type }) + const result = isEqual(identity1, identity2) + assert.strictEqual(result, false) + }) + + it('identities are not equal if id signature is different', async () => { + identity2 = await Identity({ id, publicKey, signatures: { id: 'different id signature', publicKey: signatures.publicKey }, type }) + const result = isEqual(identity1, identity2) + assert.strictEqual(result, false) + }) + + it('identities are not equal if publicKey signature is different', async () => { + identity2 = await Identity({ id, publicKey, signatures: { id: signatures.id, publicKey: 'different publicKey signature' }, type }) + const result = isEqual(identity1, identity2) + assert.strictEqual(result, false) + }) + }) + }) + + describe('Decode Identity', () => { + before(async () => { + identity = await Identity({ id, publicKey, signatures, type }) + }) + + it('decodes an identity from bytes', async () => { + const result = await decodeIdentity(expectedBytes) + + assert.strictEqual(isIdentity(result), true) + assert.strictEqual(result.id, id) + assert.strictEqual(result.publicKey, publicKey) + assert.strictEqual(result.type, type) + assert.strictEqual(result.hash, expectedHash) + assert.strictEqual(result.sign, undefined) + assert.strictEqual(result.verify, undefined) + assert.deepStrictEqual(result.bytes, expectedBytes) + assert.deepStrictEqual(result.signatures, signatures) + }) + }) +})