Pass publicKey through on verify (#883)

* Pass publicKey through on verify

* Some test fixes

* Adjust tests

* Explicit public

* Additional failing tests
This commit is contained in:
Jaco Greeff
2021-02-19 10:43:19 +01:00
committed by GitHub
parent aa9bf0780b
commit ae172e2c53
9 changed files with 116 additions and 51 deletions
+9
View File
@@ -1,5 +1,14 @@
# CHANGELOG
## 5.7.x
**Breaking change** The signature of `pair.verify` now explicitly requires the signer public key as a parameter
Changes:
- The signature on the keyring for `.verify` not explicitly requires the `publicKey` of the signer. Previously it only verified against an own public key.
## 5.6.3 Feb 16, 2021
Contributed:
+31 -17
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
import { hexToU8a, stringToU8a } from '@polkadot/util';
import { cryptoWaitReady, encodeAddress, setSS58Format } from '@polkadot/util-crypto';
import { cryptoWaitReady, encodeAddress, randomAsU8a, setSS58Format } from '@polkadot/util-crypto';
import Keyring from '.';
@@ -83,8 +83,9 @@ describe('keypair', (): void => {
const pair = keyring.getPair(publicKeyOne);
const signature = pair.sign(MESSAGE);
expect(pair.verify(MESSAGE, signature)).toBe(true);
expect(pair.verify(new Uint8Array(), signature)).toBe(false);
expect(pair.verify(MESSAGE, signature, pair.publicKey)).toBe(true);
expect(pair.verify(MESSAGE, signature, randomAsU8a())).toBe(false);
expect(pair.verify(new Uint8Array(), signature, pair.publicKey)).toBe(false);
});
it('signs and verifies (withType)', (): void => {
@@ -92,8 +93,9 @@ describe('keypair', (): void => {
const pair = keyring.getPair(publicKeyOne);
const signature = pair.sign(MESSAGE, { withType: true });
expect(pair.verify(MESSAGE, signature)).toBe(true);
expect(pair.verify(new Uint8Array(), signature)).toBe(false);
expect(pair.verify(MESSAGE, signature, pair.publicKey)).toBe(true);
expect(pair.verify(MESSAGE, signature, randomAsU8a())).toBe(false);
expect(pair.verify(new Uint8Array(), signature, pair.publicKey)).toBe(false);
});
});
@@ -159,8 +161,9 @@ describe('keypair', (): void => {
const pair = keyring.getPair(publicKeyOne);
const signature = pair.sign(MESSAGE);
expect(pair.verify(MESSAGE, signature)).toBe(true);
expect(pair.verify(new Uint8Array(), signature)).toBe(false);
expect(pair.verify(MESSAGE, signature, pair.publicKey)).toBe(true);
expect(pair.verify(MESSAGE, signature, randomAsU8a())).toBe(false);
expect(pair.verify(new Uint8Array(), signature, pair.publicKey)).toBe(false);
});
it('signs and verifies (withType)', (): void => {
@@ -168,8 +171,9 @@ describe('keypair', (): void => {
const pair = keyring.getPair(publicKeyOne);
const signature = pair.sign(MESSAGE, { withType: true });
expect(pair.verify(MESSAGE, signature)).toBe(true);
expect(pair.verify(new Uint8Array(), signature)).toBe(false);
expect(pair.verify(MESSAGE, signature, pair.publicKey)).toBe(true);
expect(pair.verify(MESSAGE, signature, randomAsU8a())).toBe(false);
expect(pair.verify(new Uint8Array(), signature, pair.publicKey)).toBe(false);
});
});
@@ -264,8 +268,9 @@ describe('keypair', (): void => {
const pair = keyring.getPair(addressKeyOne);
const signature = pair.sign(MESSAGE);
expect(pair.verify(MESSAGE, signature)).toBe(true);
expect(pair.verify(new Uint8Array(), signature)).toBe(false);
expect(pair.verify(MESSAGE, signature, pair.publicKey)).toBe(true);
expect(pair.verify(MESSAGE, signature, randomAsU8a())).toBe(false);
expect(pair.verify(new Uint8Array(), signature, pair.publicKey)).toBe(false);
});
it('signs and verifies (withType)', (): void => {
@@ -273,8 +278,9 @@ describe('keypair', (): void => {
const pair = keyring.getPair(addressKeyOne);
const signature = pair.sign(MESSAGE, { withType: true });
expect(pair.verify(MESSAGE, signature)).toBe(true);
expect(pair.verify(new Uint8Array(), signature)).toBe(false);
expect(pair.verify(MESSAGE, signature, pair.publicKey)).toBe(true);
expect(pair.verify(MESSAGE, signature, randomAsU8a())).toBe(false);
expect(pair.verify(new Uint8Array(), signature, pair.publicKey)).toBe(false);
});
});
@@ -328,9 +334,13 @@ describe('keypair', (): void => {
);
const signature = signer.sign(MESSAGE);
const dummyPublic = verifier.publicKey.slice();
expect(verifier.verify(MESSAGE, signature)).toBe(true);
expect(verifier.verify(new Uint8Array(), signature)).toBe(false);
dummyPublic[dummyPublic.length - 1] = 0;
expect(verifier.verify(MESSAGE, signature, signer.publicKey)).toBe(true);
expect(verifier.verify(MESSAGE, signature, dummyPublic)).toBe(false);
expect(verifier.verify(new Uint8Array(), signature, signer.publicKey)).toBe(false);
});
it('allows for signing/verification (withType)', (): void => {
@@ -341,9 +351,13 @@ describe('keypair', (): void => {
);
const signature = signer.sign(MESSAGE, { withType: true });
const dummyPublic = verifier.publicKey.slice();
expect(verifier.verify(MESSAGE, signature)).toBe(true);
expect(verifier.verify(new Uint8Array(), signature)).toBe(false);
dummyPublic[dummyPublic.length - 1] = 0;
expect(verifier.verify(MESSAGE, signature, signer.publicKey)).toBe(true);
expect(verifier.verify(MESSAGE, signature, dummyPublic)).toBe(false);
expect(verifier.verify(new Uint8Array(), signature, signer.publicKey)).toBe(false);
});
});
+28 -3
View File
@@ -42,16 +42,28 @@ describe('pair', (): void => {
expect(
keyring.alice.verify(
new Uint8Array([0x61, 0x62, 0x63, 0x64]),
SIGNATURE
SIGNATURE,
keyring.alice.publicKey
)
).toEqual(true);
});
it('fails a correctly signed message (signer changed)', (): void => {
expect(
keyring.alice.verify(
new Uint8Array([0x61, 0x62, 0x63, 0x64, 0x65]),
SIGNATURE,
keyring.bob.publicKey
)
).toEqual(false);
});
it('fails a correctly signed message (message changed)', (): void => {
expect(
keyring.alice.verify(
new Uint8Array([0x61, 0x62, 0x63, 0x64, 0x65]),
SIGNATURE
SIGNATURE,
keyring.alice.publicKey
)
).toEqual(false);
});
@@ -62,11 +74,24 @@ describe('pair', (): void => {
expect(
keyring.alice.vrfVerify(
message,
keyring.alice.vrfSign(message)
keyring.alice.vrfSign(message),
keyring.alice.publicKey
)
).toBe(true);
});
it('fails vrf sign and verify (publicKey changed)', (): void => {
const message = new Uint8Array([0x61, 0x62, 0x63, 0x64, 0x65]);
expect(
keyring.alice.vrfVerify(
message,
keyring.alice.vrfSign(message),
keyring.bob.publicKey
)
).toBe(false);
});
it('allows setting/getting of meta', (): void => {
keyring.bob.setMeta({ foo: 'bar', something: 'else' });
+12 -9
View File
@@ -181,8 +181,8 @@ export function createPair ({ toSS58, type }: Setup, { publicKey, secretKey }: P
unlock: (passphrase?: string): void => {
return decodePkcs8(passphrase);
},
verify: (message: string | Uint8Array, signature: Uint8Array): boolean => {
return signatureVerify(message, signature, TYPE_ADDRESS[type](publicKey)).isValid;
verify: (message: string | Uint8Array, signature: Uint8Array, _signerPublic: string | Uint8Array): boolean => {
return signatureVerify(message, signature, TYPE_ADDRESS[type](u8aToU8a(_signerPublic))).isValid;
},
vrfSign: (message: string | Uint8Array, context?: string | Uint8Array, extra?: string | Uint8Array): Uint8Array => {
assert(!isLocked(secretKey), 'Cannot sign with a locked key pair');
@@ -195,13 +195,16 @@ export function createPair ({ toSS58, type }: Setup, { publicKey, secretKey }: P
return u8aConcat(vrfHash(proof, context, extra), proof);
},
vrfVerify: (message: string | Uint8Array, vrfResult: Uint8Array, context?: string | Uint8Array, extra?: string | Uint8Array): boolean => {
return type === 'sr25519'
? schnorrkelVrfVerify(message, vrfResult, publicKey, context, extra)
: (
signatureVerify(message, u8aConcat(TYPE_PREFIX[type], vrfResult.subarray(32)), TYPE_ADDRESS[type](publicKey)).isValid &&
u8aEq(vrfResult.subarray(0, 32), vrfHash(vrfResult.subarray(32), context, extra))
);
vrfVerify: (message: string | Uint8Array, vrfResult: Uint8Array, _signerPublic: Uint8Array | string, context?: string | Uint8Array, extra?: string | Uint8Array): boolean => {
const signerPublic = TYPE_ADDRESS[type](u8aToU8a(_signerPublic));
if (type === 'sr25519') {
return schnorrkelVrfVerify(message, vrfResult, publicKey, context, extra);
}
const result = signatureVerify(message, u8aConcat(TYPE_PREFIX[type], vrfResult.subarray(32)), signerPublic);
return result.isValid && u8aEq(vrfResult.subarray(0, 32), vrfHash(vrfResult.subarray(32), context, extra));
}
};
}
+6 -6
View File
@@ -29,8 +29,8 @@ describe('vrf', (): void => {
const sig2 = ecdsa.vrfSign(MESSAGE, CONTEXT);
expect(u8aToHex(sig1)).toEqual(u8aToHex(sig2));
expect(ecdsa.vrfVerify(MESSAGE, sig1, CONTEXT)).toEqual(true);
expect(ecdsa.vrfVerify(MESSAGE, sig2, CONTEXT)).toEqual(true);
expect(ecdsa.vrfVerify(MESSAGE, sig1, ecdsa.publicKey, CONTEXT)).toEqual(true);
expect(ecdsa.vrfVerify(MESSAGE, sig2, ecdsa.publicKey, CONTEXT)).toEqual(true);
});
it('has deterministic signature values for ed25519', (): void => {
@@ -38,8 +38,8 @@ describe('vrf', (): void => {
const sig2 = ed25519.vrfSign(MESSAGE, CONTEXT);
expect(u8aToHex(sig1)).toEqual(u8aToHex(sig2));
expect(ed25519.vrfVerify(MESSAGE, sig1, CONTEXT)).toEqual(true);
expect(ed25519.vrfVerify(MESSAGE, sig2, CONTEXT)).toEqual(true);
expect(ed25519.vrfVerify(MESSAGE, sig1, ed25519.publicKey, CONTEXT)).toEqual(true);
expect(ed25519.vrfVerify(MESSAGE, sig2, ed25519.publicKey, CONTEXT)).toEqual(true);
});
it('has deterministic signature values for sr25519', (): void => {
@@ -47,7 +47,7 @@ describe('vrf', (): void => {
const sig2 = sr25519.vrfSign(MESSAGE, CONTEXT);
expect(u8aToHex(sig1.slice(0, 32))).toEqual(u8aToHex(sig2.slice(0, 32)));
expect(sr25519.vrfVerify(MESSAGE, sig1, CONTEXT)).toEqual(true);
expect(sr25519.vrfVerify(MESSAGE, sig2, CONTEXT)).toEqual(true);
expect(sr25519.vrfVerify(MESSAGE, sig1, sr25519.publicKey, CONTEXT)).toEqual(true);
expect(sr25519.vrfVerify(MESSAGE, sig2, sr25519.publicKey, CONTEXT)).toEqual(true);
});
});
+2 -2
View File
@@ -48,9 +48,9 @@ export interface KeyringPair {
sign (message: string | Uint8Array, options?: SignOptions): Uint8Array;
toJson (passphrase?: string): KeyringPair$Json;
unlock (passphrase?: string): void;
verify (message: string | Uint8Array, signature: Uint8Array): boolean;
verify (message: string | Uint8Array, signature: Uint8Array, signerPublic: string | Uint8Array): boolean;
vrfSign (message: string | Uint8Array, context?: string | Uint8Array, extra?: string | Uint8Array): Uint8Array;
vrfVerify (message: string | Uint8Array, vrfResult: Uint8Array, context?: string | Uint8Array, extra?: string | Uint8Array): boolean;
vrfVerify (message: string | Uint8Array, vrfResult: Uint8Array, signerPublic: Uint8Array | string, context?: string | Uint8Array, extra?: string | Uint8Array): boolean;
}
export interface KeyringPairs {
@@ -36,21 +36,24 @@ describe('signatureVerify', (): void => {
it('verifies an ed25519 signature', (): void => {
expect(signatureVerify(MESSAGE, SIG_ED, ADDR_ED)).toEqual({
crypto: 'ed25519',
isValid: true
isValid: true,
publicKey: decodeAddress(ADDR_ED)
});
});
it('verifies an ecdsa signature', (): void => {
expect(signatureVerify(MESSAGE, SIG_EC, ADDR_EC)).toEqual({
crypto: 'ecdsa',
isValid: true
isValid: true,
publicKey: decodeAddress(ADDR_EC)
});
});
it('verifies an ethereum signature', (): void => {
expect(signatureVerify(MESSAGE, SIG_ET, ADDR_ET)).toEqual({
crypto: 'ethereum',
isValid: true
isValid: true,
publicKey: hexToU8a(ADDR_ET)
});
});
@@ -63,35 +66,40 @@ describe('signatureVerify', (): void => {
'0x002309df96687e44280bb72c3818358faeeb699c'
)).toEqual({
crypto: 'ethereum',
isValid: true
isValid: true,
publicKey: hexToU8a('0x002309df96687e44280bb72c3818358faeeb699c')
});
});
it('fails on invalid ethereum signature', (): void => {
expect(signatureVerify(MESSAGE, SIG_EC, ADDR_ET)).toEqual({
crypto: 'none',
isValid: false
isValid: false,
publicKey: hexToU8a(ADDR_ET)
});
});
it('verifies an sr25519 signature', (): void => {
expect(signatureVerify(MESSAGE, SIG_SR, ADDR_SR)).toEqual({
crypto: 'sr25519',
isValid: true
isValid: true,
publicKey: decodeAddress(ADDR_SR)
});
});
it('allows various inputs', (): void => {
expect(signatureVerify(stringToU8a(MESSAGE), hexToU8a(SIG_ED), decodeAddress(ADDR_ED))).toEqual({
crypto: 'ed25519',
isValid: true
isValid: true,
publicKey: decodeAddress(ADDR_ED)
});
});
it('fails on an invalid signature', (): void => {
expect(signatureVerify(MESSAGE, SIG_SR, ADDR_ED)).toEqual({
crypto: 'none',
isValid: false
isValid: false,
publicKey: decodeAddress(ADDR_ED)
});
});
});
@@ -100,35 +108,40 @@ describe('signatureVerify', (): void => {
it('verifies an ed25519 signature', (): void => {
expect(signatureVerify(MESSAGE, MUL_ED, ADDR_ED)).toEqual({
crypto: 'ed25519',
isValid: true
isValid: true,
publicKey: decodeAddress(ADDR_ED)
});
});
it('verifies an ecdsa signature', (): void => {
expect(signatureVerify(MESSAGE, MUL_EC, ADDR_EC)).toEqual({
crypto: 'ecdsa',
isValid: true
isValid: true,
publicKey: decodeAddress(ADDR_EC)
});
});
it('verifies an ethereum signature', (): void => {
expect(signatureVerify(MESSAGE, MUL_ET, ADDR_ET)).toEqual({
crypto: 'ethereum',
isValid: true
isValid: true,
publicKey: hexToU8a(ADDR_ET)
});
});
it('verifies an sr25519 signature', (): void => {
expect(signatureVerify(MESSAGE, MUL_SR, ADDR_SR)).toEqual({
crypto: 'sr25519',
isValid: true
isValid: true,
publicKey: decodeAddress(ADDR_SR)
});
});
it('fails on an invalid signature', (): void => {
expect(signatureVerify(MESSAGE, MUL_SR, ADDR_ED)).toEqual({
crypto: 'sr25519',
isValid: false
isValid: false,
publicKey: new Uint8Array([61, 12, 55, 211, 0, 211, 97, 199, 4, 37, 17, 213, 81, 175, 166, 23, 251, 199, 144, 210, 19, 83, 186, 1, 196, 231, 14, 156, 171, 46, 141, 146])
});
});
});
+1 -1
View File
@@ -79,9 +79,9 @@ export function signatureVerify (message: Uint8Array | string, signature: Uint8A
assert([64, 65, 66].includes(signatureU8a.length), `Invalid signature length, expected [64..66] bytes, found ${signatureU8a.length}`);
const result: VerifyResult = { crypto: 'none', isValid: false };
const publicKey = decodeAddress(addressOrPublicKey);
const input = { message, publicKey, signature: signatureU8a };
const result: VerifyResult = { crypto: 'none', isValid: false, publicKey };
return [0, 1, 2].includes(signatureU8a[0]) && [65, 66].includes(signatureU8a.length)
? verifyMultisig(result, input)
+1
View File
@@ -16,4 +16,5 @@ export type KeypairType = 'ed25519' | 'sr25519' | 'ecdsa' | 'ethereum';
export interface VerifyResult {
crypto: 'none' | KeypairType;
isValid: boolean;
publicKey: Uint8Array;
}