ethers.js/src.ts/utils/secp256k1.ts

116 lines
3.9 KiB
TypeScript
Raw Normal View History

2018-06-13 22:39:39 +03:00
'use strict';
import { ec as EC } from 'elliptic';
import { getAddress } from './address';
import { arrayify, hexlify, hexZeroPad, splitSignature } from './bytes';
import { hashMessage } from './hash';
import { keccak256 } from './keccak256';
2018-06-17 23:47:28 +03:00
import { defineReadOnly } from './properties';
2018-06-15 11:18:17 +03:00
import * as errors from '../errors';
2018-06-15 11:18:17 +03:00
///////////////////////////////
// Imported Types
import { Arrayish, Signature } from './bytes';
///////////////////////////////
let _curve: EC = null
function getCurve() {
if (!_curve) {
_curve = new EC('secp256k1');
}
return _curve;
}
2018-06-15 11:18:17 +03:00
export class KeyPair {
readonly privateKey: string;
readonly publicKey: string;
readonly compressedPublicKey: string;
readonly publicKeyBytes: Uint8Array;
constructor(privateKey: Arrayish | string) {
let keyPair = getCurve().keyFromPrivate(arrayify(privateKey));
2018-06-15 11:18:17 +03:00
defineReadOnly(this, 'privateKey', hexlify(keyPair.priv.toArray('be', 32)));
defineReadOnly(this, 'publicKey', '0x' + keyPair.getPublic(false, 'hex'));
defineReadOnly(this, 'compressedPublicKey', '0x' + keyPair.getPublic(true, 'hex'));
defineReadOnly(this, 'publicKeyBytes', keyPair.getPublic().encode(null, true));
}
sign(digest: Arrayish | string): Signature {
let keyPair = getCurve().keyFromPrivate(arrayify(this.privateKey));
2018-06-15 11:18:17 +03:00
let signature = keyPair.sign(arrayify(digest), {canonical: true});
return {
recoveryParam: signature.recoveryParam,
2018-06-18 12:42:41 +03:00
r: hexZeroPad('0x' + signature.r.toString(16), 32),
s: hexZeroPad('0x' + signature.s.toString(16), 32),
v: 27 + signature.recoveryParam
2018-06-15 11:18:17 +03:00
}
}
computeSharedSecret(otherKey: Arrayish | string): string {
let keyPair = getCurve().keyFromPrivate(arrayify(this.privateKey));
let otherKeyPair = getCurve().keyFromPublic(arrayify(computePublicKey(otherKey)));
return hexZeroPad('0x' + keyPair.derive(otherKeyPair.getPublic()).toString(16), 32);
}
_addPoint(other: Arrayish | string): string {
let p0 = getCurve().keyFromPublic(arrayify(this.publicKey));
let p1 = getCurve().keyFromPublic(arrayify(other));
return "0x" + p0.pub.add(p1.pub).encodeCompressed("hex");
}
2018-06-15 11:18:17 +03:00
}
export function computePublicKey(key: Arrayish | string, compressed?: boolean): string {
2018-06-15 11:18:17 +03:00
let bytes = arrayify(key);
if (bytes.length === 32) {
let keyPair: KeyPair = new KeyPair(bytes);
if (compressed) {
return keyPair.compressedPublicKey;
}
return keyPair.publicKey;
} else if (bytes.length === 33) {
if (compressed) { return hexlify(bytes); }
return '0x' + getCurve().keyFromPublic(bytes).getPublic(false, 'hex');
2018-06-15 11:18:17 +03:00
} else if (bytes.length === 65) {
if (!compressed) { return hexlify(bytes); }
return '0x' + getCurve().keyFromPublic(bytes).getPublic(true, 'hex');
2018-06-15 11:18:17 +03:00
}
errors.throwError('invalid public or private key', errors.INVALID_ARGUMENT, { arg: 'key', value: '[REDACTED]' });
return null;
}
2018-06-13 22:39:39 +03:00
export function computeAddress(key: Arrayish | string): string {
// Strip off the leading "0x04"
let publicKey = '0x' + computePublicKey(key).slice(4);
return getAddress('0x' + keccak256(publicKey).substring(26));
}
export function recoverPublicKey(digest: Arrayish | string, signature: Signature | string): string {
let sig = splitSignature(signature);
let rs = { r: arrayify(sig.r), s: arrayify(sig.s) };
return '0x' + getCurve().recoverPubKey(arrayify(digest), rs, sig.recoveryParam).encode('hex', false);
}
export function recoverAddress(digest: Arrayish | string, signature: Signature | string): string {
return computeAddress(recoverPublicKey(arrayify(digest), signature));
}
export function verifyMessage(message: Arrayish | string, signature: Signature | string): string {
return recoverAddress(hashMessage(message), signature);
}