diff --git a/docs/dkg-manager.md b/docs/dkg-manager.md index 5bf033e..62835d6 100644 --- a/docs/dkg-manager.md +++ b/docs/dkg-manager.md @@ -28,7 +28,7 @@ Notes When a single dealer generates all shares and the group key. ```ts -import DKGManager from '../src/manager/dkg' +import { DKGManager } from '../src/manager/dkg' const dkg = new DKGManager() const secret = 0x43583e33fb2f47faa243b5cdf8cb251f7e9482f0386064901ae0c5e2134b78fn diff --git a/docs/signing-manager.md b/docs/signing-manager.md index 9e90b30..2ff4aba 100644 --- a/docs/signing-manager.md +++ b/docs/signing-manager.md @@ -24,7 +24,7 @@ Helper methods ## Minimal E2E usage (t-of-n) ```ts -import FROSTSigningManager from '../src/manager/signing' +import { FROSTSigningManager } from '../src/manager/signing' import { eddsaBuild } from '../src' // provides verifyPoseidon import { bigIntToBuffer } from '@zk-kit/utils' diff --git a/package.json b/package.json index 204ed37..1d13929 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@railgun-reloaded/curves-lite", - "version": "0.2.9", + "version": "0.2.14", "description": "Dependency light version of railgun curves functionality.", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", diff --git a/src/eddsa/babyposeidon.ts b/src/eddsa/babyposeidon.ts index e0ab3e2..81fbfe7 100644 --- a/src/eddsa/babyposeidon.ts +++ b/src/eddsa/babyposeidon.ts @@ -38,11 +38,11 @@ class EddsaPoseidon extends RailJubCurvePoint { // misc helpers packPoint (point: Point): Uint8Array { const result = packPublicKey(point) - return hexToBytes(result.toString(16)) + return hexToBytes(result.toString(16).padStart(64, '0')) } unpackPoint (bytesIn: Uint8Array): Point | null { - const formatted = BigInt('0x' + bytesToHex(bytesIn)) + const formatted = BigInt('0x' + bytesToHex(bytesIn).padStart(64, '0')) const result = unpackPublicKey(formatted) return result } @@ -50,6 +50,5 @@ class EddsaPoseidon extends RailJubCurvePoint { const eddsaBuild = new EddsaPoseidon() -export type { Signature } -export { EddsaPoseidon } -export default eddsaBuild +export type { Signature, SignatureHex } +export { EddsaPoseidon, eddsaBuild } diff --git a/src/eddsa/index.ts b/src/eddsa/index.ts index 3200811..9478a8c 100644 --- a/src/eddsa/index.ts +++ b/src/eddsa/index.ts @@ -1,4 +1,3 @@ -import eddsaBuild from './babyposeidon.js' +import { EddsaPoseidon, eddsaBuild } from './babyposeidon.js' -export { EddsaPoseidon } from './babyposeidon.js' -export { eddsaBuild } +export { EddsaPoseidon, eddsaBuild } diff --git a/src/frost/babyfrost.ts b/src/frost/babyfrost.ts index 63c92bf..56e51cb 100644 --- a/src/frost/babyfrost.ts +++ b/src/frost/babyfrost.ts @@ -4,7 +4,7 @@ import type { Point } from '@zk-kit/baby-jubjub' import { addPoint } from '@zk-kit/baby-jubjub' import { RailJubCurvePoint } from '../curve.js' -import RFC9591Hasher from '../hashing.js' +import { RFC9591Hasher } from '../hashing.js' import type { BindingFactor, Bindings, Commitment, NoncePair } from './types.js' @@ -212,7 +212,7 @@ class BabyFROST extends RailJubCurvePoint { } const frost = new BabyFROST() -export { frost } +export { frost, BabyFROST } // export type { LocalNonces, CommitmentPublic, PartialSignature, AggregateSignature } export default BabyFROST diff --git a/src/frost/index.ts b/src/frost/index.ts index d4053d3..1f9624a 100644 --- a/src/frost/index.ts +++ b/src/frost/index.ts @@ -1,4 +1,4 @@ -import BabyFROST, { frost } from './babyfrost.js' -import TrustedDKG from './trusted-dkg.js' +import { BabyFROST, frost } from './babyfrost.js' +import { TrustedDKG } from './trusted-dkg.js' export { BabyFROST, frost, TrustedDKG } diff --git a/src/frost/trusted-dkg.ts b/src/frost/trusted-dkg.ts index d84782c..bb1f54b 100644 --- a/src/frost/trusted-dkg.ts +++ b/src/frost/trusted-dkg.ts @@ -7,7 +7,7 @@ import type { Point } from '@zk-kit/baby-jubjub' import { addPoint, mulPointEscalar } from '@zk-kit/baby-jubjub' import { RailJubCurvePoint } from '../curve.js' -import RFC9591Hasher from '../hashing.js' +import { RFC9591Hasher } from '../hashing.js' import type { EncryptedShare } from './types.js' @@ -324,4 +324,4 @@ class TrustedDKG extends RailJubCurvePoint { } } -export default TrustedDKG +export { TrustedDKG } diff --git a/src/hashing.ts b/src/hashing.ts index db3d891..75541b4 100644 --- a/src/hashing.ts +++ b/src/hashing.ts @@ -5,7 +5,7 @@ import type { Point } from '@zk-kit/baby-jubjub' import { leBigIntToBuffer, leBufferToBigInt } from '@zk-kit/utils' import { poseidon5 } from 'poseidon-lite' -type HashFn = (m: Uint8Array) => Uint8Array +type HashFn = (m: Uint8Array) => Uint8Array // TODO: to modularize this hasher, we can add more hash functions here function blake2BWrapper (m: Uint8Array) { return blake2b(m, { dkLen: 64 }) @@ -78,4 +78,5 @@ class RFC9591Hasher { } } -export default RFC9591Hasher +export type { HashFn } +export { RFC9591Hasher } diff --git a/src/index.ts b/src/index.ts index 6d280b7..587cdc4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,98 +1,4 @@ -/* eslint-disable jsdoc/require-jsdoc */ +import * as pkg from './module-index.js' -import { getPublicKey } from '@noble/ed25519' -import type { Point } from '@zk-kit/baby-jubjub' -import { bigIntToBuffer, bufferToBigInt, leBigIntToBuffer, leBufferToBigInt } from '@zk-kit/utils' -import msgpack from 'msgpack-lite' - -import type { Signature } from './eddsa/babyposeidon.js' -import { EddsaPoseidon, eddsaBuild } from './eddsa/index.js' -import { BabyFROST, frost } from './frost/index.js' -import { DKGManager, FROSTSigningManager } from './manager/index.js' -import { poseidonFn } from './poseidon/poseidon-lite-wrapper.js' - -function poseidon (inputs: Uint8Array[]) { - const result = poseidonFn(inputs.map(a => a.reverse())) as bigint - return bigIntToBuffer(result).toReversed() -} - -function poseidonHex (inputs: string[], outputHex = false) { - // TODO: sanitize inputs 32 bytes - const result = poseidon(inputs.map(BigInt).map(bigIntToBuffer)) - const output = bufferToBigInt(result) - return outputHex ? output.toString(16) : output -} - -function getPublicSpendingKey (privateKey: Uint8Array): [bigint, bigint] { - if (privateKey.length !== 32) throw Error('Invalid private key length') - return eddsaBuild.prv2pub(Buffer.from(privateKey)) -} - -async function getPublicViewingKey (privateViewingKey: Uint8Array): Promise { - return getPublicKey(privateViewingKey) -} - -function signEDDSA (privateKey: Uint8Array, message: bigint): Signature { - // convert bigint to uint8array - const u8 = bigIntToBuffer(message) - return eddsaBuild.signPoseidon(Buffer.from(privateKey), u8) -} - -function verifyEDDSA (message: bigint, signature: Signature, pubkey: [bigint, bigint]) { - // convert bigint to uint8array - const u8 = bigIntToBuffer(message) - return eddsaBuild.verifyPoseidon(u8, signature, pubkey) -} - -function getShareableViewingKey (spendingPublicKey: Point, viewingPrivateKey: Uint8Array) { - const spendingPublicKeyString = eddsaBuild.fromBytes(eddsaBuild.packPoint(spendingPublicKey)).toString(16).padStart(64, '0') - const data = { - vpriv: eddsaBuild.fromBytes(viewingPrivateKey.toReversed()).toString(16), - spub: spendingPublicKeyString, - } - return msgpack.encode(data).toString('hex') -} - -export type * from './frost/types.js' - -export type { Point } - -export { - frost, - bufferToBigInt, - bigIntToBuffer, - leBigIntToBuffer, - leBufferToBigInt, - BabyFROST, - FROSTSigningManager, - DKGManager, - eddsaBuild, - EddsaPoseidon, - getPublicSpendingKey, - getPublicViewingKey, - getShareableViewingKey, - signEDDSA, - verifyEDDSA, - poseidon, - poseidonHex -} - -export default { - bufferToBigInt, - bigIntToBuffer, - leBigIntToBuffer, - leBufferToBigInt, - frost, - BabyFROST, - FROSTSigningManager, - DKGManager, - eddsaBuild, - EddsaPoseidon, - getPublicSpendingKey, - getPublicViewingKey, - getShareableViewingKey, - signEDDSA, - verifyEDDSA, - poseidon, - poseidonHex, -} +export * from './module-index.js' +export default pkg diff --git a/src/manager/dkg.ts b/src/manager/dkg.ts index 358e9fa..14de467 100644 --- a/src/manager/dkg.ts +++ b/src/manager/dkg.ts @@ -6,7 +6,7 @@ import { randomBytes } from '@noble/hashes/utils.js' import type { Point } from '@zk-kit/baby-jubjub' import { bufferToBigInt } from '@zk-kit/utils' -import TrustedDKG from '../frost/trusted-dkg.js' +import { TrustedDKG } from '../frost/trusted-dkg.js' import type { EncryptedShare } from '../frost/types.js' // Lightweight internal state to prevent wrong-order usage and surface clear errors @@ -275,4 +275,4 @@ class DKGManager { } } -export default DKGManager +export { DKGManager, DKGFlowState } diff --git a/src/manager/index.ts b/src/manager/index.ts index d4396a6..a998a5e 100644 --- a/src/manager/index.ts +++ b/src/manager/index.ts @@ -1,3 +1,2 @@ -import DKGManager from './dkg.js' -import FROSTSigningManager from './signing.js' -export { FROSTSigningManager, DKGManager } +export * from './dkg.js' +export * from './signing.js' diff --git a/src/manager/signing.ts b/src/manager/signing.ts index f25489a..4cf7e07 100644 --- a/src/manager/signing.ts +++ b/src/manager/signing.ts @@ -13,7 +13,6 @@ type SigningSession = { remoteSigners: Commitment[], partials: bigint[] } - /* eslint-disable jsdoc/require-jsdoc */ class FROSTSigningManager { frost: BabyFROST @@ -192,4 +191,5 @@ class FROSTSigningManager { } } -export default FROSTSigningManager +export type { SignerShare, SessionBinding, PartialSignature, SigningSession } +export { FROSTSigningManager } diff --git a/src/module-index.ts b/src/module-index.ts new file mode 100644 index 0000000..8058d10 --- /dev/null +++ b/src/module-index.ts @@ -0,0 +1,117 @@ +/* eslint-disable jsdoc/require-jsdoc */ + +import { getPublicKey } from '@noble/ed25519' +import type { Point } from '@zk-kit/baby-jubjub' +import { bigIntToBuffer, bufferToBigInt, leBigIntToBuffer, leBufferToBigInt } from '@zk-kit/utils' +import msgpack from 'msgpack-lite' + +import type { Signature } from './eddsa/babyposeidon.js' +import { EddsaPoseidon, eddsaBuild } from './eddsa/index.js' +import { BabyFROST, frost } from './frost/index.js' +import { DKGManager, FROSTSigningManager } from './manager/index.js' +import { poseidonFn } from './poseidon/poseidon-lite-wrapper.js' + +function poseidon (inputs: Uint8Array[]) { + const result = poseidonFn(inputs.map(a => a.reverse())) as bigint + return bigIntToBuffer(result).toReversed() +} + +function poseidonHex (inputs: string[], outputHex = false) { + // TODO: sanitize inputs 32 bytes + const result = poseidon(inputs.map(BigInt).map(bigIntToBuffer)) + const output = bufferToBigInt(result) + return outputHex ? output.toString(16) : output +} + +function getPublicSpendingKey (privateKey: Uint8Array): [bigint, bigint] { + if (privateKey.length !== 32) throw Error('Invalid private key length') + return eddsaBuild.prv2pub(Buffer.from(privateKey)) +} + +async function getPublicViewingKey (privateViewingKey: Uint8Array): Promise { + return getPublicKey(privateViewingKey) +} + +function signEDDSA (privateKey: Uint8Array, message: bigint): Signature { + // convert bigint to uint8array + const u8 = bigIntToBuffer(message) + return eddsaBuild.signPoseidon(Buffer.from(privateKey), u8) +} + +function verifyEDDSA (message: bigint, signature: Signature, pubkey: [bigint, bigint]) { + // convert bigint to uint8array + const u8 = bigIntToBuffer(message) + return eddsaBuild.verifyPoseidon(u8, signature, pubkey) +} + +function getShareableViewingKey (spendingPublicKey: Point, viewingPrivateKey: Uint8Array) { + const spendingPublicKeyString = eddsaBuild.fromBytes(eddsaBuild.packPoint(spendingPublicKey)).toString(16).padStart(64, '0') + const data = { + vpriv: eddsaBuild.fromBytes(viewingPrivateKey.toReversed()).toString(16), + spub: spendingPublicKeyString, + } + return msgpack.encode(data).toString('hex') +} + +function decodeShareableKey (shareableKey: string) { + const buf = Buffer.from(shareableKey, 'hex') + return msgpack.decode(buf) +} + +function decodeMultisigKey (multisigShareableKey: string) { + const decoded = decodeShareableKey(multisigShareableKey) + + const spub = eddsaBuild.toBytes(BigInt('0x' + decoded.spub)) + + // const vpriv = eddsaBuild.toBytes(BigInt('0x' + decoded.vpriv)) + + const reformatted = { + ...decoded, + vpriv: new Uint8Array(Buffer.from(decoded.vpriv, 'hex')), + spub: eddsaBuild.unpackPoint(spub), + sk: '0x' + decoded.sk, + } + return reformatted +} + +function getShareableMultisigKey (spendingPublicKey: Point, viewingPrivateKey: Uint8Array, sessionID: string, symmetricKey: string, participantId: number) { + const spendingPublicKeyString = eddsaBuild.fromBytes(eddsaBuild.packPoint(spendingPublicKey)).toString(16).padStart(64, '0') + // const sessionIdHex = Buffer.from(sessionID, 'utf-8').toString('hex') + const formattedSymmetricKey = symmetricKey.startsWith('0x') ? symmetricKey.slice(2) : symmetricKey + // const formattedParticipantId = BigInt(participantId).toString(16) + const data = { + vpriv: eddsaBuild.fromBytes(viewingPrivateKey.toReversed()).toString(16), + spub: spendingPublicKeyString, + sid: sessionID, + sk: formattedSymmetricKey, + pid: participantId + } + return msgpack.encode(data).toString('hex') +} + +export type * from './frost/types.js' + +export type { Point } + +export { + bufferToBigInt, + bigIntToBuffer, + leBigIntToBuffer, + leBufferToBigInt, + frost, + BabyFROST, + FROSTSigningManager, + DKGManager, + eddsaBuild, + EddsaPoseidon, + getPublicSpendingKey, + getPublicViewingKey, + getShareableViewingKey, + getShareableMultisigKey, + decodeShareableKey, + decodeMultisigKey, + signEDDSA, + verifyEDDSA, + poseidon, + poseidonHex, +} diff --git a/test/dkg-manager.test.ts b/test/dkg-manager.test.ts index 1b8b5eb..9250a44 100644 --- a/test/dkg-manager.test.ts +++ b/test/dkg-manager.test.ts @@ -2,8 +2,8 @@ import assert from 'node:assert' import { describe, it } from 'node:test' -import DKGManager from '../src/manager/dkg' -import FROSTSigningManager from '../src/manager/signing' +import { DKGManager } from '../src/manager/dkg' +import { FROSTSigningManager } from '../src/manager/signing' import { eddsaBuild, type Point } from '../src' import { bigIntToBuffer } from '@zk-kit/utils' diff --git a/test/eddsa-poseidon.test.ts b/test/eddsa-poseidon.test.ts index 8bff484..abaefa9 100644 --- a/test/eddsa-poseidon.test.ts +++ b/test/eddsa-poseidon.test.ts @@ -5,7 +5,7 @@ import { before, describe, it } from 'node:test' import { randomBytes } from '@noble/hashes/utils.js' import { eddsaBuild } from '../src/eddsa' -import { getShareableViewingKey } from '../src/index' +import { decodeMultisigKey, decodeShareableKey, getShareableMultisigKey, getShareableViewingKey } from '../src/index' function randomKey (): Uint8Array { const k = randomBytes(32) @@ -48,6 +48,8 @@ describe('EddsaPoseidon (production tests)', () => { 176, 149, 143, 139, 194, 134, 174, 8, 50, 250, 131, 176, 27, 113, 154, 34, 90, 7, 206, 123, 134, 31, 243, 17, 50, 63, 34, 22, 103, 179, 189, 80, ]) + + // const privateKey = new Uint8Array(Buffer.from('efc6f552f6797c74aa967c7583fc4680c3dd5df64618b6fdc25eeef045a34c03', 'hex')) const pubkey = [ 15684838006997671713939066069845237677934334329285343229142447933587909549584n, 11878614856120328179849762231924033298788609151532558727282528569229552954628n, @@ -60,6 +62,25 @@ describe('EddsaPoseidon (production tests)', () => { const expectedShareable = '82a57670726976d94062303935386638626332383661653038333266613833623031623731396132323561303763653762383631666633313133323366323231363637623362643530a473707562d94030346164336335393738653064646561373030613336393932313861333461616336663736303437646661656131323966643530313464636338306534333961' const shareable = getShareableViewingKey(pubkey, privateKey) + const sessionId = 'giraffe-exclude-tree-cross' + const symmetricKey = '0xce4b39db21f9311e84b73e6f3a66b2ff1e98c62c642a8ec7ea34ceb9210725ff' + const participantId = 1 + + const multisigInfo = { + vpriv: privateKey, + spub: pubkey, + sid: sessionId, + sk: symmetricKey, + pid: participantId + } + const multisig = getShareableMultisigKey(pubkey, privateKey, sessionId, symmetricKey, participantId) + const decoded = decodeShareableKey(multisig) + const decodedMultisig = decodeMultisigKey(multisig) + console.log('multisig', multisig) + console.log('matchdecoded', multisigInfo) + console.log('decodedMultisig', decodedMultisig) + console.log('decoded', decoded) + assert.equal(shareable, expectedShareable) }) diff --git a/test/hashes.test.ts b/test/hashes.test.ts index dd5761e..62e1382 100644 --- a/test/hashes.test.ts +++ b/test/hashes.test.ts @@ -3,7 +3,7 @@ import assert from 'node:assert' import { describe, it } from 'node:test' import { bytesToHex, hexToBytes, randomBytes } from '@noble/hashes/utils.js' -import RFC9591Hasher from '../src/hashing'; +import { RFC9591Hasher } from '../src/hashing'; import { subOrder } from '@zk-kit/baby-jubjub'; function toHex(buffer: Uint8Array) { diff --git a/test/signing-manager.test.ts b/test/signing-manager.test.ts index 49ddb9d..febd8b6 100644 --- a/test/signing-manager.test.ts +++ b/test/signing-manager.test.ts @@ -3,7 +3,7 @@ import assert from 'node:assert' import { describe, it } from 'node:test' -import FROSTSigningManager from '../src/manager/signing' +import { FROSTSigningManager } from '../src/manager/signing' import { eddsaBuild } from '../src' function mod (x: bigint) { diff --git a/test/trusted-dkg-e2e.test.ts b/test/trusted-dkg-e2e.test.ts index aa6f46e..7ba56cf 100644 --- a/test/trusted-dkg-e2e.test.ts +++ b/test/trusted-dkg-e2e.test.ts @@ -4,8 +4,8 @@ import { describe, it } from 'node:test' import type { Point } from '@zk-kit/baby-jubjub' -import TrustedDKG from '../src/frost/trusted-dkg' -import BabyFROST from '../src/frost/babyfrost' +import { TrustedDKG, BabyFROST } from '../src/frost' +// import { BabyFROST } from '../src/frost/babyfrost' import { eddsaBuild } from '../src/eddsa' import type { Commitment } from '../src/frost/types' import { poseidonHex } from '../src/index'