Files
sonr/packages/es/src/codec/sign.ts
T
Prad NukalaandGitHub 13e6c3e84d Master (#1262)
* clear

* feat: Add everything

* fix: Commenht
2025-10-03 14:45:52 -04:00

84 lines
2.9 KiB
TypeScript

import { hmac } from '@noble/hashes/hmac';
import { keccak_256 } from '@noble/hashes/sha3';
import { sha256 } from '@noble/hashes/sha256';
import * as secp256k1 from '@noble/secp256k1';
import { utf8 } from '@scure/base';
import type { CosmosTxV1beta1SignDoc as SignDoc } from '@sonr.io/es/protobufs';
import type { StdSignDoc } from '@sonr.io/es/registry';
import { serialiseSignDoc } from './serialise';
function sign(
bytes: Uint8Array,
privateKey: Uint8Array,
type: 'secp256k1' | 'ethsecp256k1'
): Uint8Array {
// Required polyfills for secp256k1 that must be called before any sign ops.
// See: https://github.com/paulmillr/noble-secp256k1?tab=readme-ov-file#usage
secp256k1.etc.hmacSha256Sync = (k, ...m) => hmac(sha256, k, secp256k1.etc.concatBytes(...m));
const hash = type === 'secp256k1' ? sha256(bytes) : keccak_256(bytes);
return secp256k1.sign(hash, privateKey).toCompactRawBytes();
}
/**
* Signs the given amino-encoded `stdSignDoc` with the given `privateKey` using
* secp256k1, and returns the signature bytes. For Injective, the `type` param
* must be set to `ethsecp256k1`.
*/
export function signAmino(
stdSignDoc: StdSignDoc,
privateKey: Uint8Array,
type: 'secp256k1' | 'ethsecp256k1' = 'secp256k1'
): Uint8Array {
return sign(serialiseSignDoc(stdSignDoc), privateKey, type);
}
/**
* Signs the given proto-encoded `signDoc` with the given `privateKey` using
* secp256k1, and returns the signature bytes. For Injective, the `type` param
* must be set to `ethsecp256k1`.
*/
export function signDirect(
signDoc: SignDoc,
privateKey: Uint8Array,
type: 'secp256k1' | 'ethsecp256k1' = 'secp256k1'
): Uint8Array {
return sign(signDoc.toBinary(), privateKey, type);
}
/**
* Hashes and returns the digest of the given EIP191 `message` bytes.
*/
export function hashEthArbitraryMessage(message: Uint8Array): Uint8Array {
return keccak_256(
Uint8Array.from([
...utf8.decode('\x19Ethereum Signed Message:\n'),
...utf8.decode(message.length.toString()),
...message,
])
);
}
/**
* Recovers and returns the secp256k1 public key of the signer given the arbitrary
* `message` and `signature` that was signed using EIP191.
*/
export function recoverPubKeyFromEthSignature(
message: Uint8Array,
signature: Uint8Array
): Uint8Array {
if (signature.length !== 65) {
throw new Error('Invalid signature');
}
const r = signature.slice(0, 32);
const s = signature.slice(32, 64);
const v = signature[64];
// Adapted from https://github.com/ethers-io/ethers.js/blob/6017d3d39a4d428793bddae33d82fd814cacd878/src.ts/crypto/signature.ts#L255-L265
const yParity = v <= 1 ? v : (v + 1) % 2;
const secpSignature = secp256k1.Signature.fromCompact(
Uint8Array.from([...r, ...s])
).addRecoveryBit(yParity);
const digest = hashEthArbitraryMessage(message);
return secpSignature.recoverPublicKey(digest).toRawBytes(true);
}