-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ae5e5aa
commit b436720
Showing
5 changed files
with
264 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
/* eslint-disable max-len */ | ||
|
||
const { HDNode } = require('bitcoinjs-lib'); | ||
const CredentialSignerVerifier = require('../src/creds/CredentialSignerVerifier'); | ||
|
||
const SEED = 'f6d466fd58c20ff964673522083efebf'; | ||
const prvBase58 = 'xprv9s21ZrQH143K4aBUwUW6GVec7Y6oUEBqrt2WWaXyxjh2pjofNc1of44BLufn4p1t7Jq4EPzm5C9sRxCuBYJdHu62jhgfyPm544sNjtH7x8S'; | ||
|
||
const pubBase58 = 'xpub661MyMwAqRbcH4Fx3W36ddbLfZwHsguhE6x7JxwbX5E1hY8ov9L4CrNfCCQpV8pVK64CVqkhYQ9QLFgkVAUqkRThkTY1R4GiWHNZtAFSVpD'; | ||
|
||
describe('CredentialSignerVerifier Tests', () => { | ||
describe('Using a ECKeyPair', () => { | ||
let keyPair; | ||
let signerVerifier; | ||
|
||
beforeAll(() => { | ||
keyPair = HDNode.fromSeedHex(SEED); | ||
signerVerifier = new CredentialSignerVerifier({ keyPair }); | ||
}); | ||
|
||
it('Should sign and verify', () => { | ||
const toSign = { merkleRoot: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' }; | ||
const signature = signerVerifier.sign(toSign); | ||
expect(signature).toBeDefined(); | ||
const toVerify = { | ||
proof: { | ||
...toSign, | ||
merkleRootSignature: signature, | ||
}, | ||
}; | ||
expect(signerVerifier.isSignatureValid(toVerify)).toBeTruthy(); | ||
}); | ||
|
||
it('Should verify', () => { | ||
const toVerify = { | ||
proof: { | ||
merkleRoot: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b666', | ||
merkleRootSignature: { | ||
algo: 'ec256k1', | ||
pubBase58: 'xpub661MyMwAqRbcH4Fx3W36ddbLfZwHsguhE6x7JxwbX5E1hY8ov9L4CrNfCCQpV8pVK64CVqkhYQ9QLFgkVAUqkRThkTY1R4GiWHNZtAFSVpD', | ||
signature: '3045022100e7f0921491e8da2759b24047443325483ac023795683dc3b91c78d0566a1159602206fd4e80982fd83705932543d02bc6abd079446bf4ec7b5d9fba4f7f5363bd6fa', | ||
}, | ||
}, | ||
}; | ||
expect(signerVerifier.isSignatureValid(toVerify)).toBeTruthy(); | ||
}); | ||
|
||
it('Should not verify', () => { | ||
const toVerify = { | ||
proof: { | ||
merkleRoot: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b666', | ||
merkleRootSignature: { | ||
algo: 'ec256k1', | ||
pubBase58: 'xpub661MyMwAqRbcH4Fx3W36ddbLfZwHsguhE6x7JxwbX5E1hY8ov9L4CrNfCCQpV8pVK64CVqkhYQ9QLFgkVAUqkRThkTY1R4GiWHNZtAFSVpD', | ||
signature: 'fa3e022100e7f0921491e8da2759b24047443325483ac023795683dc3b91c78d0566a1159602206fd4e80982fd83705932543d02bc6abd079446bf4ec7b5d9fba4f7f5363bd6fa', | ||
}, | ||
}, | ||
}; | ||
expect(signerVerifier.isSignatureValid(toVerify)).toBeFalsy(); | ||
}); | ||
}); | ||
describe('Using a prvBase58', () => { | ||
let signerVerifier; | ||
|
||
beforeAll(() => { | ||
signerVerifier = new CredentialSignerVerifier({ prvBase58 }); | ||
}); | ||
|
||
it('Should sign and verify', () => { | ||
const toSign = { merkleRoot: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' }; | ||
const signature = signerVerifier.sign(toSign); | ||
expect(signature).toBeDefined(); | ||
const toVerify = { | ||
proof: { | ||
...toSign, | ||
merkleRootSignature: signature, | ||
}, | ||
}; | ||
expect(signerVerifier.isSignatureValid(toVerify)).toBeTruthy(); | ||
}); | ||
|
||
it('Should verify', () => { | ||
const toVerify = { | ||
proof: { | ||
merkleRoot: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b666', | ||
merkleRootSignature: { | ||
algo: 'ec256k1', | ||
pubBase58: 'xpub661MyMwAqRbcH4Fx3W36ddbLfZwHsguhE6x7JxwbX5E1hY8ov9L4CrNfCCQpV8pVK64CVqkhYQ9QLFgkVAUqkRThkTY1R4GiWHNZtAFSVpD', | ||
signature: '3045022100e7f0921491e8da2759b24047443325483ac023795683dc3b91c78d0566a1159602206fd4e80982fd83705932543d02bc6abd079446bf4ec7b5d9fba4f7f5363bd6fa', | ||
}, | ||
}, | ||
}; | ||
expect(signerVerifier.isSignatureValid(toVerify)).toBeTruthy(); | ||
}); | ||
|
||
it('Should not verify', () => { | ||
const toVerify = { | ||
proof: { | ||
merkleRoot: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b666', | ||
merkleRootSignature: { | ||
algo: 'ec256k1', | ||
pubBase58: 'xpub661MyMwAqRbcH4Fx3W36ddbLfZwHsguhE6x7JxwbX5E1hY8ov9L4CrNfCCQpV8pVK64CVqkhYQ9QLFgkVAUqkRThkTY1R4GiWHNZtAFSVpD', | ||
signature: 'fa3e022100e7f0921491e8da2759b24047443325483ac023795683dc3b91c78d0566a1159602206fd4e80982fd83705932543d02bc6abd079446bf4ec7b5d9fba4f7f5363bd6fa', | ||
}, | ||
}, | ||
}; | ||
expect(signerVerifier.isSignatureValid(toVerify)).toBeFalsy(); | ||
}); | ||
}); | ||
describe('Using a pubBase58', () => { | ||
let signerVerifier; | ||
|
||
beforeAll(() => { | ||
signerVerifier = new CredentialSignerVerifier({ pubBase58 }); | ||
}); | ||
|
||
it('Should verify', () => { | ||
const toVerify = { | ||
proof: { | ||
merkleRoot: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b666', | ||
merkleRootSignature: { | ||
algo: 'ec256k1', | ||
pubBase58: 'xpub661MyMwAqRbcH4Fx3W36ddbLfZwHsguhE6x7JxwbX5E1hY8ov9L4CrNfCCQpV8pVK64CVqkhYQ9QLFgkVAUqkRThkTY1R4GiWHNZtAFSVpD', | ||
signature: '3045022100e7f0921491e8da2759b24047443325483ac023795683dc3b91c78d0566a1159602206fd4e80982fd83705932543d02bc6abd079446bf4ec7b5d9fba4f7f5363bd6fa', | ||
}, | ||
}, | ||
}; | ||
expect(signerVerifier.isSignatureValid(toVerify)).toBeTruthy(); | ||
}); | ||
|
||
it('Should not verify', () => { | ||
const toVerify = { | ||
proof: { | ||
merkleRoot: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b666', | ||
merkleRootSignature: { | ||
algo: 'ec256k1', | ||
pubBase58: 'xpub661MyMwAqRbcH4Fx3W36ddbLfZwHsguhE6x7JxwbX5E1hY8ov9L4CrNfCCQpV8pVK64CVqkhYQ9QLFgkVAUqkRThkTY1R4GiWHNZtAFSVpD', | ||
signature: 'fa3e022100e7f0921491e8da2759b24047443325483ac023795683dc3b91c78d0566a1159602206fd4e80982fd83705932543d02bc6abd079446bf4ec7b5d9fba4f7f5363bd6fa', | ||
}, | ||
}, | ||
}; | ||
expect(signerVerifier.isSignatureValid(toVerify)).toBeFalsy(); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
const _ = require('lodash'); | ||
const { HDNode, ECSignature } = require('bitcoinjs-lib'); | ||
|
||
class CredentialSignerVerifier { | ||
/** | ||
* Creates a new instance of a CredentialSignerVerifier | ||
* | ||
* @param options.keyPair any instace that implements sign and verify interface | ||
* or | ||
* @param options.prvBase58 bse58 serialized private key | ||
* or for verification only | ||
* @param options.pubBase58 bse58 serialized public key | ||
*/ | ||
constructor(options) { | ||
if (_.isEmpty(options.keyPair) && _.isEmpty(options.prvBase58) && _.isEmpty(options.pubBase58)) { | ||
throw new Error('Either a keyPair, prvBase58 or pubBase58(to verify only) is required'); | ||
} | ||
this.keyPair = options.keyPair || HDNode.fromBase58(options.prvBase58 || options.pubBase58); | ||
} | ||
|
||
/** | ||
* Verify is a credential has a valid merkletree signature, using a pinned pubkey | ||
* @param credential | ||
* @returns {*|boolean} | ||
*/ | ||
isSignatureValid(credential) { | ||
if (_.isEmpty(credential.proof) | ||
|| _.isEmpty(credential.proof.merkleRoot) | ||
|| _.isEmpty(credential.proof.merkleRootSignature)) { | ||
throw Error('Invalid Credential Proof Schema'); | ||
} | ||
|
||
try { | ||
const signatureHex = _.get(credential, 'proof.merkleRootSignature.signature'); | ||
const signature = signatureHex ? ECSignature.fromDER(Buffer.from(signatureHex, 'hex')) : null; | ||
const merkleRoot = _.get(credential, 'proof.merkleRoot'); | ||
return (signature && merkleRoot) ? this.keyPair.verify(Buffer.from(merkleRoot, 'hex'), signature) : false; | ||
} catch (error) { | ||
// verify throws in must cases but we want to return false | ||
return false; | ||
} | ||
} | ||
|
||
/** | ||
* Create a merkleRootSignature object by signing with a pinned private key | ||
* @param proof | ||
* @returns {{signature, pubBase58: *, algo: string}} | ||
*/ | ||
sign(proof) { | ||
const hash = Buffer.from(proof.merkleRoot, 'hex'); | ||
const signature = this.keyPair.sign(hash); | ||
return { | ||
algo: 'ec256k1', | ||
pubBase58: this.keyPair.neutered().toBase58(), | ||
signature: signature.toDER().toString('hex'), | ||
}; | ||
} | ||
} | ||
|
||
module.exports = CredentialSignerVerifier; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters