2018-06-13 22:39:39 +03:00
|
|
|
'use strict';
|
|
|
|
|
2018-06-17 23:32:57 +03:00
|
|
|
import { getAddress } from './address';
|
2018-06-17 23:47:28 +03:00
|
|
|
import { arrayify, Arrayish, hexlify } from './bytes';
|
2018-06-17 23:32:57 +03:00
|
|
|
import { keccak256 } from './keccak256';
|
2018-06-17 23:47:28 +03:00
|
|
|
import { defineReadOnly } from './properties';
|
2018-06-15 11:18:17 +03:00
|
|
|
|
2018-06-17 23:47:28 +03:00
|
|
|
import * as errors from './errors';
|
2018-06-15 11:18:17 +03:00
|
|
|
|
|
|
|
interface _BN {
|
2018-06-13 22:39:39 +03:00
|
|
|
toString(radix: number): string;
|
|
|
|
encode(encoding: string, compact: boolean): Uint8Array;
|
|
|
|
toArray(endian: string, width: number): Uint8Array;
|
|
|
|
}
|
|
|
|
|
2018-06-15 11:18:17 +03:00
|
|
|
interface _Signature {
|
|
|
|
r: _BN,
|
|
|
|
s: _BN,
|
2018-06-13 22:39:39 +03:00
|
|
|
recoveryParam: number
|
|
|
|
}
|
|
|
|
|
2018-06-15 11:18:17 +03:00
|
|
|
interface _KeyPair {
|
|
|
|
sign(message: Uint8Array, options: { canonical?: boolean }): _Signature;
|
2018-06-13 22:39:39 +03:00
|
|
|
getPublic(compressed: boolean, encoding?: string): string;
|
2018-06-15 11:18:17 +03:00
|
|
|
getPublic(): _BN;
|
2018-06-13 22:39:39 +03:00
|
|
|
getPrivate(encoding?: string): string;
|
|
|
|
encode(encoding: string, compressed: boolean): string;
|
2018-06-15 11:18:17 +03:00
|
|
|
priv: _BN;
|
2018-06-13 22:39:39 +03:00
|
|
|
}
|
|
|
|
|
2018-06-15 11:18:17 +03:00
|
|
|
interface _EC {
|
2018-06-13 22:39:39 +03:00
|
|
|
constructor(curve: string);
|
|
|
|
|
2018-06-15 11:18:17 +03:00
|
|
|
n: _BN;
|
2018-06-13 22:39:39 +03:00
|
|
|
|
2018-06-15 11:18:17 +03:00
|
|
|
keyFromPublic(publicKey: string | Uint8Array): _KeyPair;
|
|
|
|
keyFromPrivate(privateKey: string | Uint8Array): _KeyPair;
|
|
|
|
recoverPubKey(data: Uint8Array, signature: { r: Uint8Array, s: Uint8Array }, recoveryParam: number): _KeyPair;
|
2018-06-13 22:39:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
import * as elliptic from 'elliptic';
|
|
|
|
|
2018-06-15 11:18:17 +03:00
|
|
|
const curve:_EC = new elliptic.ec('secp256k1');
|
|
|
|
|
|
|
|
|
2018-06-17 23:32:57 +03:00
|
|
|
export const N = '0x' + curve.n.toString(16);
|
|
|
|
|
|
|
|
export interface Signature {
|
2018-06-15 11:18:17 +03:00
|
|
|
r: string;
|
|
|
|
s: string;
|
|
|
|
recoveryParam: number;
|
2018-06-17 23:32:57 +03:00
|
|
|
v?: number;
|
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) {
|
|
|
|
let keyPair: _KeyPair = curve.keyFromPrivate(arrayify(privateKey));
|
|
|
|
|
|
|
|
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): Signature {
|
|
|
|
let keyPair: _KeyPair = curve.keyFromPrivate(arrayify(this.privateKey));
|
|
|
|
let signature = keyPair.sign(arrayify(digest), {canonical: true});
|
|
|
|
return {
|
|
|
|
recoveryParam: signature.recoveryParam,
|
|
|
|
r: '0x' + signature.r.toString(16),
|
2018-06-17 23:32:57 +03:00
|
|
|
s: '0x' + signature.s.toString(16),
|
|
|
|
v: 27 + signature.recoveryParam
|
2018-06-15 11:18:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function recoverPublicKey(digest: Arrayish, signature: Signature): string {
|
|
|
|
let sig = {
|
|
|
|
r: arrayify(signature.r),
|
|
|
|
s: arrayify(signature.s)
|
|
|
|
};
|
2018-06-17 23:32:57 +03:00
|
|
|
return '0x' + curve.recoverPubKey(arrayify(digest), sig, signature.recoveryParam).encode('hex', false);
|
2018-06-15 11:18:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
export function computePublicKey(key: Arrayish, compressed?: boolean): string {
|
|
|
|
|
|
|
|
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' + curve.keyFromPublic(bytes).getPublic(false, 'hex');
|
|
|
|
|
|
|
|
} else if (bytes.length === 65) {
|
|
|
|
if (!compressed) { return hexlify(bytes); }
|
|
|
|
return '0x' + curve.keyFromPublic(bytes).getPublic(true, 'hex');
|
|
|
|
}
|
|
|
|
|
|
|
|
errors.throwError('invalid public or private key', errors.INVALID_ARGUMENT, { arg: 'key', value: '[REDACTED]' });
|
|
|
|
return null;
|
|
|
|
}
|
2018-06-13 22:39:39 +03:00
|
|
|
|
2018-06-17 23:32:57 +03:00
|
|
|
export function recoverAddress(digest: Arrayish, signature: Signature): string {
|
|
|
|
return computeAddress(recoverPublicKey(digest, signature));
|
|
|
|
}
|
|
|
|
|
|
|
|
export function computeAddress(key: string): string {
|
|
|
|
// Strip off the leading "0x04"
|
|
|
|
let publicKey = '0x' + computePublicKey(key).slice(4);
|
|
|
|
return getAddress('0x' + keccak256(publicKey).substring(26));
|
|
|
|
}
|
|
|
|
|