Skip to content

Commit

Permalink
Merge pull request #14 from nemtech/task/10/verify-signature
Browse files Browse the repository at this point in the history
Task/10/verify signature
  • Loading branch information
Aleix Morgadas authored Jun 27, 2018
2 parents 94bc0d6 + 7977ea4 commit e3c7b5c
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 6 deletions.
11 changes: 11 additions & 0 deletions src/model/account/Account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,15 @@ export class Account {
public signCosignatureTransaction(cosignatureTransaction: CosignatureTransaction): CosignatureSignedTransaction {
return cosignatureTransaction.signWith(this);
}

/**
* Sign raw data
* @param data - Data to be signed
* @return {string} - Signed data result
*/
public signData(data: string): string {
return convert.uint8ToHex(KeyPair.sign(this.keyPair,
convert.hexToUint8(convert.utf8ToHex(data)),
));
}
}
38 changes: 36 additions & 2 deletions src/model/account/PublicAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@
* limitations under the License.
*/

import {NetworkType} from '../blockchain/NetworkType';
import {Address} from './Address';
import { convert, KeyPair } from 'nem2-library';
import { NetworkType } from '../blockchain/NetworkType';
import { Address } from './Address';

const Hash512 = 64;

/**
* The public account structure contains account's address and public key.
Expand Down Expand Up @@ -53,6 +56,36 @@ export class PublicAccount {
return new PublicAccount(publicKey, address);
}

/**
* Verify a signature.
*
* @param {string} data - The data to verify.
* @param {string} signature - The signature to verify.
*
* @return {boolean} - True if the signature is valid, false otherwise.
*/
public verifySignature(data: string, signature: string): boolean {
if (!signature) {
throw new Error('Missing argument');
}

if (signature.length / 2 !== Hash512) {
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);

// Convert to Uint8Array
const convertedData = convert.hexToUint8(convert.utf8ToHex(data));

return KeyPair.verify(convert.hexToUint8(this.publicKey), convertedData, convertedSignature);
}

/**
* Compares public accounts for equality.
* @param publicAccount
Expand All @@ -61,4 +94,5 @@ export class PublicAccount {
equals(publicAccount: PublicAccount) {
return this.publicKey === publicAccount.publicKey && this.address.plain() === publicAccount.address.plain();
}

}
24 changes: 24 additions & 0 deletions test/model/account/Account.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import {expect} from 'chai';
import {Account} from '../../../src/model/account/Account';
import {NetworkType} from '../../../src/model/blockchain/NetworkType';
import { PublicAccount } from '../../../src/model/model';

describe('Account', () => {
const accountInformation = {
Expand Down Expand Up @@ -45,4 +46,27 @@ describe('Account', () => {
expect(account.address).to.not.be.equal(undefined);
});

describe('signData', () => {
it('utf-8', () => {
const account = Account.createFromPrivateKey(
'AB860ED1FE7C91C02F79C02225DAC708D7BD13369877C1F59E678CC587658C47',
NetworkType.MIJIN_TEST,
);
const publicAccount = account.publicAccount;
const signed = account.signData('catapult rocks!');
expect(publicAccount.verifySignature('catapult rocks!', signed))
.to.be.true;
});

it('hexa', () => {
const account = Account.createFromPrivateKey(
'AB860ED1FE7C91C02F79C02225DAC708D7BD13369877C1F59E678CC587658C47',
NetworkType.MIJIN_TEST,
);
const publicAccount = account.publicAccount;
const signed = account.signData('0xAA');
expect(publicAccount.verifySignature('0xAA', signed))
.to.be.true;
});
});
});
76 changes: 73 additions & 3 deletions test/model/account/PublicAccount.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -27,3 +27,73 @@ describe('PublicAccount', () => {
expect(publicAccount.address.plain()).to.be.equal('SARNASAS2BIAB6LMFA3FPMGBPGIJGK6IJETM3ZSP');
});
});

describe('Signature verification', () => {
it('Can verify a signature', () => {
// Arrange:'
const signerPublicAccount = PublicAccount.createFromPublicKey(
'1464953393CE96A08ABA6184601FD08864E910696B060FF7064474726E666CA8',
NetworkType.MIJIN_TEST);
const data = 'I am so so so awesome as always';
const signature = '2092660F5BD4AE832B2E290F34A76B41506EE473B02FD7FD468B32C80C945CF60A0D60D005FA9B2DB3AD3212F8028C1449D3DCF81C9FAB3EB4975A7409D8D802'; // tslint:disable-line

// Act & Assert:
expect(signerPublicAccount.verifySignature(data, signature)).to.be.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(() => { signerPublicAccount.verifySignature(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(() => { signerPublicAccount.verifySignature(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(signerPublicAccount.verifySignature(data, signature)).to.be.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(signerPublicAccount.verifySignature(data, signature)).to.be.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(signerPublicAccount.verifySignature(data, signature)).to.be.false;
});
});
2 changes: 1 addition & 1 deletion tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unnecessary-initializer": true,
"no-unused-expression": true,
"no-unused-expression": false,
"no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
Expand Down

0 comments on commit e3c7b5c

Please sign in to comment.