| Crates.io | fp-encrypted-memo |
| lib.rs | fp-encrypted-memo |
| version | 0.1.1 |
| created_at | 2026-01-24 02:10:30.281843+00 |
| updated_at | 2026-01-24 21:31:31.669158+00 |
| description | Encrypted memo primitives for Orbinum shielded transactions |
| homepage | https://orbinum.network |
| repository | https://github.com/orbinum/node |
| max_upload_size | |
| id | 2065922 |
| size | 112,747 |
Encrypted memo primitives for Orbinum shielded transactions.
When transferring private assets, the sender encrypts note details (value, blinding, owner public key) using the recipient's viewing key. This allows the recipient to:
encryption_key = SHA256(viewing_key || commitment || "orbinum-note-encryption-v1")
ciphertext = ChaCha20Poly1305(note_data, encryption_key, random_nonce)
encrypted_memo = nonce (12 bytes) || ciphertext (76 bytes + 16 bytes MAC)
fp-encrypted-memo
├── core/ # Layer 1: Core types and constants
│ ├── constants.rs # Size limits, domain separators
│ ├── error.rs # MemoError enum
│ └── types.rs # MemoData, ViewingKey, NullifierKey, EdDSAKey
│
├── crypto/ # Layer 2: Cryptographic operations
│ ├── encryption.rs # encrypt_memo(), decrypt_memo()
│ ├── key_derivation.rs # derive_viewing_key(), derive_encryption_key()
│ └── validation.rs # is_valid_encrypted_memo()
│
└── models/ # Layer 3: High-level abstractions
└── keyset.rs # KeySet management
Fundamental types and constants:
Low-level cryptographic operations:
High-level abstractions:
spending_key (master secret)
│
├── viewing_key = SHA256(spending_key || "orbinum-viewing-key-v1")
├── nullifier_key = SHA256(spending_key || "orbinum-nullifier-key-v1")
└── eddsa_key = SHA256(spending_key || "orbinum-eddsa-key-v1")
use fp_encrypted_memo::{MemoData, encrypt_memo, decrypt_memo};
// Prepare memo data
let memo = MemoData {
value: 1000,
owner_pk: [1u8; 32],
blinding: [2u8; 32],
asset_id: 0,
};
// Encrypt (sender side)
let encrypted = encrypt_memo(&memo, &commitment, &recipient_viewing_key, &nonce)?;
// Decrypt (recipient side)
let decrypted = decrypt_memo(&encrypted, &commitment, &my_viewing_key)?;
use fp_encrypted_memo::KeySet;
// Derive all keys from spending key
let keys = KeySet::from_spending_key(spending_key);
// Share viewing key with auditor (read-only access)
let auditor_key = keys.export_viewing_key();
// Decrypt memos
let memo = keys.viewing_key.decrypt(&encrypted, &commitment)?;
use fp_encrypted_memo::{derive_viewing_key, derive_eddsa_key};
let viewing_key = derive_viewing_key(&spending_key);
let eddsa_key = derive_eddsa_key(&spending_key);
encrypt - Enables encrypt_memo_random() with automatic nonce generation (requires std)substrate - Enables Substrate codec derives (Encode, Decode, TypeInfo)Critical: When using encrypt_memo(), the nonce MUST be randomly generated and NEVER reused with the same key. Nonce reuse allows an attacker to recover plaintext.
use rand::RngCore;
let mut nonce = [0u8; 12];
rand::thread_rng().fill_bytes(&mut nonce);
For convenience, use encrypt_memo_random() (requires encrypt feature) which handles nonce generation automatically.
All derived keys use domain separation to prevent key confusion:
cargo test
Tests cover:
Licensed under Apache 2.0 or GPL-3.0.