Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
a6956b0
feat: enhance wideBytes method for unbiased scalar generation and upd…
zy0n Oct 16, 2025
6324d4b
feat: refactor BabyFrostVSSDKG to replace mulPointEscalar with Scalar…
zy0n Oct 16, 2025
a1abd22
feat: implement RFC9591Hasher for improved hashing functions and refa…
zy0n Oct 17, 2025
727233f
refactor: use test-vector inputs from trusted-dkg
zy0n Oct 17, 2025
78602d0
refactor: rename lagrangeBasisAtZero to deriveInterpolatingValue for …
zy0n Oct 17, 2025
e0dfca9
feat: add inverse modulo order function and enhance BabyFrostVSSDKG w…
zy0n Oct 18, 2025
5c55cec
feat: add end-to-end tests for TrustedDKG and extensions with compreh…
zy0n Oct 18, 2025
b1b9db7
feat: implement FROSTSigningManager with signing session management a…
zy0n Oct 18, 2025
ee91b5a
feat: enhance FROSTSigningManager to support threshold-based signing …
zy0n Oct 18, 2025
f19f5f0
feat: add DKGManager class with trusted key generation and correspond…
zy0n Oct 20, 2025
91c3139
feat: enhance TrustedDKG with AES-GCM encryption for shares and updat…
zy0n Oct 20, 2025
e48cf82
feat: add @noble/curves dependency and update DKGManager for share en…
zy0n Oct 21, 2025
7cee1fd
feat: enhance DKGManager with share encryption and participant manage…
zy0n Oct 21, 2025
5a31cd4
refactor: clean up DKGManager test by removing commented-out code and…
zy0n Oct 21, 2025
3dc69d6
feat: implement commitment tracking and retrieval in DKGManager
zy0n Oct 21, 2025
b8813b7
refactor: remove comments
zy0n Oct 21, 2025
037b315
refactor: update DKGManager to use Uint8Array for keys and shares
zy0n Oct 22, 2025
27acdbe
feat: implement DKG flow state management and enhance error handling …
zy0n Oct 22, 2025
4bdddc6
feat: add coordinator-less end-to-end tests for TrustedDKG and extens…
zy0n Oct 22, 2025
845fee3
feat: add detailed test cases for TrustedDKG and remove deprecated te…
zy0n Oct 22, 2025
df811d0
feat: refactor DKG and FROST imports by removing BabyFrostVSSDKG and …
zy0n Oct 22, 2025
db55851
recap old
zy0n Oct 28, 2025
519aeac
refactor: standardize LE encoding
zy0n Oct 28, 2025
f0a27d1
refactor: add preventative checks
zy0n Oct 28, 2025
f20cdb3
refactor: update serialization methods to use packPoint and unpackPoi…
zy0n Oct 29, 2025
dc39229
replace pointpacking from BIP62 compatibility to RFC8032
rdubois-crypto Oct 29, 2025
9c35a61
Merge pull request #4 from Railgun-Community/Reno/8032compliant
zy0n Oct 29, 2025
102dc60
lint
zy0n Oct 29, 2025
71f3416
refactor: improve nonce handling in TrustedDKG and update exports in …
zy0n Oct 31, 2025
3110c4b
refactor: clean up exports and add default export for module
zy0n Oct 31, 2025
bc2b47a
refactor: add bufferToBigInt and bigIntToBuffer exports in index
zy0n Oct 31, 2025
8f2d81b
refactor: update exports in package.json and improve buffer handling …
zy0n Nov 1, 2025
3591ad3
refactor: update import paths to include file extensions and improve …
zy0n Nov 3, 2025
81b6de6
refactor: update version in package.json, enhance .gitignore, and add…
zy0n Nov 7, 2025
87aa435
refactor: update package version to 0.2.8, add pack script, and modif…
zy0n Nov 7, 2025
ac328b5
refactor: update package version to 0.2.9 and enhance DKGManager with…
zy0n Nov 7, 2025
40766c1
refactor: replace leBigIntToBuffer and leBufferToBigInt with hexToByt…
zy0n Nov 7, 2025
2ba9123
refactor: remove unnecessary blank line in babyposeidon.ts
zy0n Nov 7, 2025
9e2f15d
refactor: remove commented-out packPublicKey and unpackPublicKey impo…
zy0n Nov 7, 2025
ab15b37
refactor: remove unused import and update comment for secretComKey in…
zy0n Nov 7, 2025
7327286
refactor: update TODO comments for modularization in hashing and clar…
zy0n Nov 7, 2025
0d0fdf2
refactor: remove commented-out exports in index.ts for cleaner code
zy0n Nov 7, 2025
668f3e7
refactor: add missing newline at end of file in pack.sh and update DK…
zy0n Nov 7, 2025
f5cbfaf
refactor: add missing newline at end of .gitignore for consistency
zy0n Nov 7, 2025
b9b8ee2
refactor: remove trailing whitespace in signing.ts for consistency
zy0n Nov 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ protected-main.json
# misc
dist
.vscode
coverage
coverage

local-distribution
113 changes: 113 additions & 0 deletions docs/dkg-manager.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# DKGManager

A small orchestration helper around the FROST DKG primitives that supports two flows:

- Trusted-dealer keygen: one dealer produces all shares and the group public key.
- Coordinator-less multi-dealer: many dealers each contribute a polynomial; participants encrypt/decrypt shares and finalize their own share.

This README covers the public API, step-by-step usage, and common errors. The code lives in `src/manager/dkg.ts`.

## Public API (stable)

- `new DKGManager()`
- `getAnnouncement(): { pubKey: Uint8Array }` — publish your X25519 public key for share encryption.
- `assignRoster(roster: Record<number, Uint8Array>): void` — map of participantId → X25519 public key. Your own key must be present.
- `runTrustedKeygen(secret: bigint, n: number, t: number)` → `{ shares, groupPublicKey, viewingPrivateKey }`
- `commitmentRound(secret: bigint, n: number, t: number)` → `{ shares: Record<number, bigint>, commitments: Point[] }`
- `addParticipantCommitments(dealerId: number, commitments: Point[]): void`
- `getEncryptedShares()` → `Record<number, { nonce: Uint8Array, ciphertext: Uint8Array }>`
- `addEncryptedShares(dealerId: number, shares: Record<number, EncryptedShare>): void`
- `finalize()` → `{ share: { id, skShare, skShareDiv8 }, PKGroup, viewingPrivateKey }`

Notes
- Participant IDs are 1..N positive integers.
- Shares and commitments must be provided for every dealer in the roster before finalizing.

## Flow 1: Trusted-dealer keygen

When a single dealer generates all shares and the group key.

```ts
import DKGManager from '../src/manager/dkg'

const dkg = new DKGManager()
const secret = 0x43583e33fb2f47faa243b5cdf8cb251f7e9482f0386064901ae0c5e2134b78fn
const n = 5
const t = 3

const { groupPublicKey, shares, viewingPrivateKey } = dkg.runTrustedKeygen(secret, n, t)
// shares: [{ identifier, skShare, skShareDiv8 }]
// groupPublicKey: [xHex, yHex]
```

Validation
- `n > 0`, `t > 0`, `t <= n`.

## Flow 2: Coordinator-less multi-dealer

Every dealer contributes a polynomial. Each participant obtains an encrypted share from every dealer, decrypts locally, and finalizes their own share.

High level steps per dealer:

1. Announce and roster
```ts
const dealer = new DKGManager()
const announce = dealer.getAnnouncement() // { pubKey }
// Build a roster across all dealers
const roster: Record<number, Uint8Array> = { 1: pub1, 2: pub2, 3: pub3, 4: pub4, 5: pub5 }
dealer.assignRoster(roster) // assigns dealer.participantID implicitly based on its own pubKey
```

2. Commitment round (per dealer)
```ts
const { shares, commitments } = dealer.commitmentRound(secret_i, n, t)
// Broadcast `commitments` to everyone; keep `shares` to encrypt next
```

3. Collect commitments
```ts
for (const [dealerId, comms] of Object.entries(allCommitments)) {
dealer.addParticipantCommitments(Number(dealerId), comms)
}
```

4. Encrypt and distribute shares (per dealer)
```ts
const encryptedByRecipient = dealer.getEncryptedShares()
// Send encryptedByRecipient[participantId] to each participant
```

5. Collect encrypted shares (per participant)
```ts
for (const [dealerId, encBundle] of Object.entries(collectedEncrypted)) {
dealer.addEncryptedShares(Number(dealerId), encBundle)
}
```

6. Finalize local share (per participant)
```ts
const { share, PKGroup, viewingPrivateKey } = dealer.finalize()
// share.id === dealer.participantID
```

Validation and ordering
- `assignRoster` must run before `commitmentRound`.
- Each `addParticipantCommitments` must be called for all dealers listed in roster (same ID set).
- `getEncryptedShares` requires a complete commitment set and local shares from `commitmentRound`.
- `addEncryptedShares` must be called for all dealers in roster.
- `finalize` throws if any dealers are missing.

## Error messages and causes

- `roster not assigned` — call `assignRoster` first.
- `our announcement pubKey not present in roster` — include the current manager's pubKey in roster.
- `roster size does not match desiredShares` — ensure `n` equals roster size.
- `missing commitments for one or more dealers` — collect all dealer commitments before encrypting or finalizing.
- `missing encrypted shares or keys from dealers: ...` — not all encrypted shares were collected.
- `threshold cannot exceed desiredShares` — fix `t` vs `n`.

## Tips

- Participant IDs are 1..N consecutively.
- The `viewingPrivateKey` is derived deterministically from the set of dealers (C0 commitments) and is identical across participants.
- The returned `PKGroup` is the sum of the first commitments across dealers.
73 changes: 73 additions & 0 deletions docs/signing-manager.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# FROSTSigningManager

Orchestrates the 2-round FROST signing flow on top of `BabyFROST` primitives, given a finalized group public key and per-participant shares.

This README documents the public API, a minimal E2E example, and helper methods for readiness and diagnostics. The code lives in `src/manager/signing.ts`.

## Public API (stable)

- `new FROSTSigningManager(groupPublicKey: Point, threshold: number)`
- `addSigner({ id: number, skShare: bigint }): void` — add a local signer/share owned by this manager instance.
- `round1(): void` — generate commitments for local signers and reset round state.
- `exportRound1(): Commitment[]` — commitments for local signers to share with others.
- `addRemoteSigner(commitment: Commitment): void` — add a commitment from another participant (id is unique).
- `sign(msgHash: bigint): { identifier: number, partial: bigint }[]` — produce signature shares for all local signers, bound to the combined commitment list.
- `recievePartials(partials: { identifier, partial }[]): void` — collect partials from others.
- `finalize(msgHash: bigint)` → aggregated signature (tuple `[R8x, R8y, s]` from `BabyFROST`).

Helper methods
- `expectedParticipantIds(): number[]` — identifiers implied by the current commitment list.
- `getMissingPartials(): number[]` — identifiers without collected partials.
- `readyToFinalize(): boolean` — true when at least `threshold` partials are available among expected ids.
- `resetRoundState(): void` — clears commitments and partials.

## Minimal E2E usage (t-of-n)

```ts
import FROSTSigningManager from '../src/manager/signing'
import { eddsaBuild } from '../src' // provides verifyPoseidon
import { bigIntToBuffer } from '@zk-kit/utils'

const t = 3
const groupPublicKey = /* Point<bigint> from DKG */

// choose any t participants that have finalized DKG shares
const signers: FROSTSigningManager[] = []
for (const { id, skShare } of subsetShares) {
const sm = new FROSTSigningManager(groupPublicKey, t)
sm.addSigner({ id, skShare })
signers.push(sm)
}

// round 1: produce + exchange commitments
for (const sm of signers) {
sm.round1()
const local = sm.exportRound1()
for (const c of local) {
for (const peer of signers) if (!peer.hasId(c.identifier)) peer.addRemoteSigner(c)
}
}

// round 2: sign and exchange partials
const msg = 42069n
const partialsFromAll: { identifier: number, partial: bigint }[][] = []
for (const sm of signers) partialsFromAll.push(sm.sign(msg))
for (const sm of signers) for (const batch of partialsFromAll) sm.recievePartials(batch)

// finalize and verify
const sig = signers[0].finalize(msg)
const ok = eddsaBuild.verifyPoseidon(bigIntToBuffer(msg), sig, groupPublicKey)
```

## Validation and errors

- `getCommitmentList` sorts commitments by identifier and enforces `list.length >= threshold`.
- `sign` and `finalize` throw when commitments are missing or inconsistent.
- `finalize` verifies local signature shares before aggregation and throws if any fail verification.
- Use `readyToFinalize` and `getMissingPartials` to monitor progress.

## Tips

- Keep one manager instance per participating device/process. Each instance can manage one or more local shares if needed.
- Always call `round1()` before `exportRound1()` and `sign()`.
- Identifiers must be positive integers and must match identifiers assigned during DKG.
25 changes: 25 additions & 0 deletions pack.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash

# default to patch version bump
VERSION_TYPE="${1:-patch}"
OUT_DIR="${2:-./local-distribution}"

# bump version in package.json
npm version $VERSION_TYPE --no-git-tag-version

# pack the package
yarn pack

# get package name and version
PACKAGE_NAME=$(node -p "require('./package.json').name.replace('@', '').replace('/', '-')")
PACKAGE_VERSION=$(node -p "require('./package.json').version")
OUT_NAME="${PACKAGE_NAME}-v${PACKAGE_VERSION}.tgz"

# move the tarball to specified directory
if [ "$OUT_DIR" != "./" ]; then
mkdir -p "$OUT_DIR"
mv "$OUT_NAME" "$OUT_DIR/"
echo "Package moved to: $OUT_DIR/$OUT_NAME"
else
echo "Package created: $OUT_NAME"
fi
28 changes: 15 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@railgun-reloaded/curves-lite",
"version": "0.0.1",
"version": "0.2.9",
"description": "Dependency light version of railgun curves functionality.",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
Expand All @@ -11,20 +11,20 @@
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js"
},
"./frost/rfc9591": {
"types": "./dist/types/frost/rfc9591.d.ts",
"import": "./dist/esm/frost/rfc9591.js",
"require": "./dist/cjs/frost/rfc9591.js"
"./babyfrost": {
"types": "./dist/types/frost/babyfrost.d.ts",
"import": "./dist/esm/frost/babyfrost.js",
"require": "./dist/cjs/frost/babyfrost.js"
},
"./frost/vss-dkg": {
"types": "./dist/types/frost/vss-dkg.d.ts",
"import": "./dist/esm/frost/vss-dkg.js",
"require": "./dist/cjs/frost/vss-dkg.js"
"./trusted-dkg": {
"types": "./dist/types/frost/trusted-dkg.d.ts",
"import": "./dist/esm/frost/trusted-dkg.js",
"require": "./dist/cjs/frost/trusted-dkg.js"
},
"./vss": {
"types": "./dist/types/frost/vss-dkg.d.ts",
"import": "./dist/esm/frost/vss-dkg.js",
"require": "./dist/cjs/frost/vss-dkg.js"
"./manager": {
"types": "./dist/types/manager/index.d.ts",
"import": "./dist/esm/manager/index.js",
"require": "./dist/cjs/manager/index.js"
}
},
"scripts": {
Expand All @@ -38,6 +38,7 @@
"pretest": "npm run build",
"test": "node --import tsx --test --test-reporter=spec \"./test/**/*.test.ts\"",
"coverage": "c8 --clean --reporter=text --reporter=lcov --include='src/**/*.ts' --exclude='src/**/*.test.ts' --exclude='**/dist/**' node --import tsx --test --test-reporter=spec \"./test/**/*.test.ts\"",
"pack:dev": "./pack.sh",
"prepack": "npm run build"
},
"repository": {
Expand All @@ -63,6 +64,7 @@
},
"dependencies": {
"@noble/ciphers": "^2.0.1",
"@noble/curves": "^2.0.1",
"@noble/ed25519": "^3.0.0",
"@noble/hashes": "^2.0.1",
"@zk-kit/baby-jubjub": "^1.0.3",
Expand Down
Loading