ripple-keypairs
Version:
Cryptographic key pairs for the XRP Ledger
75 lines (66 loc) • 2.26 kB
text/typescript
import { numberToBytesBE } from '@noble/curves/utils.js'
import { secp256k1 as nobleSecp256k1 } from '@noble/curves/secp256k1.js'
import { bytesToHex, hexToBytes } from '@xrplf/isomorphic/utils'
import type {
DeriveKeyPairOptions,
HexString,
SigningScheme,
} from '../../types'
import { derivePrivateKey } from './utils'
import assert from '../../utils/assert'
import Sha512 from '../../utils/Sha512'
const SECP256K1_PREFIX = '00'
const secp256k1: SigningScheme = {
deriveKeypair(
entropy: Uint8Array,
options?: DeriveKeyPairOptions,
): {
privateKey: string
publicKey: string
} {
const derived = derivePrivateKey(entropy, options)
const privateKey =
SECP256K1_PREFIX + bytesToHex(numberToBytesBE(derived, 32))
const publicKey = bytesToHex(
nobleSecp256k1.getPublicKey(numberToBytesBE(derived, 32), true),
)
return { privateKey, publicKey }
},
sign(message: Uint8Array, privateKey: HexString): string {
// Some callers pass the privateKey with the prefix, others without.
// @noble/curves will throw if the key is not exactly 32 bytes, so we
// normalize it before passing to the sign method.
assert.ok(
(privateKey.length === 66 && privateKey.startsWith(SECP256K1_PREFIX)) ||
privateKey.length === 64,
)
const normedPrivateKey =
privateKey.length === 66 ? privateKey.slice(2) : privateKey
return bytesToHex(
nobleSecp256k1.sign(Sha512.half(message), hexToBytes(normedPrivateKey), {
// "Canonical" signatures
lowS: true,
// Would fail tests if signatures aren't deterministic
extraEntropy: undefined,
format: 'der',
// We pass a pre-hashed message (Sha512Half), so disable secp256k1's
// default SHA-256 prehashing (added as default in @noble/curves 2.0.0)
prehash: false,
}),
).toUpperCase()
},
verify(
message: Uint8Array,
signature: HexString,
publicKey: HexString,
): boolean {
const decoded = nobleSecp256k1.Signature.fromHex(signature, 'der')
return nobleSecp256k1.verify(
decoded.toBytes('compact'),
Sha512.half(message),
hexToBytes(publicKey),
{ prehash: false },
)
},
}
export default secp256k1