From 9b5de61a2f2cded1626755776f4f37cb165d8140 Mon Sep 17 00:00:00 2001 From: nixer89 Date: Thu, 25 Sep 2025 10:40:34 +0200 Subject: [PATCH] v.3.2.3: add support for secp256k1 keys --- lib/fetchUnl.mjs | 58 ++++++++++++++++++++++++++++++++++++++++-------- package.json | 2 +- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/lib/fetchUnl.mjs b/lib/fetchUnl.mjs index 1c6b0b8..e7108ac 100644 --- a/lib/fetchUnl.mjs +++ b/lib/fetchUnl.mjs @@ -5,6 +5,8 @@ import elliptic from 'elliptic' const ed25519 = elliptic.eddsa('ed25519') +const secp256k1 = elliptic.ec('secp256k1') +import { createHash } from 'crypto'; import fetch from 'node-fetch' import address from 'ripple-address-codec' @@ -97,12 +99,36 @@ const fetchUnl = (url, master_public_key) => { const manifest = parse_manifest(Buffer.from(json.manifest, 'base64')) // verify manifest signature and payload signature - const master_key = ed25519.keyFromPublic(master_public_key.slice(2), 'hex') - assert(master_key.verify(manifest.without_signing_fields, manifest.MasterSignature), - "Master signature in master manifest does not match vl key") - let signing_key = ed25519.keyFromPublic(manifest.SigningPubKey.slice(2), 'hex') - assert(signing_key.verify(blob.toString('hex'), json.signature), - "Payload signature in mantifest failed verification") + // distinguish between ed25519 and secp256k1 + let master_key = null; + if(master_public_key.startsWith('ED')) { + master_key = ed25519.keyFromPublic(master_public_key.slice(2), 'hex'); + assert(master_key.verify(manifest.without_signing_fields, manifest.MasterSignature), + "Master signature in master manifest does not match ed25519 vl key") + } else { + master_key = secp256k1.keyFromPublic(master_public_key, 'hex'); + assert(master_key.verify(manifest.without_signing_fields, manifest.MasterSignature), + "Master signature in master manifest does not match secp256k1 vl key"); + } + + let signing_key = null; + + if(manifest.SigningPubKey.toUpperCase().startsWith('ED')) { + signing_key = ed25519.keyFromPublic(manifest.SigningPubKey.slice(2), 'hex') + assert(signing_key.verify(blob.toString('hex'), json.signature), + "Payload signature in mantifest failed verification with ed25519") + } else { + signing_key = secp256k1.keyFromPublic(manifest.SigningPubKey.toString('hex'), 'hex'); + + //sha512 half the blob! + //https://xrpl.org/cryptographic-keys.html#key-derivation + let sha512Blob = createHash("sha512").update(blob); + let sha512HalfBuffer = sha512Blob.digest().slice(0,32); + + assert(signing_key.verify(sha512HalfBuffer, json.signature), + "Payload signature in mantifest failed verification with SECP256K1") + } + blob = JSON.parse(blob) assert(blob.validators !== undefined, "validators missing from blob") @@ -118,10 +144,24 @@ const fetchUnl = (url, master_public_key) => { let manifest = parse_manifest(Buffer.from(blob.validators[idx].manifest, 'base64')) // verify signature - signing_key = ed25519.keyFromPublic(blob.validators[idx].validation_public_key.slice(2), 'hex') - assert(signing_key.verify(manifest.without_signing_fields, manifest.MasterSignature), - "Validation manifest " + idx + " signature verification failed") + let publicKey = blob.validators[idx].validation_public_key; + + if (publicKey.slice(0, 1) === 'n') { + const publicKeyBuffer = codec.address.decodeNodePublic(publicKey); + publicKey = publicKeyBuffer.toString("hex").toUpperCase(); + } + + if(publicKey.toUpperCase().startsWith('ED')) { + signing_key = ed25519.keyFromPublic(publicKey.slice(2), 'hex') + + assert(signing_key.verify(manifest.without_signing_fields, manifest.MasterSignature), + "Validation manifest " + idx + " signature verification failed with ed25519"); + } else { + signing_key = secp256k1.keyFromPublic(publicKey, 'hex'); + const computedHash = createHash("sha512").update(manifest.without_signing_fields).digest().toString("hex").slice(0, 32); + assert(signing_key.verify(computedHash, manifest.MasterSignature), "Validation manifest " + idx + " signature verification failed with secp256k1"); + } blob.validators[idx].validation_public_key = Buffer.from(blob.validators[idx].validation_public_key, 'hex') blob.validators[idx].manifest = manifest diff --git a/package.json b/package.json index 33392a4..f19ef97 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xpopgen", - "version": "3.2.2", + "version": "3.2.3", "description": "Store XRPL Proof of Validation messages", "main": "index.mjs", "scripts": {