Expand description
Β§π¦ π ZebraChain: A futuristic cryptographic identity system.
ZebraChain is a logged, quantum safe signing protocol designed to replace the long lived asymmetric key pairs used to sign software releases (and to sign other super important stuff).
In short, ZebraChain:
-
Logs each signature in a block chain
-
Changes the keypairs at every signature by including the public key used to sign the current block and the hash of the public key that will be used to sign the next block
-
Is quantum secure because it uses ML-DSA + ed25519 in a hybrid signing construction (as recommended by the ML-DSA authors)
This is a pre-release crate. The API is still being finalized. The 0.0.x releases make no API commitments, nor any commits to the protocol.
However, the dust is settling quickly and itβs a perfect time to jump in and start building experimental applications on top of ZebraChain!
Β§β οΈ Security Warning
ZebraChain is not yet suitable for production use.
This is a nascent implementation of a yet to be finalized protocol. Itβs also built on a quite new Rust implementation of ML-DSA that has its own security warning.
Β§π Quickstart
use tempfile;
use zf_zebrachain::{ChainStore, Cursor, Hash, OwnedChainStore, Payload};
// To create a chain and make signatures, you need a directory for your public chain files
// and another directory for your secret chain files:
let chain_dir = tempfile::TempDir::new().unwrap();
let secret_chain_dir = tempfile::TempDir::new().unwrap();
// Use both directories in your OwnedChainStore:
let owned_store = OwnedChainStore::new(chain_dir.path(), secret_chain_dir.path());
// OwnedChainStore.list_chains() will return zero chains (as we haven't created any yet):
assert_eq!(owned_store.list_chains().unwrap(), []);
// A Payload is what you to sign. Currently it's a 64-bit timestamp and a 320-bit hash. To
// create a new chain, you need the first payload that you want to sign:
let payload1 = Payload::new_time_stamped(Hash::compute(b"Message number 1"));
// Lastly, you need a password (or a key from a hardware security module or similar) that will
// be used to encrypt this secret chain:
let password = b"SUPER BAD PASSWORD";
let mut owned_chain = owned_store.generate_chain(&payload1, password).unwrap();
assert_eq!(owned_chain.head().payload, payload1);
assert_eq!(owned_chain.tail().payload, payload1);
// Make another signature like this:
let payload2 = Payload::new_time_stamped(Hash::compute(b"Message number 2"));
owned_chain.sign(&payload2).unwrap();
assert_eq!(owned_chain.head().payload, payload1);
assert_eq!(owned_chain.tail().payload, payload2);
// A chain is identified by its `chain_hash`, which is the hash of the 1st block in the chain:
let chain_hash = *owned_chain.chain_hash();
assert_eq!(chain_hash, owned_chain.head().block_hash);
// OwnedChainStore.list_chains() now shows our expected chain:
assert_eq!(owned_store.list_chains().unwrap(), [chain_hash]);
// Reopen the owned chain and create additional signatures like this:
let mut owned_chain = owned_store.open_chain(&chain_hash, password).unwrap();
let payload3 = Payload::new_time_stamped(Hash::compute(b"Message number 3"));
owned_chain.sign(&payload3).unwrap();
assert_eq!(owned_chain.head().payload, payload1);
assert_eq!(owned_chain.tail().payload, payload3);
// A ChainStore is used for consuming the public side of the chain:
let store = ChainStore::new(chain_dir.path());
// ChainStore.list_chains() likewise shows our expected chain:
assert_eq!(store.list_chains().unwrap(), [chain_hash]);
// Open and fully verify the public chain by the `chain_hash` like this:
let mut chain = store.open_chain(&chain_hash).unwrap();
assert_eq!(chain.head().payload, payload1);
assert_eq!(chain.tail().payload, payload3);
// A Cursor allows you step step forward and backward through a chain:
let mut cursor = Cursor::from_tail(&mut chain);
assert_eq!(cursor.block_state().payload, payload3);
cursor.previous_block().unwrap();
assert_eq!(cursor.block_state().payload, payload2);
cursor.previous_block().unwrap();
assert_eq!(cursor.block_state().payload, payload1);StructsΒ§
- Block
- Validate block wire format, extract items from the same.
- Block
State - Contains state from current block needed to validate next block, plus the payload.
- Chain
- Read and write blocks to a file.
- Chain
Iter - Iterate through each Block in a Chain.
- Chain
Store - Organizes Chain files in a directory.
- Check
Point - Check point a chain for fast reload.
- Cursor
- Step forward or backward through a Chain, block by block.
- Entropy
Error - Error equivalent to gettrandom::Error.
- Hash
- Buffer containing the 320-bit (40-byte) BLAKE2b hash, with ConstantTimeEq.
- MutBlock
- Builds up a new block in a buffer.
- MutOwned
Block - Builds up both public and secret block for new block being signed.
- MutSecret
Block - Builds a new SecretBlock up in a buffer.
- Owned
Block State - Combines BlockState and SecretBlockState.
- Owned
Chain - Sign new blocks in an owned chain.
- Owned
Chain Store - Used to create new blocks in a chain you own.
- Payload
- Content to be included in block and signed.
- Secret
- A 384-bit (48-byte) root secret with ZeroizeOnDrop and ConstantTimeEq.
- Secret
Block - Decrypts and validates a secret block.
- Secret
Block State - State from this secret block needed to validate the next block, plus the payload.
- Secret
Chain - Save secret chain to non-volatile storage (encrypted and authenticated).
- Secret
Chain Header - Secret chain header.
- Secret
Chain Iter - Iterate through secret blocks contained in a secret chain file.
- Secret
Chain Store - Organizes SecretChain files in a directory.
- Seed
- Stores secret and next_secret.
- SubSecret
- Simple buffer with ZeroizeOnDrop and ConstantTimeEq.
EnumsΒ§
- Block
Error - Error conditions hit when validating a Block.
- HexError
- Error when trying to decode a hex encoded Hash.
- Secret
Block Error - Error conditions hit when validating a SecretBlock.
- Zbase32
Error - Error when trying to decode a Zbase32 encoded Hash.
ConstantsΒ§
- BLOCK
- Size of the ZebraChain block (4069 bytes).
- CONTEXT
- Size of context bytes (48 bytes)
- DIGEST
- Size of hash output digest (45 bytes).
- HEXDIGEST
- Size of hex-encoded hash (90 bytes).
- PAYLOAD
- Size of the ZebraChain payload (48 bytes).
- SECRET
- Size of secrets (48 bytes)
- SECRET_
BLOCK - Size of the decrypted secret block (292 bytes).
- SECRET_
BLOCK_ AEAD - Size of the encrypted secret block (308 bytes) [this is the size on disk].
- SECRET_
CHAIN_ HEADER - Size of header at start of secret chain (88 bytes).
- Z32DIGEST
- Size of Zbase32-encoded hash (72 bytes).
FunctionsΒ§
- sign_
block - Sign a block buffer.
Type AliasesΒ§
- SubSecret192
- A 192-bit (24-byte) derived secret.
- SubSecret256
- A 256-bit (32-byte) derived secret.