Skip to content

Commit

Permalink
Enable ECDSA signing & verification.
Browse files Browse the repository at this point in the history
  • Loading branch information
nyusternie committed Aug 19, 2024
1 parent 79d79d8 commit a2beb9b
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 47 deletions.
90 changes: 80 additions & 10 deletions lib/test/unit/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,16 @@ import { expect } from 'chai'

import CryptoJS from 'crypto-js'

import { encodeAddress } from '@nexajs/address'

import {
encodeDataPush,
OP,
} from '@nexajs/script'

import {
binToHex,
hexToBin
hexToBin,
} from '@nexajs/utils'

/* Import (individual) classes. */
Expand All @@ -29,8 +36,10 @@ import {
ripemd160,
sha256,
sha512,
signMessageHashEcdsa,
signMessageHashSchnorr,
validateSecp256k1PrivateKey,
verifyMessageHashEcdsa,
verifyMessageHashSchnorr,
} from '../../index.js'

Expand All @@ -43,13 +52,22 @@ import {
} from '../test_vectors.js'

/* Initialize globals. */
let address
let hashbuf
let isValid
let msgbuf
let prefix
let privateKey
let privateKeyBin
let publicKey
let result
let response
let signature

describe( 'Crypto (Unit) Test Suite', () => {
before( () => {
console.info( ` ↳ targeting all (non-mutating) JavaScript methods provided by the 'Crypto' class.` )

prefix = 'nexa'
} )

describe( 'Crypto -> Point (Class)', () => {
Expand Down Expand Up @@ -254,11 +272,28 @@ describe( 'Crypto (Unit) Test Suite', () => {

} )

describe( 'Crypto -> signMessageHashEcdsa', () => {
it( 'should generate a Base64 ECDSA signature', async () => {
privateKeyBin = sha256(NEXA_TEST_SECRET, 'binary')

const signingSerialization = sha256(NEXA_TEST_BODY, 'binary')

const msgbuf = sha256(sha256(signingSerialization))

const signatureB64 = signMessageHashEcdsa(privateKeyBin, msgbuf)
// console.log('SIGNATURE (base64)', signatureB64)

// expect(signatureB64).to.equal('ILaaLaj8SY0aihKkCjgvo8E88n30CZ8DkuDggecwZfMFcVzBQHxK+kmBf5az+OfyFlz/qd1quLB+J4rfeLPt48s=')
expect(signatureB64.length).to.equal(88)
} )
} )

describe( 'Crypto -> signMessageHashSchnorr', () => {
it( 'should generate a 64-byte signature', async () => {
const privateKeyBin = sha256(NEXA_TEST_SECRET, 'binary')
it( 'should generate a 64-byte SCHNORR signature', async () => {
privateKeyBin = sha256(NEXA_TEST_SECRET, 'binary')

const signingSerialization = sha256(NEXA_TEST_BODY, 'binary')

const sighash = sha256(sha256(signingSerialization))

// Generate a signature over the "sighash" using the passed private key.
Expand All @@ -271,18 +306,53 @@ describe( 'Crypto (Unit) Test Suite', () => {
describe( 'Crypto -> validateSecp256k1PrivateKey', () => {
it( 'should validate the private on the Secp256k1 curve', async () => {
// Encode Private Key WIF.
result = validateSecp256k1PrivateKey(hexToBin(PRIVATE_KEY))
expect(result).to.be.true
response = validateSecp256k1PrivateKey(hexToBin(PRIVATE_KEY))
expect(response).to.be.true
} )
} )

describe( 'Crypto -> verifyMessageHashEcdsa', () => {
it( 'should veryify an ECDSA data payload using a address, sig + msg', () => {
privateKeyBin = sha256(NEXA_TEST_SECRET, 'binary')
publicKey = derivePublicKeyCompressed(privateKeyBin)

const scriptData = encodeDataPush(publicKey)
const publicKeyHash = ripemd160(sha256(scriptData))
const scriptPubKey = new Uint8Array([
OP.ZERO,
OP.ONE,
...encodeDataPush(publicKeyHash),
])
address = encodeAddress(
prefix,
'TEMPLATE',
scriptPubKey,
)
// console.info('SIG ADDRESS', address)

signature = 'H9lselbAsiwzifmu0Es541xkbkn/TP6Y2EQeACf46WUzDZJLlikzMv5MC3VcjeffluK7htz8/POqo+nVCB4O3BA='

const signingSerialization = sha256(NEXA_TEST_BODY, 'binary')
msgbuf = sha256(sha256(signingSerialization))

// FIXME: Remove the dependency for an "Address Encoder" ASAP!!
response = verifyMessageHashEcdsa(address, signature, msgbuf, encodeAddress)
// console.log('SIG RESPONSE', response)

isValid = response.isValid
// console.log('IS VALID', isValid)

expect(isValid).to.equal(true)
} )
} )

describe( 'Crypto -> verifyMessageHashSchnorr', () => {
it( 'should veryify a (Schnorr) data payload using a sig, msg + pubkey', () => {
const hashbuf = hexToBin('b8743a1bcfc800d33cea8e89d1d687c646dfcc7a98172baab19c8f45921ae64a')
it( 'should veryify a SCHNORR data payload using a sig, msg + pubkey', () => {
hashbuf = hexToBin('b8743a1bcfc800d33cea8e89d1d687c646dfcc7a98172baab19c8f45921ae64a')

const privkey = hexToBin('eaf130c173f06c6bb3dfc76cc9e64051136a4280d6791ab85d20938c1a63137f')
privateKey = hexToBin('eaf130c173f06c6bb3dfc76cc9e64051136a4280d6791ab85d20938c1a63137f')

const isValid = verifyMessageHashSchnorr(privkey, hashbuf)
const isValid = verifyMessageHashSchnorr(privateKey, hashbuf)
console.log('IS VALID', isValid)

expect(isValid).to.equal(true)
Expand Down
3 changes: 2 additions & 1 deletion packages/Crypto/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nexajs/crypto",
"version": "24.7.20",
"version": "24.8.15",
"description": "Suite of useful cryptographic functions for decentralized applications.",
"main": "index.js",
"repository": "https://github.com/avasdao/nexajs",
Expand All @@ -9,6 +9,7 @@
"type": "module",
"dependencies": {
"@ethersproject/random": "5.7.0",
"@nexajs/script": "24.7.15",
"@nexajs/utils": "24.7.15",
"bn.js": "5.2.1",
"bs58": "6.0.0",
Expand Down
1 change: 1 addition & 0 deletions packages/Crypto/src/signMessageHashEcdsa.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export default (_privkey, _msgbuf) => {
ecdsa.pubkey = privkey.toPublicKey()

/* Sign. */
// FIXME Is there an option to make this deterministic??
ecdsa.signRandomK()

/* Calculate. */
Expand Down
82 changes: 46 additions & 36 deletions packages/Crypto/src/verifyMessageHashEcdsa.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
/* Import modules. */
import {
encodeDataPush,
OP,
} from '@nexajs/script'

import {
binToHex,
hexToBin,
utf8ToBin,
} from '@nexajs/utils'
// import { encodeDataPush } from '@nexajs/script'

import Signature from '../libs/Signature.js'

Expand All @@ -13,9 +18,9 @@ import {
sha256,
} from '../index.js'

export default (_address, _signature, _msgbuf) => {
export default (_address, _signature, _msgbuf, _addressEncoder) => {
let prefix
let result
let response
let signatureAddress

prefix = 'nexa'
Expand Down Expand Up @@ -53,45 +58,50 @@ export default (_address, _signature, _msgbuf) => {
ecdsa.pubkey = publicKey
// console.log('PUBLIC KEY', publicKey.toString())

// FIXME

// /* Hash the public key hash according to the P2PKH/P2PKT scheme. */
// scriptData = encodeDataPush(hexToBin(publicKey.toString()))
//
// publicKeyHash = ripemd160(sha256(scriptData))
//
// scriptPubKey = new Uint8Array([
// OP.ZERO,
// OP.ONE,
// ...encodeDataPush(publicKeyHash),
// ])
// // console.info('\n Script Public Key:', binToHex(scriptPubKey))
//
// /* Encode the public key hash into a P2PKH nexa address. */
// signatureAddress = encodeAddress(
// prefix,
// 'TEMPLATE',
// scriptPubKey,
// )
// console.info('SIG ADDRESS', signatureAddress)

// FIXME

// check that the recovered address and specified address match
// if (_address !== signatureAddress.toString()) {
// throw new Error('The signature did not match the message digest')
// // return false
// }
/* Hash the public key hash according to the P2PKH/P2PKT scheme. */
const scriptData = encodeDataPush(hexToBin(publicKey.toString()))

const publicKeyHash = ripemd160(sha256(scriptData))

const scriptPubKey = new Uint8Array([
OP.ZERO,
OP.ONE,
...encodeDataPush(publicKeyHash),
])
// console.info('\n Script Public Key:', binToHex(scriptPubKey))

if (typeof _addressEncoder === 'undefined' || !_addressEncoder) {
throw new Error('TEMP FIX: Please add an ADDRESS ENCODER as a (4th) parameter `verifyMessageHashEcdsa(p1, p2, p3, ADDRESS_ENCODER)`.')
}

/* Encode the public key hash into a P2PKH nexa address. */
signatureAddress = _addressEncoder(
prefix,
'TEMPLATE',
scriptPubKey,
)
// console.info('SIG ADDRESS (recovered)', signatureAddress)

/* Verify recovered address and specified address match. */
if (_address !== signatureAddress) {
// throw new Error('The signature did not match the message digest')
console.error('The signature DOES NOT match the message digest.')

return {
publicKey: hexToBin(publicKey.toString()),
isValid: false,
}
}

result = ecdsa.verify(hash, signature, publicKey)
// console.log('RESULT', result)
response = ecdsa.verify(hash, signature, publicKey)
// console.log('RESPONSE', response)

if (!result) {
if (!response) {
throw new Error('The signature was invalid')
}

return {
publicKey: hexToBin(publicKey.toString()),
isValid: result.verified
isValid: response.verified,
}
}
7 changes: 7 additions & 0 deletions packages/Crypto/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/logger" "^5.7.0"

"@nexajs/script@^24.7.15":
version "24.7.15"
resolved "https://registry.yarnpkg.com/@nexajs/script/-/script-24.7.15.tgz#2af82c392805c8a1e9794d77d4e0b962f8b8daed"
integrity sha512-mhGqfT7EAQMfxTx4v6FgPKzCQvo/Thy5XMXxREXJ5I8LccVKce/OiWVllWs73BXUd8BbTVrERuhTwj1EMnNPqw==
dependencies:
"@nexajs/utils" "24.7.15"

"@nexajs/utils@24.7.15":
version "24.7.15"
resolved "https://registry.yarnpkg.com/@nexajs/utils/-/utils-24.7.15.tgz#499bf20e1643e7c7bad91529a3f30c80512b6c5a"
Expand Down

0 comments on commit a2beb9b

Please sign in to comment.