165 lines
4.7 KiB
JavaScript
165 lines
4.7 KiB
JavaScript
var __awaiter = this && this.__awaiter || function (thisArg, _arguments, P, generator) {
|
|
function adopt(value) {
|
|
return value instanceof P ? value : new P(function (resolve) {
|
|
resolve(value);
|
|
});
|
|
}
|
|
|
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
function fulfilled(value) {
|
|
try {
|
|
step(generator.next(value));
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
}
|
|
|
|
function rejected(value) {
|
|
try {
|
|
step(generator["throw"](value));
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
}
|
|
|
|
function step(result) {
|
|
result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
}
|
|
|
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
});
|
|
}; // For ESM compatibility
|
|
|
|
|
|
import pkg from 'elliptic';
|
|
const {
|
|
ec
|
|
} = pkg;
|
|
import { BeeError } from "../utils/error.js";
|
|
import { isBytes, assertBytes, wrapBytesWithHelpers } from "../utils/bytes.js";
|
|
import { keccak256Hash } from "../utils/hash.js";
|
|
import { hexToBytes, makeHexString } from "../utils/hex.js";
|
|
import { SIGNATURE_BYTES_LENGTH, SIGNATURE_HEX_LENGTH } from "../types/index.js";
|
|
import { isStrictlyObject } from "../utils/type.js";
|
|
const UNCOMPRESSED_RECOVERY_ID = 27;
|
|
|
|
function hashWithEthereumPrefix(data) {
|
|
const ethereumSignedMessagePrefix = `\x19Ethereum Signed Message:\n${data.length}`;
|
|
const prefixBytes = new TextEncoder().encode(ethereumSignedMessagePrefix);
|
|
return keccak256Hash(prefixBytes, data);
|
|
}
|
|
/**
|
|
* The default signer function that can be used for integrating with
|
|
* other applications (e.g. wallets).
|
|
*
|
|
* @param data The data to be signed
|
|
* @param privateKey The private key used for signing the data
|
|
*/
|
|
|
|
|
|
export function defaultSign(data, privateKey) {
|
|
const curve = new ec('secp256k1');
|
|
const keyPair = curve.keyFromPrivate(privateKey);
|
|
const hashedDigest = hashWithEthereumPrefix(data);
|
|
const sigRaw = curve.sign(hashedDigest, keyPair, {
|
|
canonical: true,
|
|
pers: undefined
|
|
});
|
|
|
|
if (sigRaw.recoveryParam === null) {
|
|
throw new BeeError('signDigest recovery param was null');
|
|
}
|
|
|
|
const signature = new Uint8Array([...sigRaw.r.toArray('be', 32), ...sigRaw.s.toArray('be', 32), sigRaw.recoveryParam + UNCOMPRESSED_RECOVERY_ID]);
|
|
return signature;
|
|
}
|
|
|
|
function publicKeyToAddress(pubKey) {
|
|
const pubBytes = pubKey.encode('array', false);
|
|
return keccak256Hash(pubBytes.slice(1)).slice(12);
|
|
}
|
|
/**
|
|
* Recovers the ethereum address from a given signature.
|
|
*
|
|
* Can be used for verifying a piece of data when the public key is
|
|
* known.
|
|
*
|
|
* @param signature The signature
|
|
* @param digest The digest of the data
|
|
*
|
|
* @returns the recovered address
|
|
*/
|
|
|
|
|
|
export function recoverAddress(signature, digest) {
|
|
const curve = new ec('secp256k1');
|
|
const sig = {
|
|
r: signature.slice(0, 32),
|
|
s: signature.slice(32, 64)
|
|
};
|
|
const recoveryParam = signature[64] - UNCOMPRESSED_RECOVERY_ID;
|
|
const hash = hashWithEthereumPrefix(digest);
|
|
const recPubKey = curve.recoverPubKey(hash, sig, recoveryParam);
|
|
return publicKeyToAddress(recPubKey);
|
|
}
|
|
/**
|
|
* Creates a singer object that can be used when the private key is known.
|
|
*
|
|
* @param privateKey The private key
|
|
*/
|
|
|
|
export function makePrivateKeySigner(privateKey) {
|
|
const curve = new ec('secp256k1');
|
|
const keyPair = curve.keyFromPrivate(privateKey);
|
|
const address = publicKeyToAddress(keyPair.getPublic());
|
|
return {
|
|
sign: digest => defaultSign(digest, privateKey),
|
|
address
|
|
};
|
|
}
|
|
export function assertSigner(signer) {
|
|
if (!isStrictlyObject(signer)) {
|
|
throw new TypeError('Signer must be an object!');
|
|
}
|
|
|
|
const typedSigner = signer;
|
|
|
|
if (!isBytes(typedSigner.address, 20)) {
|
|
throw new TypeError("Signer's address must be Uint8Array with 20 bytes!");
|
|
}
|
|
|
|
if (typeof typedSigner.sign !== 'function') {
|
|
throw new TypeError('Signer sign property needs to be function!');
|
|
}
|
|
}
|
|
export function makeSigner(signer) {
|
|
if (typeof signer === 'string') {
|
|
const hexKey = makeHexString(signer, 64);
|
|
const keyBytes = hexToBytes(hexKey); // HexString is verified for 64 length => 32 is guaranteed
|
|
|
|
return makePrivateKeySigner(keyBytes);
|
|
} else if (signer instanceof Uint8Array) {
|
|
assertBytes(signer, 32);
|
|
return makePrivateKeySigner(signer);
|
|
}
|
|
|
|
assertSigner(signer);
|
|
return signer;
|
|
}
|
|
export function sign(signer, data) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
const result = yield signer.sign(wrapBytesWithHelpers(data));
|
|
|
|
if (typeof result === 'string') {
|
|
const hexString = makeHexString(result, SIGNATURE_HEX_LENGTH);
|
|
return hexToBytes(hexString);
|
|
}
|
|
|
|
if (result instanceof Uint8Array) {
|
|
assertBytes(result, SIGNATURE_BYTES_LENGTH);
|
|
return result;
|
|
}
|
|
|
|
throw new TypeError('Invalid output of sign function!');
|
|
});
|
|
} |