Infyn Development · Technical Reference · Rust

Infyn® Crypto Library

Complete documentation of the hybrid cryptographic library — ML-DSA-87 (FIPS 204) plus P-521 and Ed448, quantum-secure, Ethereum-compatible, one Rust crate for native nodes, the Substrate runtime and the browser wallet.

3
Signature Schemes
NIST L5
PQ Security (FIPS 204)
4.873 B
Composite Signature
0
unsafe Blocks (forbidden)
01 · Overview

What is Infyn Crypto?

A hybrid cryptographic library that combines one post-quantum and two classical signature schemes into a single composite signature — Defense in Depth for the post-quantum era, implemented in Rust with #![forbid(unsafe_code)].

ML-DSA-87

Post-quantum signature scheme based on Module-LWE/SIS lattice problems, standardised as FIPS 204 (successor of CRYSTALS-Dilithium5). NIST Level 5 (≈ AES-256). Public key 2,592 B, signature 4,627 B. The secret key is stored as its 32-byte seed and re-expanded on demand. Crate: ml-dsa.

Post-Quantum

P-521 ECDSA

NIST P-521 (FIPS 186-4) with ~256-bit classical security. Uncompressed SEC1 public key 133 B, fixed-width signature r ‖ s = 132 B. No recovery ID — the composite public key travels inside the transaction. Crate: p521.

Classical

Ed448

Edwards curve Ed448 per RFC 8032 with 224-bit security. Deterministic signatures, public key 57 B, signature 114 B. Key generation and signing via ed448-rust, verification via ed448-goldilocks — validated against the RFC 8032 test vectors.

Classical
Defense in Depth: An attacker would have to break all three schemes at once — ML-DSA-87 (Module-LWE/SIS) plus P-521 and Ed448 (ECDLP on two independent curves). A cryptographically relevant quantum computer breaks the two ECC schemes via Shor; security then rests on ML-DSA-87 alone. Conversely, the ECC layer protects against a yet-unknown weakness in the lattice scheme. Verification only succeeds if all three partial signatures are valid.
NIST L5 PQ ML-DSA-87 P-521 Ed448
02 · System Architecture

Crate Organisation

One Rust crate (blackchain-crypto-lib-rust, edition 2024, MSRV 1.85) with clear module boundaries. Consensus-critical dependencies are exact-pinned so that signature validity can never drift with a patch release.

src/crypto.rs — Composite Keys
Composite key types, address derivation, message signing
BlackChainPublicKeyBlackChainPrivateKeyderive_address
Holds the composite key types. BlackChainPrivateKey::generate(seed) derives all three key pairs from a 64-byte seed, derive_address() computes the Keccak-256 address, sign_message() / verify_message() implement the off-chain message protocol. Secret material is wrapped in Zeroizing and cleared on drop; Debug output redacts keys.
src/sign.rs — Unified Traits
Scheme, PublicKey, PrivateKey, TypedScheme traits
SchemeTypedSchemeSignatureOpts
Defines the common traits for all signature schemes: Scheme (object-safe: generate_key, derive_key, sign, verify, public_key_size, signature_size, seed_size) and TypedScheme (typed keys). Private-key equality must be constant-time. Trait methods may panic on wrong key types — the typed API in crypto.rs and api.rs is the safe entry point.
src/dilithium/ — Post-Quantum
ML-DSA-87 (FIPS 204) via the ml-dsa crate
MlDsa87FIPS 204Seed 32 B
Wraps the RustCrypto ml-dsa crate (ML-DSA-87). The module path dilithium is kept for historical reasons — the algorithm is ML-DSA-87 per FIPS 204, not Dilithium Round 3. Keys are derived deterministically from a 32-byte seed ξ (new_key_from_seed); the private key is stored and serialised as this seed. Constants: PUBLIC_KEY_SIZE = 2592, SIGNATURE_SIZE = 4627, PRIVATE_KEY_SIZE = 32.
src/mdecc/ — Multi-Dimensional ECC
p521.rs and ed448.rs implementations
P-521Ed448Curve-ID 1 / 2
Two elliptic-curve schemes behind the common traits. p521.rs: ECDSA on NIST P-521 (RustCrypto p521), fixed-width 132-byte signatures, on-curve validation on parse. ed448.rs: RFC 8032 Ed448, keygen/sign with ed448-rust, verify with ed448-goldilocks. Each curve has a domain-separation ID (P-521 = 1, Ed448 = 2) used both in seed derivation and in the signed message.
src/hdwallet/ — HD Wallet
bip39.rs (24 words only), derivation.rs (hardened-only BIP-32)
BIP-39BIP-32Hardened-Only
bip39.rs generates 24-word mnemonics from 256 bits of OS entropy and turns them into a 64-byte root seed (Zeroizing). 12-word phrases are rejected by policy. derivation.rs implements the hardened-only BIP-32 child derivation, the path parser ("m/44'/60'/0'/0'/0'") and the per-curve curve seed derivation (SHAKE256 + HKDF-SHA3-512).
src/transaction/ — Transactions
types.rs, rlp.rs, signing.rs — typed transaction 0x80
BlackChainTxTypeRLPrecover_sender
types.rs defines BlackChainTxType (EIP-1559 fee fields plus pqc_signature and pub_key, type byte 0x80). rlp.rs implements canonical encoding and decoding with alloy-rlp. signing.rs contains signature_hash(), sign_transaction() and recover_sender() including the chain-bound entanglement protocol.
src/api.rs · src/wasm.rs — Facades
High-level developer API and browser bindings (feature "browser")
create_walletsign_transactionBlackWallet (JS)
api.rs is the five-call developer surface (create_wallet, restore_wallet, sign_message, verify_message, sign_transaction, verify_transaction). wasm.rs exposes the same functionality to JavaScript via wasm-bindgen (BlackWallet, verifyMessage, recoverSender, validateMnemonic) — compiled only for wasm32 with the browser feature, so the Substrate runtime build stays free of browser dependencies.
Module Dependency Graph
Module diagram — crypto.rs and transaction/ build on the scheme modules; api.rs and wasm.rs are thin facades on top.

Data Flow: Sign Transaction

1

Key Derivation

The 64-byte account seed is split in two halves: seed[0..32] → ML-DSA-87 ξ, seed[32..64] → curve master. P-521 and Ed448 seeds are expanded from the curve master via SHAKE256 (curve ID) + HKDF-SHA3-512.

2

Transaction Hash

The unsigned transaction is RLP-encoded, prefixed with the type byte and hashed: H = Keccak-256(0x80 ‖ RLP(chain_id, nonce, fees, gas, to, value, data))

3

Entanglement (chain-bound)

Nonce and combined hash bind the signature to chain, address and key set: entg_nonce = SHAKE256("entangle" ‖ ALGO_ID ‖ VERSION ‖ chain_id₈ ‖ address)[0..16], then h_combined = SHAKE256(pk_dil ‖ pk_p521 ‖ pk_ed448 ‖ entg_nonce)[0..32]

4

Three Signatures

ML-DSA-87 signs the raw hash H. P-521 signs H ‖ h_combined ‖ 0x01, Ed448 signs H ‖ h_combined ‖ 0x02. The curve ID prevents cross-curve reuse; h_combined links the ECC signatures to all three public keys.

5

Composite Signature

Concatenation: pqc_signature = σ_dil ‖ σ_p521 ‖ σ_ed448 → exactly 4,873 bytes. The composite public key (2,782 B) is embedded as pub_key; v = chain_id·2 + 35, r = s = 0 remain as EIP-155 framing placeholders and are checked on recovery.

03 · Hierarchical Deterministic Wallet

HD Wallet System

BIP-39/32/44 compatible wallet with one 24-word mnemonic for all three schemes — hardened-only derivation, 256-bit master secrets for both the post-quantum and the ECC layer.

Key Derivation

Mnemonic
24 words (256 bit)
→
PBKDF2-SHA512
2048 rounds
→
Root Seed
64 bytes
→
BIP-32 (hardened)
m/44'/60'/0'/0'/i'
→
Account Seed Split
2 × 32 bytes
Seed Splittingaccount_seed = priv(path) ‖ chain_code(path) // 64 bytes from hardened BIP-32 leaf

seed[0..32] → ML-DSA-87 seed ξ (256 bit → FIPS 204 KeyGen_internal)
seed[32..64] → curve master (256 bit → per-curve expansion)

Per-curve expansion (curve_id: P-521 = 1, Ed448 = 2):
shaken_i = SHAKE256(curve_master ‖ curve_id_i)[0..32]
seed_i = HKDF-SHA3-512(IKM = shaken_i, salt = ∅, info = ∅)[0..n_i] // P-521: 66 B, Ed448: 57 B

Address:
addr = Keccak-256(pk_dil ‖ pk_p521 ‖ pk_ed448)[12..32] // 20 bytes, AccountId20 / EIP-55

Derivation Path (BIP-44, Hardened-Only)

m / 44' / 60' / 0' / 0' / 0'
↑ ↑ ↑ ↑ ↑
BIP-44 ETH Account Change Index

Every index must be hardened (≥ 0x80000000); bare integers in a
path string are hardened automatically, non-hardened indices are rejected.
Hardened-only because ML-DSA-87 and Ed448 do not support public
child-key derivation — and it removes the parent-key exposure attack.

Coin type 60 = Ethereum (SLIP-44) — for address compatibility.
Account i of the browser wallet: m/44'/60'/0'/0'/i'
wallet_example.rs
use blackchain_crypto_lib_rust::api::{create_wallet, restore_wallet, sign_message, verify_message}; // Create wallet: fresh 24-word mnemonic + account key at m/44'/60'/0'/0'/0' let (mnemonic, key) = create_wallet("")?; // "" = no BIP-39 passphrase let address = key.public_key().derive_address(); // 0x… (20 bytes, EIP-55) // Sign and verify a message (off-chain protocol, zeroed nonce) let sig = sign_message(b"Hello, Infyn!", &key)?; // 4,873 bytes let ok = verify_message(b"Hello, Infyn!", &sig, key.public_key())?; // Restore later from the backed-up mnemonic let same = restore_wallet(&mnemonic, "")?; assert_eq!(same.public_key().derive_address(), address);
Key Derivation Tree — Mnemonic to 3 Key Pairs
Visualisation: the 64-byte account seed is split into two 256-bit halves. The curve half is expanded per curve via SHAKE256 (curve ID) + HKDF-SHA3-512.
04 · Signature System

Hybrid Signature Linking

The entanglement mechanism prevents signature reuse by cryptographically binding all three schemes — Chain Binding, Address Binding, Public Key Linking, Curve Separation.

Final test phase — 512-bit pre-hash. Before genesis, the transaction hash moves from Keccak-256 to a 512-bit pre-hash (SHA3-512) over the canonical transcript, signed as HashML-DSA per FIPS 204 §5.4 with the same binding nonce for P-521 and Ed448. This lifts the transcript from collision class 2¹²⁸ to 2²⁵⁶ (category 5 end to end); the old hash path is retired by a versioned switch-off rule. Keccak-256 remains for transaction ID and address — it identifies, it does not authorize.
EntanglementStep 1: Transaction hash
H = Keccak-256(0x80 ‖ RLP(unsigned tx)) // 32 bytes

Step 2: Entanglement nonce (chain-bound)
entg_nonce = SHAKE256("entangle" ‖ ALGO_ID(1) ‖ VERSION(1) ‖ chain_id_be₈ ‖ address₂₀)[0..16]

Step 3: Combined hash
h_combined = SHAKE256(pk_dil ‖ pk_p521 ‖ pk_ed448 ‖ entg_nonce)[0..32]

Step 4: Three signatures
σ_dil = ML-DSA-87.Sign(sk_dil, H)
σ_p521 = ECDSA-P521.Sign(sk_p521, H ‖ h_combined ‖ 0x01)
σ_ed448 = Ed448.Sign(sk_ed448, H ‖ h_combined ‖ 0x02)

Step 5: Composite signature
pqc_signature = σ_dil[4627] ‖ σ_p521[132] ‖ σ_ed448[114]
|pqc_signature| = 4,873 bytes (fixed — any other length is rejected)

Message signing (off-chain) uses the same construction with a zeroed 16-byte nonce.

Signature Structure

Security Mechanisms

Chain & Address Binding

The entanglement nonce contains the 8-byte chain ID and the sender address. A signature made for one network is invalid on every other network, and signatures of one address cannot be reused for another — replay across chains and accounts is rejected by construction.

Public Key Linking

h_combined binds all three public keys. Sub-signatures and sub-keys cannot be spliced across key sets. Note: σ_dil signs H directly — binding to chain, address and key set is enforced by the two ECC signatures and by the verifier recomputing the address from pub_key.

Curve Separation

Each ECC signature covers its curve ID (P-521 = 1, Ed448 = 2). Cross-curve signature reuse is prevented; the same IDs domain-separate the seed derivation.

Signature Entanglement — Cryptographic Linking
The three schemes are linked via the combined hash (SHAKE256). Every signature is bound to chain ID, sender address and all public keys.
05 · Transaction System

Ethereum-compatible Transactions

EIP-1559 fee fields, EIP-2718 typed envelope (type 0x80), canonical RLP via alloy-rlp for consensus and serde-JSON for APIs — extended with the composite post-quantum signature and the embedded public key.

Transaction Lifecycle

1
Create
Fill TX fields
2
Hash
Keccak(0x80‖RLP)
3
Entangle
SHAKE256 · chain-bound
4
Sign
3× Sign ~17.8 ms
5
Encode
RLP / JSON
6
Broadcast
~7.8 KB TX
7
Recover
3× Verify ~3.5 ms
8
Finalized
On-Chain
FieldTypeDescription
chain_idu64EIP-155 chain identifier — also part of the entanglement nonce
nonceu64Account transaction sequence number
max_priority_fee_per_gasU256Priority fee per gas unit (EIP-1559)
max_fee_per_gasU256Maximum fee per gas unit (EIP-1559)
gas_limitu64Gas limit
toOption<Address>Recipient address (None for contract creation)
valueU256Transfer amount in Wei
dataBytesContract call data / bytecode
vOption<U256>EIP-155 framing placeholder: chain_id · 2 + 35 — not secp256k1
r, sOption<U256>Always 0 — secp256k1 is not used; recovery rejects other values
pqc_signatureOption<Bytes>Composite signature ML-DSA-87 ‖ P-521 ‖ Ed448 (4,873 bytes)
pub_keyOption<Bytes>Composite public key (2,782 bytes) — enables sender recovery without a key registry

RLP layout: [chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value, data] unsigned; signed transactions append [v, r, s, pqc_signature, pub_key]. Type byte 0x80 is prepended to the envelope. Protocol constants: VERSION = 1, ALGO_ID = 1.

transaction_example.rs
use blackchain_crypto_lib_rust::transaction::types::BlackChainTxType; use alloy_primitives::{address, Bytes, U256}; // Create and sign a transaction let mut tx = BlackChainTxType { chain_id: 8888, // Infyn EVM chain ID nonce: 0, max_priority_fee_per_gas: U256::from(2_000_000_000u64), // 2 Gwei max_fee_per_gas: U256::from(30_000_000_000u64), // 30 Gwei gas_limit: 21_000, to: Some(address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")), value: U256::from(1_000_000_000_000_000_000u128), // 1 INFYN (18 decimals) data: Bytes::new(), v: None, r: None, s: None, pqc_signature: None, pub_key: None, }; tx.sign_transaction(&key)?; // fills v, r, s, pqc_signature, pub_key // Sender recovery (= full verification of all three signatures) let sender = tx.recover_sender()?; assert_eq!(sender, key.public_key().derive_address()); // Canonical RLP for the wire / JSON for RPC let mut raw = vec![BlackChainTxType::TX_TYPE]; // 0x80 alloy_rlp::Encodable::encode(&tx, &mut raw);
06 · Technical Specifications

Key & Signature Sizes

All sizes are fixed and enforced by the parsers — a composite key or signature of any other length is rejected before any cryptographic operation runs.

Composite Public Key: 2,782 Bytes

ML-DSA-87
2.592 B
P-521
133 B
Ed448
57 B

Composite Signature: 4,873 Bytes

ML-DSA-87
4.627 B
P-521
132 B
Ed448
114 B
2.782
Public Key (bytes)
155
Private Key (bytes, 32 + 66 + 57)
4.873
Signature (bytes)
20
Address (bytes)
~7.8 KB
TX Size (transfer)
64
Seed (bytes, 32 + 32)

Private key: the ML-DSA-87 secret is stored as its 32-byte seed and re-expanded to the 4,896-byte FIPS 204 signing key on demand; P-521 scalar 66 B, Ed448 seed 57 B. All secret buffers are zeroized on drop.

AlgorithmSecurity LevelQuantum ResistantBasis / Standard
ML-DSA-87NIST Level 5 (≈ AES-256)✓ YesModule-LWE / SIS · FIPS 204
P-521 ECDSA~256-bit (curve)✗ NoECDLP (Weierstraß) · FIPS 186-4
Ed448224-bit (curve)✗ NoECDLP (Edwards, Curve448) · RFC 8032
CombinedNIST L5 PQ + 224-bit classical (min. of the ECC layer)✓ YesHybrid Defense-in-Depth

Both seed halves are 256 bit, so the ECC layer is no longer capped by the seed; the classical bound is set by the curves themselves (Ed448: 224 bit).

Size Comparison: Infyn vs. Bitcoin vs. Ethereum

Infyn
Public Key2.782 B
Signature4.873 B
TX Size~7.8 KB
PQ Secure✓ NIST L5
Schemes3 hybrid
Bitcoin
Public Key33 B
Signature64 B
TX Size~250 B
PQ Secure✗ No
Schemes1 (secp256k1)
Ethereum
Public Key33 B
Signature65 B
TX Size~110 B
PQ Secure✗ No
Schemes1 (secp256k1)
Trade-off: An Infyn transfer (~7.8 KB: 4,873 B signature + 2,782 B public key + ~110 B payload) is ~70× larger than an Ethereum transaction (~110 B) and ~30× larger than a Bitcoin transaction (~250 B). This overhead is the price for post-quantum security and triple cryptographic protection; it bounds L1 throughput mainly via bandwidth (see whitepaper, section 05).
07 · Performance

Operation Times

Measured with the crate's criterion benchmark suite (benches/latency.rs) in release mode on Intel Xeon 2.8 GHz (x86-64) (single core). Numbers are medians; your hardware will differ.

Generate mnemonic
1.7 µs
Mnemonic → root seed (PBKDF2)
1.7 ms
BIP-32 path derivation (5 levels)
0.40 ms
Hybrid keygen from seed (3 schemes)
6.9 ms
Transaction hash (Keccak + RLP)
0.8 µs
Sign transaction (composite)
17.8 ms
Recover sender (verify all three)
3.5 ms
Derive address (Keccak-256)
10.8 µs
Build requirements: Rust 1.85+ (edition 2024), Linux/macOS/Windows, x86-64 or arm64; wasm32-unknown-unknown target plus wasm-pack for the browser build. No unsafe code, no C dependencies. ~7.8 KB storage per transaction.
08 · API Reference

Interface Documentation

Core functions of the main modules — api, crypto, transaction, hdwallet, dilithium, mdecc and the JavaScript bindings.

api create_wallet / restore_wallet / sign_* / verify_*

▾
pub const DEFAULT_ACCOUNT_PATH: &str = "m/44'/60'/0'/0'/0'";

pub fn create_wallet(passphrase: &str) -> Result<(String, BlackChainPrivateKey), CryptoError>
// fresh 24-word mnemonic + account key (show mnemonic once, then store encrypted)

pub fn restore_wallet(mnemonic: &str, passphrase: &str) -> Result<BlackChainPrivateKey, CryptoError>

pub fn sign_message(message: &[u8], key: &BlackChainPrivateKey) -> Result<Vec<u8>, CryptoError>
pub fn verify_message(message: &[u8], signature: &[u8], pub_key: &BlackChainPublicKey) -> Result<bool, CryptoError>

pub fn sign_transaction(tx: &mut BlackChainTxType, key: &BlackChainPrivateKey) -> Result<(), CryptoError>
pub fn verify_transaction(tx: &BlackChainTxType) -> Result<Address, CryptoError>
// = tx.recover_sender()

crypto BlackChainPrivateKey / BlackChainPublicKey

▾
impl BlackChainPrivateKey {
pub fn generate(seed: &[u8]) -> Result<(Self, BlackChainPublicKey), CryptoError> // seed ≥ 64 bytes
pub fn public_key(&self) -> &BlackChainPublicKey
pub fn derive_public_key(&self) -> Result<BlackChainPublicKey, CryptoError>
pub fn sign_message(&self, message: &[u8]) -> Result<Vec<u8>, CryptoError> // 4,873 bytes
pub fn dilithium_bytes / p521_bytes / ed448_bytes / seed(&self) -> &[u8]
}
impl BlackChainPublicKey {
pub fn to_bytes(&self) -> Vec<u8> // 2,782 bytes: dil ‖ p521 ‖ ed448
pub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> // exact length + on-curve checks
pub fn derive_address(&self) -> Address // Keccak-256(pk)[12..32]
pub fn verify_message(&self, message: &[u8], signature: &[u8]) -> Result<bool, CryptoError>
}
pub const COMPOSITE_PK_SIZE: usize = 2782; pub const ALGO_ID: u8 = 1; pub const VERSION: u8 = 1;

transaction BlackChainTxType

▾
impl BlackChainTxType {
pub const TX_TYPE: u8 = 0x80;
pub fn signature_hash(&self) -> [u8; 32] // Keccak-256(0x80 ‖ RLP(unsigned))
pub fn sign_transaction(&mut self, key: &BlackChainPrivateKey) -> Result<(), CryptoError>
pub fn recover_sender(&self) -> Result<Address, CryptoError> // verifies all three sub-signatures
}
impl Encodable / Decodable for BlackChainTxType // alloy-rlp, canonical
impl Serialize / Deserialize for BlackChainTxType // serde JSON (RPC)

pub const COMPOSITE_SIG_SIZE: usize = 4873; // 4627 + 132 + 114

hdwallet bip39 / derivation

▾
// bip39.rs — 24 words only (ENTROPY_BYTES = 32, MNEMONIC_WORD_COUNT = 24)
pub fn generate_mnemonic() -> Result<String, CryptoError>
pub fn seed_from_mnemonic(phrase: &str, password: &str) -> Result<Zeroizing<Vec<u8>>, CryptoError> // 64 bytes
pub fn validate_mnemonic(phrase: &str) -> bool

// derivation.rs
pub const CURVE_ID_P521: u8 = 1; pub const CURVE_ID_ED448: u8 = 2;
pub fn derive_child_seed(master_seed: &[u8], path: &[u32]) -> Result<Zeroizing<Vec<u8>>, CryptoError>
// hardened-only; returns priv(32) ‖ chain_code(32)
pub fn parse_hardened_path(s: &str) -> Result<Vec<u32>, CryptoError> // "m/44'/60'/0'/0'/0'" or "44/60/0/0/0"
pub fn derive_mdecc_curve_seed(mdecc_seed: &[u8], curve_id: u8, seed_size: usize) -> Result<Vec<u8>, CryptoError>

dilithium · mdecc Scheme Modules

▾
// dilithium/mod.rs — ML-DSA-87 (FIPS 204), historical module name
pub const SEED_SIZE: usize = 32; PUBLIC_KEY_SIZE = 2592; PRIVATE_KEY_SIZE = 32 (seed form); SIGNATURE_SIZE = 4627;
pub fn new_key_from_seed(seed: &[u8; 32]) -> (PublicKey, PrivateKey)
pub fn sign_to(sk: &PrivateKey, msg: &[u8], sig: &mut [u8])
pub fn verify(pk: &PublicKey, msg: &[u8], signature: &[u8]) -> bool

// mdecc/p521.rs — ECDSA P-521
SEED_SIZE = 66; PRIVATE_KEY_SIZE = 66; PUBLIC_KEY_SIZE = 133 (SEC1 uncompressed); SIGNATURE_SIZE = 132 (r ‖ s)
P521PrivateKey::sign_msg(&self, msg) -> Vec<u8> P521PublicKey::verify_sig(&self, msg, sig) -> Result<(), CryptoError>

// mdecc/ed448.rs — Ed448 (RFC 8032)
SEED_SIZE = 57; PRIVATE_KEY_SIZE = 57; PUBLIC_KEY_SIZE = 57; SIGNATURE_SIZE = 114
PrivateKey::sign_msg(&self, msg, context: Option<&[u8]>) PublicKey::verify_sig(&self, msg, sig, context)

// sign.rs — common traits
pub trait Scheme { fn generate_key(); fn derive_key(seed); fn sign(sk, msg, opts); fn verify(pk, msg, sig, opts);
fn name(); fn public_key_size(); fn private_key_size(); fn signature_size(); fn seed_size(); }
pub trait TypedScheme { type PublicKey; type PrivateKey; fn derive_key_typed(&self, seed) -> (Self::PublicKey, Self::PrivateKey); … }

wasm Browser Bindings (feature "browser")

▾
// Build: wasm-pack build --target web --release --features browser
import init, { BlackWallet, verifyMessage, recoverSender, validateMnemonic } from "./pkg/blackchain_crypto_lib_rust.js";
await init();

const wallet = BlackWallet.create(""); // 24-word mnemonic, account 0
const same = BlackWallet.fromMnemonic(phrase, "");
const acct7 = BlackWallet.fromMnemonicAt(phrase, "", 7); // m/44'/60'/0'/0'/7'
wallet.mnemonic() → string wallet.address() → "0x…" wallet.publicKey() → Uint8Array (2,782)
wallet.signMessage(bytes) → Uint8Array (4,873) verifyMessage(msg, sig, pub) → boolean
wallet.signTransaction(txJson) → signed JSON recoverSender(signedJson) → "0x…"
validateMnemonic(phrase) → boolean
// RNG: host crypto.getRandomValues (getrandom wasm_js backend)
09 · Dependencies & Testing

Dependencies & Quality Assurance

Built on the RustCrypto ecosystem. Consensus-critical crates are pinned to exact versions (=x.y.z) in lock-step with Cargo.lock; a patch-version drift would otherwise change verify behaviour and split the network.

ml-dsa =0.1.1

ML-DSA-87 per FIPS 204 (RustCrypto). Deterministic keygen from the 32-byte seed, signing and verification. Apache-2.0 / MIT.

p521 =0.13.3

NIST P-521 ECDSA (RustCrypto elliptic-curves). SEC1 encoding, on-curve validation. Apache-2.0 / MIT.

ed448-rust =0.1.1 · ed448-goldilocks =0.9.0

Ed448 key generation and signing (ed448-rust) plus verification (ed448-goldilocks), cross-checked against RFC 8032 vectors.

sha3 =0.10.8 · hkdf =0.12.4 · sha2 =0.10.9

Keccak-256, SHAKE256, SHA3-512 and HKDF for hashing, nonces, address derivation and per-curve seed expansion.

alloy-primitives =1.5.7 · alloy-rlp =0.3.13

Ethereum types (Address, U256, Bytes) and canonical RLP encoding/decoding of the transaction envelope.

bip39 =2.2.2 · bip32 0.5 · zeroize =1.8.2

BIP-39 mnemonics (24 words), hardened BIP-32 derivation, and zeroization of every secret buffer on drop.

Test & Verification Suite

148 Unit & KAT Tests

Known-answer tests against RFC 8032 (Ed448), FIPS 204 (ML-DSA-87) and FIPS 186-4 (P-521), golden vectors for composite keys, addresses and transaction hashes, plus attack_vectors.rs: malleability, sub-signature and sub-key splicing, tampering, non-canonical RLP, degenerate keys — each asserted to be rejected.

Property-based (proptest)

tests/adversarial.rs: random sign→recover round-trips, bit-flip resilience, cross-chain replay protection and address determinism over thousands of generated inputs.

6 libFuzzer Targets

fuzz_tx_decode, fuzz_pubkey_parse, fuzz_composite_sig_verify, fuzz_tx_sign_recover, fuzz_mnemonic_seed, fuzz_signature_hash — each encodes explicit invariants, not just absence of crashes (see FUZZING.md).

criterion Benchmarks

benches/latency.rs measures BIP-39 setup, HD derivation, keygen, key serialisation, transaction hashing, composite signing, recovery and payload scaling — the source of section 07.

CI / Supply Chain

GitHub Actions: fmt, clippy, tests, cargo audit; Dependabot for dependency updates; #![forbid(unsafe_code)] at crate root; MSRV 1.85 pinned.

WASM Build

Target-gated wasm32-unknown-unknown package for the browser wallet extension; the same crate is linked natively into the node and the Substrate runtime host function.

Audit status: The library has not yet undergone an independent cryptographic audit (see SECURITY.md). Until the external review is complete it is designated for research, development and evaluation; the mainnet launch plan gates value transfers on that review.
testing
# Run all tests (unit, KAT, attack vectors, proptest) cargo test --release # Lint and audit as in CI cargo clippy --all-targets -- -D warnings cargo audit # Benchmarks (criterion) cargo bench --bench latency # Fuzzing (nightly) and browser build cargo +nightly fuzz run fuzz_tx_sign_recover -- -max_total_time=300 wasm-pack build --target web --release --features browser
Conclusion

Quantum-secure Cryptography
for the Blockchain Era

Infyn Crypto combines the proven security of classical elliptic-curve cryptography with the future protection of the standardised lattice scheme ML-DSA-87. Three signature schemes, one composite signature, one Rust crate for node, runtime and browser — with full Ethereum address and transaction compatibility.

Infyn Development · blackchain-crypto-lib-rust 0.1.0 · September 2026