Cryptography API Reference
The cryptography module contains the core primitives required for zero-knowledge proofs and secure operations on the Midnight Network, including Poseidon hashing, Jubjub elliptic curve operations, and key derivation.
Key Management
noxipher.crypto.keys
Midnight HD Key Derivation.
CONFIRMED from official deploy guide (docs.midnight.network/guides/deploy-mn-app, Apr 2026): - mnemonic → 64-byte seed via PBKDF2-HMAC-SHA512 (Mnemonic.to_seed()) - DO NOT use mnemonicToEntropy() — this is a common mistake - HDWallet path: m/44'/2400'/account'/role/index - BIP-44 style with Substrate sr25519 (NOT SLIP-0010!)
⚠️ ABOUT SLIP-0010 vs BIP-32: SLIP-0010 for ed25519/sr25519 requires ALL path levels HARDENED. Midnight uses hardened for (44', 2400', account') but NON-hardened for (role, index). → This is Substrate-compatible BIP-44, NOT pure SLIP-0010. → Python implementation uses _ckd_hardened for first 3 levels, _ckd_normal for role + index.
VERIFY STEP (required before shipping): TypeScript (official deploy guide): import { HDWallet, Roles } from '@midnight-ntwrk/wallet-sdk-hd' // v3.1.0 import * as bip39 from 'bip39' const seed = bip39.mnemonicToSeedSync('abandon '.repeat(23) + 'art') const hd = HDWallet.fromSeed(seed) if (hd.type !== 'seedOk') throw new Error('bad seed') const result = hd.hdWallet .selectAccount(0) .selectRoles([Roles.Zswap, Roles.NightExternal, Roles.Dust]) .deriveKeysAt(0) const keys = result.keys console.log('NightExternal:', Buffer.from(keys[Roles.NightExternal]).toString('hex')) console.log('Zswap:', Buffer.from(keys[Roles.Zswap]).toString('hex')) console.log('Dust:', Buffer.from(keys[Roles.Dust]).toString('hex'))
Compare with Python output of KeyDerivation.derive_keys()
KeyDerivation
BIP-44 style HD key derivation for Midnight (coin type 2400).
Path: m/44'/2400'/account'/role/index
ALGORITHM: 1. mnemonic → 64-byte seed (BIP39 PBKDF2-HMAC-SHA512) 2. HMAC-SHA512("Bitcoin seed", seed) → master_key (32B) + master_chain_code (32B) 3. Hardened child derivation for: 44', 2400', account' 4. Normal child derivation for: role, index 5. Result 32-byte key → sr25519.pair_from_seed()
NOTE: HDWallet in TypeScript SDK is Rust WASM. Python implementation uses standard BIP-32 HMAC-SHA512 derivation. VERIFY output with TypeScript test vectors.
Source code in src/noxipher/crypto/keys.py
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | |
derive_all_roles(seed_64, account=0, index=0)
classmethod
Derive keys for all 3 roles (NightExternal, Zswap, Dust).
Returns:
| Type | Description |
|---|---|
dict[int, bytes]
|
{role_int: 32-byte key} |
Source code in src/noxipher/crypto/keys.py
derive_key(seed_64, account=0, role=0, index=0)
classmethod
Derive 32-byte raw key at path m/44'/2400'/account'/role/index.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seed_64
|
bytes
|
64-byte BIP39 seed from mnemonic_to_seed() |
required |
account
|
int
|
Account index (default 0) |
0
|
role
|
int
|
Role index (Roles.NIGHT_EXTERNAL=0, Roles.ZSWAP=3, Roles.DUST=2) |
0
|
index
|
int
|
Key index (default 0) |
0
|
Returns:
| Type | Description |
|---|---|
bytes
|
32-byte raw key (mini secret key for sr25519) |
VERIFY: Compare output with TypeScript wallet-sdk-hd for same seed.
Source code in src/noxipher/crypto/keys.py
mnemonic_to_seed(mnemonic)
staticmethod
BIP39 mnemonic → 64-byte PBKDF2 seed.
IMPORTANT: Uses Mnemonic.to_seed() (64 bytes), NOT
mnemonicToEntropy() (16-32 bytes). This is a common source of errors.
Confirmed: forum.midnight.network/t/.../325 post #7 by Midnight team.
Source code in src/noxipher/crypto/keys.py
Roles
HD derivation role indices for Midnight wallet.
Source code in src/noxipher/crypto/keys.py
SpendingKey
Master spending key — derive all 3 key types from a single mnemonic. Memory-safe: seed is cleared immediately after deriving keys.
Source code in src/noxipher/crypto/keys.py
dust_seed
property
32-byte seed for DustSecretKey derivation.
night_key
property
32-byte raw key for NIGHT_EXTERNAL role.
signer
property
Sr25519 signer for unshielded operations.
zswap_seed
property
32-byte seed for ZswapSecretKeys derivation.
from_mnemonic(mnemonic, network)
classmethod
Sr25519Signer
sr25519 (Schnorr/Ristretto255) signer for unshielded NIGHT transactions.
Context string: b"substrate" (hardcoded in schnorrkel library). Midnight uses sr25519 — standard Substrate signing scheme.
Source code in src/noxipher/crypto/keys.py
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 | |
public_key
property
32-byte sr25519 public key.
__init__(secret_key_bytes)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
secret_key_bytes
|
bytes
|
32-byte raw key from KeyDerivation.derive_key(role=NIGHT_EXTERNAL) |
required |
Source code in src/noxipher/crypto/keys.py
as_substrate_keypair()
Convert to substrate-interface Keypair. Requires substrate-interface.
Source code in src/noxipher/crypto/keys.py
compute_address(network)
Compute unshielded Bech32m address.
CONFIRMED FLOW from ledger-v8 TypeScript
signatureVerifyingKey(private_key_hex) → 32-byte sr25519 public key addressFromKey(verifying_key) → 32-byte address bytes MidnightBech32m.encode('addr', networkId, address_bytes)
HYPOTHESIS: addressFromKey = Blake2b-256(public_key) VERIFY with TypeScript: ledger.addressFromKey(ledger.signatureVerifyingKey(privHex))
Source code in src/noxipher/crypto/keys.py
sign(data)
Sign data with sr25519. Returns 64-byte signature.
Source code in src/noxipher/crypto/keys.py
verify(signature, data)
Verify sr25519 signature.
Source code in src/noxipher/crypto/keys.py
Hash Functions
noxipher.crypto.hash
PersistentHashWriter
A SHA-256 based hasher. Matches Midnight's PersistentHashWriter in base-crypto.
Source code in src/noxipher/crypto/hash.py
blake2_256(data)
hmac_sha256(key, data)
hmac_sha512(key, data)
persistent_hash(data)
ripemd160(data)
sample_bytes(length, domain_separator, seed)
Two-level hash expansion logic used for key derivation (ESK, DSK). Matches Midnight's sample_bytes implementation in ledger/src/dust.rs. Construction: hash(domain || hash(round_u64_le || seed))
Source code in src/noxipher/crypto/hash.py
Poseidon Hash
noxipher.crypto.poseidon
Poseidon
Implementation of Poseidon hash function for BLS12-381 scalar field. Matches Midnight's circuits/src/hash/poseidon/poseidon_cpu.rs.
Source code in src/noxipher/crypto/poseidon.py
hash(inputs)
Sponge-based hashing of multiple field elements.
Source code in src/noxipher/crypto/poseidon.py
linear_layer(state, round_index)
Matrix multiplication + addition of round constants.
Source code in src/noxipher/crypto/poseidon.py
permutation(state)
The core Poseidon permutation.
Source code in src/noxipher/crypto/poseidon.py
Jubjub Curve
noxipher.crypto.jubjub
JubJub curve operations and key derivation for Midnight shielded keys.
This module implements key derivation for Zswap and Dust ecosystems, complying with Midnight Protocol v8.1.0-rc.1.
DustSecretKey
Implementation of DustSecretKey. Matches DustSecretKey in ledger/src/dust.rs.
Source code in src/noxipher/crypto/jubjub.py
public_key
property
Derive Dust Public Key. Matches DustPublicKey derivation: transient_hash([Fr::from_le_bytes("mdn:dust:pk"), sk])
from_seed(seed)
classmethod
Derive Dust key from seed. Matches DustSecretKey::sample_bytes(seed, 64, b"midnight:dsk"). Uses Fr (BLS12-381 scalar field) as per ledger/src/dust.rs.
Source code in src/noxipher/crypto/jubjub.py
ZswapSecretKeys
Implementation of midnight-zswap SecretKeys. Derived from a 32-byte seed.
Source code in src/noxipher/crypto/jubjub.py
coin_public_key
property
Derive Coin Public Key (CPK). Matches SecretKey::public_key in coin-structure/src/coin.rs. hash(b"midnight:zswap-pk[v1]" || coin_sk)
encryption_public_key
property
Derive Encryption Public Key (EPK). Matches SecretKey::public_key in transient-crypto/src/encryption.rs. EPK = generator * enc_sk
from_seed(seed)
classmethod
Derive Zswap keys from a 32-byte seed using protocol-defined domain separators. Matches Seed::derive_coin_secret_key and Seed::derive_encryption_secret_key in midnight-zswap/src/keys.rs.
Source code in src/noxipher/crypto/jubjub.py
coin_commitment(nonce, token_type, value, recipient_is_user, recipient_hash)
Compute Zswap coin commitment. Matches Info::commitment in coin-structure/src/coin.rs.
Source code in src/noxipher/crypto/jubjub.py
compute_binding_commitment(randomness, value=0)
Compute Pedersen binding commitment: G^randomness * H^value. Ensures cryptographic binding between public value balance and ZK proofs.
Source code in src/noxipher/crypto/jubjub.py
hash_to_field(data)
Implementation of transient-crypto/src/hash.rs:hash_to_field. Construction: transient_hash([midnight:field_hash, data])