From 64fc8a27133c743c4321921d08fe29e4e214d1c5 Mon Sep 17 00:00:00 2001 From: Faiz Faidurrahman Date: Fri, 22 Jun 2018 15:33:14 +0800 Subject: [PATCH 1/3] Added verify signature function in PublicAccount class --- src/model/account/PublicAccount.ts | 48 ++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/src/model/account/PublicAccount.ts b/src/model/account/PublicAccount.ts index c23eeb028f..a57cf40faa 100644 --- a/src/model/account/PublicAccount.ts +++ b/src/model/account/PublicAccount.ts @@ -14,8 +14,9 @@ * limitations under the License. */ -import {NetworkType} from '../blockchain/NetworkType'; -import {Address} from './Address'; +import { KeyPair, convert } from 'nem2-library'; +import { NetworkType } from '../blockchain/NetworkType'; +import { Address } from './Address'; /** * The public account structure contains account's address and public key. @@ -53,6 +54,48 @@ export class PublicAccount { return new PublicAccount(publicKey, address); } + /** + * Verify a signature. + * + * @param {string} publicKey - The public key to use for verification. + * @param {string} data - The data to verify. + * @param {string} signature - The signature to verify. + * + * @return {boolean} - True if the signature is valid, false otherwise. + */ + static verifySignature(publicKey: string, data: string, signature: string): boolean { + if (!publicKey || !data || !signature) { + throw new Error('Missing argument !'); + } + + if (publicKey.length !== 64 && publicKey.length !== 66) { + throw new Error('Not a valid public key'); + } + + if (convert.isHexString(signature)) { + throw new Error('Signature must be hexadecimal only !'); + } + + if (signature.length !== 128) { + throw new Error('Signature length is incorrect !'); + } + + // Convert signature key to Uint8Array + const _signature = convert.hexToUint8(signature); + + let _data; + + // Convert data to hex if data is not hex + if (!convert.isHexString(data)) { + _data = convert.utf8ToHex(data); + } + + // Convert to Uint8Array + _data = convert.hexToUint8(_data); + + return KeyPair.verify(publicKey, _data, _signature); + } + /** * Compares public accounts for equality. * @param publicAccount @@ -61,4 +104,5 @@ export class PublicAccount { equals(publicAccount: PublicAccount) { return this.publicKey === publicAccount.publicKey && this.address.plain() === publicAccount.address.plain(); } + } From 2f308902ad6a452a2bdbe3425d328ce4dc46d1c6 Mon Sep 17 00:00:00 2001 From: Faiz Faidurrahman Date: Mon, 25 Jun 2018 17:14:21 +0800 Subject: [PATCH 2/3] FIX #2 - verify signature function 1. change publicKey parameter to publicAccount 2. omit "_" from variable names 3. remove "!" from error messages --- src/model/account/PublicAccount.ts | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/model/account/PublicAccount.ts b/src/model/account/PublicAccount.ts index a57cf40faa..9fc7964e4a 100644 --- a/src/model/account/PublicAccount.ts +++ b/src/model/account/PublicAccount.ts @@ -57,43 +57,39 @@ export class PublicAccount { /** * Verify a signature. * - * @param {string} publicKey - The public key to use for verification. + * @param {PublicAccount} publicAccount - The public account to use for verification. * @param {string} data - The data to verify. * @param {string} signature - The signature to verify. * * @return {boolean} - True if the signature is valid, false otherwise. */ - static verifySignature(publicKey: string, data: string, signature: string): boolean { - if (!publicKey || !data || !signature) { - throw new Error('Missing argument !'); - } - - if (publicKey.length !== 64 && publicKey.length !== 66) { - throw new Error('Not a valid public key'); + static verifySignature(publicAccount: PublicAccount, data: string, signature: string): boolean { + if (!publicAccount || !data || !signature) { + throw new Error('Missing argument'); } if (convert.isHexString(signature)) { - throw new Error('Signature must be hexadecimal only !'); + throw new Error('Signature must be hexadecimal only'); } if (signature.length !== 128) { - throw new Error('Signature length is incorrect !'); + throw new Error('Signature length is incorrect'); } // Convert signature key to Uint8Array - const _signature = convert.hexToUint8(signature); + const convertedSignature = convert.hexToUint8(signature); - let _data; + let convertedData; // Convert data to hex if data is not hex if (!convert.isHexString(data)) { - _data = convert.utf8ToHex(data); + convertedData = convert.utf8ToHex(data); } // Convert to Uint8Array - _data = convert.hexToUint8(_data); + convertedData = convert.hexToUint8(convertedData); - return KeyPair.verify(publicKey, _data, _signature); + return KeyPair.verify(publicAccount.publicKey, convertedData, convertedSignature); } /** From f1a4182c33e9451e64748988d103ea71319244e8 Mon Sep 17 00:00:00 2001 From: Faiz Faidurrahman Date: Wed, 27 Jun 2018 11:08:58 +0800 Subject: [PATCH 3/3] Added test for verify signature --- src/model/account/PublicAccount.ts | 8 +-- test/model/account/PublicAccount.spec.ts | 86 +++++++++++++++++++++++- 2 files changed, 87 insertions(+), 7 deletions(-) diff --git a/src/model/account/PublicAccount.ts b/src/model/account/PublicAccount.ts index 9fc7964e4a..3b9ea64166 100644 --- a/src/model/account/PublicAccount.ts +++ b/src/model/account/PublicAccount.ts @@ -68,14 +68,14 @@ export class PublicAccount { throw new Error('Missing argument'); } - if (convert.isHexString(signature)) { - throw new Error('Signature must be hexadecimal only'); - } - if (signature.length !== 128) { throw new Error('Signature length is incorrect'); } + if (!convert.isHexString(signature)) { + throw new Error('Signature must be hexadecimal only'); + } + // Convert signature key to Uint8Array const convertedSignature = convert.hexToUint8(signature); diff --git a/test/model/account/PublicAccount.spec.ts b/test/model/account/PublicAccount.spec.ts index 9b7ddd3818..86e9006883 100644 --- a/test/model/account/PublicAccount.spec.ts +++ b/test/model/account/PublicAccount.spec.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import {expect} from 'chai'; -import {PublicAccount} from '../../../src/model/account/PublicAccount'; -import {NetworkType} from '../../../src/model/blockchain/NetworkType'; +import { expect } from 'chai'; +import { PublicAccount } from '../../../src/model/account/PublicAccount'; +import { NetworkType } from '../../../src/model/blockchain/NetworkType'; describe('PublicAccount', () => { const publicKey = 'b4f12e7c9f6946091e2cb8b6d3a12b50d17ccbbf646386ea27ce2946a7423dcf'; @@ -27,3 +27,83 @@ describe('PublicAccount', () => { expect(publicAccount.address.plain()).to.be.equal('SARNASAS2BIAB6LMFA3FPMGBPGIJGK6IJETM3ZSP'); }); }); + +describe('Signature verification', () => { + it('Can verify a signature', () => { + // Arrange:' + const signerPublicAccount = PublicAccount.createFromPublicKey('22816F825B4CACEA334723D51297D8582332D8B875A5829908AAE85831ABB508', + NetworkType.MIJIN_TEST); + const data = 'I am so so so awesome as always'; + const signature = 'B01DCA6484026C2ECDF3C822E64DEAAFC15EBCCE337EEE209C28513CB5351CDED8863A8E7B855CD471B55C91FAE611C548625C9A5916A555A24F72F3526FA508'; // tslint:disable-line + + // Act & Assert: + expect(PublicAccount.verifySignature(signerPublicAccount, data, signature)).equal(true); + }); + + it('Throw error if signature has invalid length', () => { + // Arrange: + const signerPublicAccount = PublicAccount.createFromPublicKey('22816F825B4CACEA334723D51297D8582332D8B875A5829908AAE85831ABB508', + NetworkType.MIJIN_TEST); + const data = 'I am so so so awesome as always'; + const signature = 'B01DCA6484026C2ECDF3C822E64DEAAFC15EBCCE337EEE209C28513CB5351CDED8863A8E7B855CD471B55C91FAE611C5486'; // tslint:disable-line + + // Act & Assert: + expect(() => { PublicAccount.verifySignature(signerPublicAccount, data, signature); }).to.throw('Signature length is incorrect'); + }); + + it('Throw error if signature is not strictly hexadecimal', () => { + // Arrange: + const signerPublicAccount = PublicAccount.createFromPublicKey('22816F825B4CACEA334723D51297D8582332D8B875A5829908AAE85831ABB508', + NetworkType.MIJIN_TEST); + const data = 'I am so so so awesome as always'; + const signature = 'B01DCA6484026C2ECDF3C822E64DEAAFC15EBCCE337EEE209C28513CB5351CDED8863A8E7B855CD471B55C91FAE611C548625C9A5916A555A24F72F35a1wwwww';// tslint:disable-line + + // Act & Assert: + expect(() => { PublicAccount.verifySignature(signerPublicAccount, data, signature); }) + .to.throw('Signature must be hexadecimal only'); + }); + + it('Return false if wrong public key provided', () => { + // Arrange: + const signerPublicAccount = PublicAccount.createFromPublicKey('12816F825B4CACEA334723D51297D8582332D8B875A5829908AAE85831ABB509', + NetworkType.MIJIN_TEST); + const data = 'I am so so so awesome as always'; + const signature = 'B01DCA6484026C2ECDF3C822E64DEAAFC15EBCCE337EEE209C28513CB5351CDED8863A8E7B855CD471B55C91FAE611C548625C9A5916A555A24F72F3526FA508';// tslint:disable-line + + // Act & Assert: + expect(PublicAccount.verifySignature(signerPublicAccount, data, signature)).equal(false); + }); + + it('Return false if data is not corresponding to signature provided', () => { + // Arrange: + const signerPublicAccount = PublicAccount.createFromPublicKey('22816F825B4CACEA334723D51297D8582332D8B875A5829908AAE85831ABB508', + NetworkType.MIJIN_TEST); + const data = 'I am awesome as always'; + const signature = 'B01DCA6484026C2ECDF3C822E64DEAAFC15EBCCE337EEE209C28513CB5351CDED8863A8E7B855CD471B55C91FAE611C548625C9A5916A555A24F72F3526FA508';// tslint:disable-line + + // Act & Assert: + expect(PublicAccount.verifySignature(signerPublicAccount, data, signature)).equal(false); + }); + + it('Return false if signature is not corresponding to data provided', () => { + // Arrange: + const signerPublicAccount = PublicAccount.createFromPublicKey('22816F825B4CACEA334723D51297D8582332D8B875A5829908AAE85831ABB508', + NetworkType.MIJIN_TEST); + const data = 'I am so so so awesome as always'; + const signature = 'A01DCA6484026C2ECDF3C822E64DEAAFC15EBCCE337EEE209C28513CB5351CDED8863A8E7B855CD471B55C91FAE611C548625C9A5916A555A24F72F3526FA509';// tslint:disable-line + + // Act & Assert: + expect(PublicAccount.verifySignature(signerPublicAccount, data, signature)).equal(false); + }); + + it('Throw error if signature verification is missing a parameter', () => { + // Arrange: + const signerPublicAccount = PublicAccount.createFromPublicKey('22816F825B4CACEA334723D51297D8582332D8B875A5829908AAE85831ABB508', + NetworkType.MIJIN_TEST); + const data = ''; + const signature = 'B01DCA6484026C2ECDF3C822E64DEAAFC15EBCCE337EEE209C28513CB5351CDED8863A8E7B855CD471B55C91FAE611C548625C9A5916A555A24F72F3526FA508';// tslint:disable-line + + // Act & Assert: + expect(() => { PublicAccount.verifySignature(signerPublicAccount, data, signature); }).to.throw('Missing argument'); + }); +});