@polkadot/util-crypto
Version:
A collection of useful crypto utilities for @polkadot
82 lines (81 loc) • 3.61 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.signatureVerify = signatureVerify;
const util_1 = require("@polkadot/util");
const decode_js_1 = require("../address/decode.js");
const verify_js_1 = require("../ed25519/verify.js");
const verify_js_2 = require("../secp256k1/verify.js");
const verify_js_3 = require("../sr25519/verify.js");
const secp256k1VerifyHasher = (hashType) => (message, signature, publicKey) => (0, verify_js_2.secp256k1Verify)(message, signature, publicKey, hashType);
const VERIFIERS_ECDSA = [
['ecdsa', secp256k1VerifyHasher('blake2')],
['ethereum', secp256k1VerifyHasher('keccak')]
];
const VERIFIERS = [
['ed25519', verify_js_1.ed25519Verify],
['sr25519', verify_js_3.sr25519Verify]
];
function verifyDetect(result, { message, publicKey, signature }, verifiers = [...VERIFIERS, ...VERIFIERS_ECDSA]) {
result.isValid = verifiers.some(([crypto, verify]) => {
try {
if (verify(message, signature, publicKey)) {
result.crypto = crypto;
return true;
}
}
catch {
// do nothing, result.isValid still set to false
}
return false;
});
return result;
}
function verifyMultisig(result, { message, publicKey, signature }) {
if (![0, 1, 2].includes(signature[0]) || ![65, 66].includes(signature.length)) {
throw new Error(`Unknown crypto type, expected signature prefix [0..2], found ${signature[0]}`);
}
// If the signature is 66 bytes it must be an ecdsa signature
// containing: prefix [1 byte] + signature [65] bytes.
// Remove the and then verify
if (signature.length === 66) {
result = verifyDetect(result, { message, publicKey, signature: signature.subarray(1) }, VERIFIERS_ECDSA);
}
else {
// The signature contains 65 bytes which is either
// - A ed25519 or sr25519 signature [1 byte prefix + 64 bytes]
// - An ecdsa signature [65 bytes]
result = verifyDetect(result, { message, publicKey, signature: signature.subarray(1) }, VERIFIERS);
if (!result.isValid) {
result = verifyDetect(result, { message, publicKey, signature }, VERIFIERS_ECDSA);
}
// If both failed, explicitly set crypto to 'none'
if (!result.isValid) {
result.crypto = 'none';
}
}
return result;
}
function getVerifyFn(signature) {
return [0, 1, 2].includes(signature[0]) && [65, 66].includes(signature.length)
? verifyMultisig
: verifyDetect;
}
function signatureVerify(message, signature, addressOrPublicKey) {
const signatureU8a = (0, util_1.u8aToU8a)(signature);
if (![64, 65, 66].includes(signatureU8a.length)) {
throw new Error(`Invalid signature length, expected [64..66] bytes, found ${signatureU8a.length}`);
}
const publicKey = (0, decode_js_1.decodeAddress)(addressOrPublicKey);
const input = { message: (0, util_1.u8aToU8a)(message), publicKey, signature: signatureU8a };
const result = { crypto: 'none', isValid: false, isWrapped: (0, util_1.u8aIsWrapped)(input.message, true), publicKey };
const isWrappedBytes = (0, util_1.u8aIsWrapped)(input.message, false);
const verifyFn = getVerifyFn(signatureU8a);
verifyFn(result, input);
if (result.crypto !== 'none' || (result.isWrapped && !isWrappedBytes)) {
return result;
}
input.message = isWrappedBytes
? (0, util_1.u8aUnwrapBytes)(input.message)
: (0, util_1.u8aWrapBytes)(input.message);
return verifyFn(result, input);
}
;