@node-dlc/crypto
Version:
Lightning cryptography utilities
201 lines (186 loc) • 5.98 kB
text/typescript
import secp256k1 from 'secp256k1';
/**
* Elliptic Curve Diffie-Helman on secp256k1 takes a
* public key and point multiplies it by the private key.
* The resulting compressed public key value is hashed via
* sha256.
*
* @param publicKey 33-byte compressed public key
* @param privateKey 32-byte private key
* @returns 32-byte sha256 of the point multiple
*/
export function ecdh(publicKey: Buffer, privateKey: Buffer): Buffer {
return Buffer.from(secp256k1.ecdh(publicKey, privateKey));
}
/**
* Performs modular multiplication on the secret value by multiplying by the
* tweak value
* @param secret 32-byte secret
* @param tweak 32-byte tweak
* @returns 32-byte secret
*/
export function privateKeyMul(secret: Buffer, tweak: Buffer): Buffer {
return Buffer.from(secp256k1.privateKeyTweakMul(Buffer.from(secret), tweak));
}
/**
* Create an ECDSA signature for the 32-byte message using the private
* key. This method returns the normalized low-s version of the signature
* as a 64-byte Buffer (r,s) values.
* @param msg 32-byte message
* @param privateKey 32-byte secp256k1 private key
* @returns signatures as 64-byte buffer with (r,s) pair
*/
export function sign(msg: Buffer, privateKey: Buffer): Buffer {
const { signature } = secp256k1.ecdsaSign(msg, privateKey);
secp256k1.signatureNormalize(signature);
return Buffer.from(signature);
}
/**
* Create an ECDSA signature for the 32-byte message using the private
* key. This method returns both the signature in the normalized low-s
* version of the signature as a 64-bte Buffer (r,s) and the recovery
* identifier.
* @param msg 32-byte message
* @param privateKey 32-byte secp256k1 private key
*/
export function signWithRecovery(
msg: Buffer,
privateKey: Buffer,
): { signature: Buffer; recovery: number } {
const { signature, recid } = secp256k1.ecdsaSign(msg, privateKey);
return {
signature: Buffer.from(signature),
recovery: recid,
};
}
/**
* Encodes a signature into a DER encoding. This encoding is 8-73 bytes
* in length depending on the length of the s value in the signature.
* @param sig 64-byte buffer containing (r, s)
*/
export function sigToDER(sig: Buffer): Buffer {
return Buffer.from(secp256k1.signatureExport(sig));
}
/**
* Verifies an ECDSA signature and returns true or false
* if the signatures is valid.
*
* @param msg 32-byte message (256 hash or message)
* @param sig 64-byte
* @param pubkey 33-byte compressed pubkey
* @returns true when signature is valid
*/
export function verifySig(msg: Buffer, sig: Buffer, pubkey: Buffer): boolean {
return secp256k1.ecdsaVerify(sig, msg, pubkey);
}
/**
* Returns true if the signature is a DER encoded signature
* @param sig a DER encoded signature
*/
export function isDERSig(sig: Buffer): boolean {
try {
secp256k1.signatureImport(sig);
return true;
} catch (ex) {
return false;
}
}
/**
* Parses a DER-encoded signature and returns the raw 64-byte signature (r,s)
* @param derSig DER-encoded signature buffer
* @returns 64-byte buffer containing raw (r,s) signature
*/
export function sigFromDER(derSig: Buffer): Buffer {
try {
const rawSig = secp256k1.signatureImport(derSig);
return Buffer.from(rawSig);
} catch (ex) {
throw new Error(`Invalid DER signature: ${ex.message}`);
}
}
/**
* Recovers the public key from the signature and recovery identifier
* for the signed message.
* @param signature 64-byte signature
* @param recovery recovery id
* @param message message that was signed
* @param compressed whether the pubkey is compressed
*/
export function recoverPubKey(
signature: Buffer,
recovery: number,
message: Buffer,
compressed = true,
): Buffer {
const result = secp256k1.ecdsaRecover(
signature,
recovery,
message,
compressed,
);
return Buffer.from(result);
}
/**
* Tweaks a public key by adding tweak * G to the point. The equation is
* T = P + t*G
*
* @param publicKey 33-byte or 65-byte public key
* @param tweak 32-byte scalar value that is multiplied by G
* @param compressed true to compress the resulting point
* @returns the 33-byte compressed or 65-byte uncompressed public key point
*/
export function publicKeyTweakAdd(
publicKey: Buffer,
tweak: Buffer,
compressed = true,
): Buffer {
return Buffer.from(secp256k1.publicKeyTweakAdd(publicKey, tweak, compressed));
}
/**
* Tweaks a public key by multiplying it against a scalar. The equation is
* T = P * t
*
* @param publicKey 33-byte or 65-byte public key
* @param tweak 32-byte tweak to multiply against the public key
* @param compressed true to compress the resulting point
* @returns the 33-byte compressed or 65-byte uncompressed public key point
*/
export function publicKeyTweakMul(
publicKey: Buffer,
tweak: Buffer,
compressed = true,
): Buffer {
return Buffer.from(secp256k1.publicKeyTweakMul(publicKey, tweak, compressed));
}
/**
* Performs point addition
*
* @param pubkeys list of 33-byte or 65-byte public keys
* @param compressed true to compress the resulting point
* @returns the 33-byte compressed or 65-byte uncompressed public key point
*/
export function publicKeyCombine(pubkeys: Buffer[], compressed = true): Buffer {
return Buffer.from(secp256k1.publicKeyCombine(pubkeys, compressed));
}
/**
* Tweaks a private key by adding a value to it. The question is: e + t.
*
* @param privateKey the 32-byte private key
* @param tweak a 32-byte tweak
*/
export function privateKeyTweakAdd(privateKey: Buffer, tweak: Buffer): Buffer {
return Buffer.from(
secp256k1.privateKeyTweakAdd(Buffer.from(privateKey), tweak),
);
}
/**
* Tweaks a private key by multiplying it. The equation is: e * t.
*
* @param privateKey the 32-byte private key
* @param tweak a 32-byte tweak
*/
export function privateKeyTweakMul(privateKey: Buffer, tweak: Buffer): Buffer {
return Buffer.from(
secp256k1.privateKeyTweakMul(Buffer.from(privateKey), tweak),
);
}