General Upgrade, NEW: WebServers, Better Docs
Some checks failed
CI / checks (push) Failing after 3m30s
Some checks failed
CI / checks (push) Failing after 3m30s
This commit is contained in:
parent
5f11d476b6
commit
59419f086f
122 changed files with 10122 additions and 4965 deletions
2
crypto/Cargo.lock
generated
2
crypto/Cargo.lock
generated
|
|
@ -4,4 +4,4 @@ version = 4
|
|||
|
||||
[[package]]
|
||||
name = "crypto"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "mtp-crypto"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
edition = "2024"
|
||||
|
||||
[package.metadata.cargo-machete]
|
||||
|
|
@ -18,11 +18,14 @@ sha2 = { version = "0.11", optional = true }
|
|||
zeroize = { version = "1.9", features = ["derive"] }
|
||||
thiserror = "1"
|
||||
base64 = "0.22"
|
||||
rand_core = { version = "0.6", features = ["getrandom"] }
|
||||
rand_core = { version = "0.10.1" }
|
||||
rand = "0.10.2"
|
||||
getrandom = "0.4.3"
|
||||
mlkem-tls = { version = "0.2", optional = true }
|
||||
ml-dsa = { version = "0.1.1", optional = true }
|
||||
serde = { version = "1", optional = true, features = ["derive"] }
|
||||
rcgen = { version = "0.14", optional = true }
|
||||
time = { version = "0.3", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["chacha20poly1305", "ed25519-dalek", "hkdf", "sha2", "ml-dsa"]
|
||||
|
|
@ -32,3 +35,6 @@ full = ["default", "aes-gcm"]
|
|||
pqc = ["mlkem-tls", "ml-dsa"]
|
||||
serde = ["dep:serde"]
|
||||
wasm = ["getrandom/wasm_js"]
|
||||
hkdf = ["dep:hkdf", "dep:sha2"]
|
||||
sha2 = ["dep:sha2"]
|
||||
tls = ["dep:rcgen", "dep:time"]
|
||||
|
|
|
|||
164
crypto/README.md
164
crypto/README.md
|
|
@ -1,164 +0,0 @@
|
|||
# mtp-crypto
|
||||
|
||||
Cryptographic primitives for the MTP protocol. Classical and post-quantum.
|
||||
|
||||
## Features
|
||||
|
||||
| Feature | Primitives |
|
||||
|---------|-----------|
|
||||
| `default` | XChaCha20-Poly1305, Ed25519, ML-DSA-65, HKDF-SHA-256, SHA-256 |
|
||||
| `full` | default + AES-256-GCM |
|
||||
| `pqc` | ML-KEM-768+X25519 hybrid KEM |
|
||||
|
||||
ML-DSA-65 is enabled by default so dual-signature support is always available
|
||||
without a separate PQC feature flag in protocol crates.
|
||||
|
||||
## AEAD
|
||||
|
||||
XChaCha20-Poly1305 (default) and AES-256-GCM (`full` feature). Nonce is prepended to ciphertext.
|
||||
|
||||
```rust
|
||||
use mtp_crypto::{ChaCha20Poly1305, AeadEncrypt, AeadDecrypt};
|
||||
|
||||
let cipher = ChaCha20Poly1305::new([0u8; 32]);
|
||||
let ct = cipher.encrypt(b"hello", b"aad")?;
|
||||
let pt = cipher.decrypt(&ct, b"aad")?;
|
||||
```
|
||||
|
||||
## Signatures
|
||||
|
||||
### Ed25519
|
||||
|
||||
```rust
|
||||
use mtp_crypto::{Ed25519Signer, SignatureScheme};
|
||||
|
||||
let (signer, sk, pk) = Ed25519Signer::generate();
|
||||
let sig = signer.sign(b"message")?;
|
||||
signer.verify(b"message", &sig)?;
|
||||
```
|
||||
|
||||
### ML-DSA-65
|
||||
|
||||
```rust
|
||||
use mtp_crypto::{MlDsaSigner, SignatureScheme};
|
||||
|
||||
let (signer, sk, pk) = MlDsaSigner::generate();
|
||||
let sig = signer.sign(b"message")?;
|
||||
signer.verify(b"message", &sig)?;
|
||||
|
||||
// Load from stored bytes
|
||||
let signer = MlDsaSigner::new(&sk, &pk)?;
|
||||
```
|
||||
|
||||
### Dual signatures
|
||||
|
||||
```rust
|
||||
use mtp_crypto::{sign_dual, DualSignature, Ed25519Signer, MlDsaSigner};
|
||||
|
||||
let (ed_signer, _, _) = Ed25519Signer::generate();
|
||||
let (ml_signer, _, _) = MlDsaSigner::generate();
|
||||
let dual = sign_dual(ed_signer.signing_key(), ml_signer.signing_key(), b"msg");
|
||||
dual.verify(ed_signer.verifying_key(), ml_signer.verifying_key(), b"msg")?;
|
||||
```
|
||||
|
||||
## Hybrid KEM
|
||||
|
||||
X25519 + ML-KEM-768. 64-byte shared secret. Feed into HKDF before use.
|
||||
|
||||
```rust
|
||||
use mtp_crypto::HybridKem;
|
||||
|
||||
let (sk, pk) = HybridKem::generate_keypair();
|
||||
let enc = HybridKem::encapsulate(&pk)?;
|
||||
let ss = HybridKem::decapsulate(&sk, &enc.ciphertext)?;
|
||||
assert_eq!(enc.shared_secret, ss);
|
||||
```
|
||||
|
||||
## Encrypted containers
|
||||
|
||||
Self-describing encrypted blobs with algorithm selection via `EncryptionType`.
|
||||
Each blob begins with a marking byte so recipients can decrypt without
|
||||
out-of-band agreement.
|
||||
|
||||
```rust
|
||||
use mtp_crypto::{EncryptionType, Keyring, encrypt_for, decrypt_with};
|
||||
|
||||
let kr = Keyring::generate();
|
||||
let blob = encrypt_for(EncryptionType::MlKemChaCha20Poly1305, &kr.public_key_bundle(), b"data", b"aad")?;
|
||||
let pt = decrypt_with(&blob, &kr, b"aad")?;
|
||||
```
|
||||
|
||||
## Multi-recipient encryption
|
||||
|
||||
Encrypt a payload for multiple recipients using a content-encryption key wrapped
|
||||
per-recipient via Hybrid KEM.
|
||||
|
||||
```rust
|
||||
use mtp_crypto::{Keyring, encrypt_multi, decrypt_multi};
|
||||
|
||||
let alice = Keyring::generate();
|
||||
let bob = Keyring::generate();
|
||||
let msg = encrypt_multi(b"secret", b"aad", &[alice.public_key_bundle(), bob.public_key_bundle()])?;
|
||||
let pt = decrypt_multi(&msg, b"aad", &alice)?;
|
||||
```
|
||||
|
||||
## Authentication handshake
|
||||
|
||||
Canonical domain-separated payloads for the challenge-response handshake.
|
||||
|
||||
```rust
|
||||
use mtp_crypto::auth::{challenge_payload, login_proof_payload, register_proof_payload, host_final_payload};
|
||||
```
|
||||
|
||||
Each payload type uses a distinct domain tag to prevent replay across protocol steps.
|
||||
|
||||
## KDF
|
||||
|
||||
```rust
|
||||
use mtp_crypto::{hkdf_expand, hkdf_extract, derive_encryption_key};
|
||||
|
||||
let key = derive_encryption_key(b"ikm", b"salt", b"context")?;
|
||||
let prk = hkdf_extract(b"ikm", b"salt");
|
||||
```
|
||||
|
||||
## Hashing
|
||||
|
||||
```rust
|
||||
use mtp_crypto::{sha256, sha256_double, Sha256Hasher};
|
||||
|
||||
let h = sha256(b"data");
|
||||
let h2 = sha256_double(b"data");
|
||||
|
||||
let mut hasher = Sha256Hasher::new();
|
||||
hasher.update(b"da");
|
||||
hasher.update(b"ta");
|
||||
let h3 = hasher.finalize();
|
||||
```
|
||||
|
||||
## Key types
|
||||
|
||||
| Type | Secret | Zeroized |
|
||||
|------|--------|----------|
|
||||
| `EncryptionPrivateKey` | KEM/ECDH secret | Yes |
|
||||
| `EncryptionPublicKey` | KEM/ECDH public | No |
|
||||
| `SignaturePrivateKey` | Classical signing key | Yes |
|
||||
| `SignaturePublicKey` | Classical verifying key | No |
|
||||
| `KemPrivateKey` | Hybrid KEM secret | Yes |
|
||||
| `KemPublicKey` | Hybrid KEM public | No |
|
||||
| `SignaturePqPrivateKey` | PQC signing key | Yes |
|
||||
| `SignaturePqPublicKey` | PQC verifying key | No |
|
||||
|
||||
`Keyring` holds all six keys (hybrid KEM + PQ sig + classical sig) plus
|
||||
`generate()`, `to_bytes()`, and `from_bytes()` for serialization.
|
||||
`PublicKeyBundle` holds the three public keys for distribution.
|
||||
|
||||
## Feature flags
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
mtp-crypto = { path = "../crypto" } # classical + ML-DSA
|
||||
mtp-crypto = { path = "../crypto", features = ["pqc"] } # adds hybrid KEM
|
||||
mtp-crypto = { path = "../crypto", features = ["full", "pqc"] } # adds AES-256-GCM + hybrid KEM
|
||||
mtp-crypto = { path = "../crypto", features = ["serde"] } # serde support
|
||||
mtp-crypto = { path = "../crypto", features = ["wasm"] } # WASM compat
|
||||
```
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
use crate::error::CryptoError;
|
||||
|
||||
#[cfg(any(feature = "chacha20poly1305", feature = "aes-gcm"))]
|
||||
use rand_core::OsRng;
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
#[cfg(any(feature = "chacha20poly1305", feature = "aes-gcm"))]
|
||||
use rand_core::RngCore;
|
||||
use getrandom::fill;
|
||||
|
||||
pub trait AeadEncrypt {
|
||||
fn encrypt(&self, plaintext: &[u8], aad: &[u8]) -> Result<Vec<u8>, CryptoError>;
|
||||
|
|
@ -28,13 +28,15 @@ fn prepend_nonce(nonce: &[u8], ciphertext: &mut Vec<u8>) -> Vec<u8> {
|
|||
|
||||
#[cfg(feature = "chacha20poly1305")]
|
||||
pub struct ChaCha20Poly1305 {
|
||||
key: [u8; 32],
|
||||
key: Zeroizing<[u8; 32]>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "chacha20poly1305")]
|
||||
impl ChaCha20Poly1305 {
|
||||
pub fn new(key: [u8; 32]) -> Self {
|
||||
Self { key }
|
||||
Self {
|
||||
key: Zeroizing::new(key),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -45,11 +47,11 @@ impl AeadEncrypt for ChaCha20Poly1305 {
|
|||
use chacha20poly1305::XNonce;
|
||||
use chacha20poly1305::aead::{Aead, KeyInit, Payload};
|
||||
|
||||
let key = chacha20poly1305::Key::from_slice(&self.key);
|
||||
let key = chacha20poly1305::Key::from_slice(self.key.as_ref());
|
||||
let cipher = XChaCha20Poly1305::new(key);
|
||||
|
||||
let mut nonce = [0u8; 24];
|
||||
OsRng.fill_bytes(&mut nonce);
|
||||
fill(&mut nonce).map_err(|_| CryptoError::EncryptionFailed)?;
|
||||
let nonce_ref = XNonce::from_slice(&nonce);
|
||||
|
||||
let payload = Payload {
|
||||
|
|
@ -77,7 +79,7 @@ impl AeadDecrypt for ChaCha20Poly1305 {
|
|||
}
|
||||
|
||||
let (nonce, ct) = ciphertext.split_at(24);
|
||||
let key = chacha20poly1305::Key::from_slice(&self.key);
|
||||
let key = chacha20poly1305::Key::from_slice(self.key.as_ref());
|
||||
let cipher = XChaCha20Poly1305::new(key);
|
||||
let nonce_ref = XNonce::from_slice(nonce);
|
||||
|
||||
|
|
@ -98,13 +100,15 @@ impl AeadCipher for ChaCha20Poly1305 {
|
|||
|
||||
#[cfg(feature = "aes-gcm")]
|
||||
pub struct Aes256Gcm {
|
||||
key: [u8; 32],
|
||||
key: Zeroizing<[u8; 32]>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "aes-gcm")]
|
||||
impl Aes256Gcm {
|
||||
pub fn new(key: [u8; 32]) -> Self {
|
||||
Self { key }
|
||||
Self {
|
||||
key: Zeroizing::new(key),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -115,11 +119,11 @@ impl AeadEncrypt for Aes256Gcm {
|
|||
use aes_gcm::Nonce;
|
||||
use aes_gcm::aead::{Aead, KeyInit, Payload};
|
||||
|
||||
let key = aes_gcm::Key::<AesGcmInner>::from_slice(&self.key);
|
||||
let key = aes_gcm::Key::<AesGcmInner>::from_slice(self.key.as_ref());
|
||||
let cipher = AesGcmInner::new(key);
|
||||
|
||||
let mut nonce = [0u8; 12];
|
||||
OsRng.fill_bytes(&mut nonce);
|
||||
fill(&mut nonce).map_err(|_| CryptoError::EncryptionFailed)?;
|
||||
let nonce_ref = Nonce::from_slice(&nonce);
|
||||
|
||||
let payload = Payload {
|
||||
|
|
@ -147,7 +151,7 @@ impl AeadDecrypt for Aes256Gcm {
|
|||
}
|
||||
|
||||
let (nonce, ct) = ciphertext.split_at(12);
|
||||
let key = aes_gcm::Key::<AesGcmInner>::from_slice(&self.key);
|
||||
let key = aes_gcm::Key::<AesGcmInner>::from_slice(self.key.as_ref());
|
||||
let cipher = AesGcmInner::new(key);
|
||||
let nonce_ref = Nonce::from_slice(nonce);
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
* Step 4. Host -> Client : IdentificationResponse { connected, id, host_sig } host_sig over host_final_payload
|
||||
*/
|
||||
|
||||
/// Domain-separation tags — a distinct leading byte per signed context.
|
||||
/// Domain-separation tags
|
||||
pub mod domain {
|
||||
/// Host's signature over the challenge it issues (step 2).
|
||||
pub const CHALLENGE: u8 = 0x10;
|
||||
|
|
|
|||
|
|
@ -30,4 +30,6 @@ pub enum CryptoError {
|
|||
InvalidHex,
|
||||
#[error("invalid base64 encoding")]
|
||||
InvalidBase64,
|
||||
#[error("TLS error: {0}")]
|
||||
Tls(String),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@ use crate::kem::HybridKem;
|
|||
#[cfg(all(feature = "mlkem-tls", feature = "chacha20poly1305", feature = "hkdf"))]
|
||||
use crate::keypair::{Keyring, PublicKeyBundle};
|
||||
#[cfg(all(feature = "mlkem-tls", feature = "chacha20poly1305", feature = "hkdf"))]
|
||||
use rand_core::RngCore;
|
||||
use rand::Rng;
|
||||
#[cfg(all(feature = "mlkem-tls", feature = "chacha20poly1305", feature = "hkdf"))]
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
pub struct RecipientEntry {
|
||||
pub kem_ciphertext: Vec<u8>,
|
||||
|
|
@ -126,10 +128,10 @@ pub fn encrypt_multi(
|
|||
aad: &[u8],
|
||||
entities: &[PublicKeyBundle],
|
||||
) -> Result<MultiEncryptedMessage, CryptoError> {
|
||||
let mut cek = [0u8; 32];
|
||||
rand_core::OsRng.fill_bytes(&mut cek);
|
||||
let mut cek = Zeroizing::new([0u8; 32]);
|
||||
rand::rng().fill_bytes(cek.as_mut());
|
||||
|
||||
let cipher = ChaCha20Poly1305::new(cek);
|
||||
let cipher = ChaCha20Poly1305::new(*cek);
|
||||
let encrypted_payload = cipher.encrypt(plaintext, aad)?;
|
||||
|
||||
let nonce: [u8; 24] = encrypted_payload[..24]
|
||||
|
|
@ -140,14 +142,14 @@ pub fn encrypt_multi(
|
|||
let mut recipients = Vec::with_capacity(entities.len());
|
||||
for entity in entities {
|
||||
let enc = HybridKem::encapsulate(&entity.kem_public_key)?;
|
||||
let wrap_key = derive_encryption_key(
|
||||
let wrap_key = Zeroizing::new(derive_encryption_key(
|
||||
&enc.shared_secret,
|
||||
b"mtp-multi-key-wrap",
|
||||
b"multi-recipient",
|
||||
)?;
|
||||
)?);
|
||||
|
||||
let wrap_cipher = ChaCha20Poly1305::new(wrap_key);
|
||||
let encrypted_key = wrap_cipher.encrypt(&cek, b"")?;
|
||||
let wrap_cipher = ChaCha20Poly1305::new(*wrap_key);
|
||||
let encrypted_key = wrap_cipher.encrypt(cek.as_ref(), b"")?;
|
||||
|
||||
recipients.push(RecipientEntry {
|
||||
kem_ciphertext: enc.ciphertext,
|
||||
|
|
@ -179,19 +181,27 @@ pub fn decrypt_multi(
|
|||
Ok(s) => s,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let wrap_key = derive_encryption_key(&ss, b"mtp-multi-key-wrap", b"multi-recipient")?;
|
||||
let wrap_cipher = ChaCha20Poly1305::new(wrap_key);
|
||||
let wrap_key = Zeroizing::new(derive_encryption_key(
|
||||
&ss,
|
||||
b"mtp-multi-key-wrap",
|
||||
b"multi-recipient",
|
||||
)?);
|
||||
let wrap_cipher = ChaCha20Poly1305::new(*wrap_key);
|
||||
let cek = match wrap_cipher.decrypt(&entry.encrypted_key, b"") {
|
||||
Ok(k) => k,
|
||||
Ok(k) => Zeroizing::new(k),
|
||||
Err(_) => continue,
|
||||
};
|
||||
let cek_arr: [u8; 32] = cek.try_into().map_err(|_| CryptoError::DecryptionFailed)?;
|
||||
let cek_arr = Zeroizing::new(
|
||||
cek.as_slice()
|
||||
.try_into()
|
||||
.map_err(|_| CryptoError::DecryptionFailed)?,
|
||||
);
|
||||
|
||||
let mut full_ct = Vec::with_capacity(24 + msg.ciphertext.len());
|
||||
full_ct.extend_from_slice(&msg.nonce);
|
||||
full_ct.extend_from_slice(&msg.ciphertext);
|
||||
|
||||
let data_cipher = ChaCha20Poly1305::new(cek_arr);
|
||||
let data_cipher = ChaCha20Poly1305::new(*cek_arr);
|
||||
return data_cipher.decrypt(&full_ct, aad);
|
||||
}
|
||||
Err(CryptoError::DecryptionFailed)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use crate::error::CryptoError;
|
||||
use crate::keypair::{KemPrivateKey, KemPublicKey};
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
pub struct Encapsulated {
|
||||
pub ciphertext: Vec<u8>,
|
||||
pub shared_secret: Vec<u8>,
|
||||
pub shared_secret: Zeroizing<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "mlkem-tls")]
|
||||
|
|
@ -12,7 +13,10 @@ pub struct HybridKem;
|
|||
#[cfg(feature = "mlkem-tls")]
|
||||
impl HybridKem {
|
||||
pub fn generate_keypair() -> (KemPrivateKey, KemPublicKey) {
|
||||
let (ek, dk) = mlkem_tls::X25519MlKem768::keygen(&mut rand_core::OsRng);
|
||||
/* Obviously: cannot find module or crate rand_core06 in this scope
|
||||
use of unresolved module or unlinked crate rand_core06 (rustc E0433) */
|
||||
let (ek, dk) =
|
||||
mlkem_tls::X25519MlKem768::keygen(&mut chacha20poly1305::aead::rand_core::OsRng);
|
||||
(
|
||||
KemPrivateKey::new(dk.as_bytes().to_vec()),
|
||||
KemPublicKey::new(ek.as_bytes().to_vec()),
|
||||
|
|
@ -22,22 +26,25 @@ impl HybridKem {
|
|||
pub fn encapsulate(recipient_pk: &KemPublicKey) -> Result<Encapsulated, CryptoError> {
|
||||
let ek = mlkem_tls::EncapsKey768::try_from(recipient_pk.as_bytes())
|
||||
.map_err(|_| CryptoError::KemEncapsulationFailed)?;
|
||||
let (ct, ss) = mlkem_tls::X25519MlKem768::encapsulate(&ek, &mut rand_core::OsRng);
|
||||
let (ct, ss) = mlkem_tls::X25519MlKem768::encapsulate(
|
||||
&ek,
|
||||
&mut chacha20poly1305::aead::rand_core::OsRng,
|
||||
);
|
||||
Ok(Encapsulated {
|
||||
ciphertext: ct.as_bytes().to_vec(),
|
||||
shared_secret: ss.as_bytes().to_vec(),
|
||||
shared_secret: Zeroizing::new(ss.as_bytes().to_vec()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn decapsulate(
|
||||
recipient_sk: &KemPrivateKey,
|
||||
ciphertext: &[u8],
|
||||
) -> Result<Vec<u8>, CryptoError> {
|
||||
) -> Result<Zeroizing<Vec<u8>>, CryptoError> {
|
||||
let dk = mlkem_tls::DecapsKey768::try_from(recipient_sk.as_bytes())
|
||||
.map_err(|_| CryptoError::KemDecapsulationFailed)?;
|
||||
let ct = mlkem_tls::Ciphertext768Hybrid::try_from(ciphertext)
|
||||
.map_err(|_| CryptoError::KemDecapsulationFailed)?;
|
||||
let ss = mlkem_tls::X25519MlKem768::decapsulate(&dk, &ct);
|
||||
Ok(ss.as_bytes().to_vec())
|
||||
Ok(Zeroizing::new(ss.as_bytes().to_vec()))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,179 +2,59 @@ use std::fmt;
|
|||
|
||||
use base64::Engine;
|
||||
use base64::engine::general_purpose;
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
|
||||
|
||||
// --- Private key types ---
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||
#[derive(Zeroize, ZeroizeOnDrop)]
|
||||
pub struct EncryptionPrivateKey(Vec<u8>);
|
||||
macro_rules! impl_private_key {
|
||||
($name:ident) => {
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||
#[derive(Zeroize, ZeroizeOnDrop)]
|
||||
pub struct $name(Vec<u8>);
|
||||
|
||||
impl EncryptionPrivateKey {
|
||||
pub fn new(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
impl $name {
|
||||
pub fn new(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for $name {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct(stringify!($name))
|
||||
.field("len", &self.0.len())
|
||||
.field("data", &"[REDACTED]")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for $name {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for $name {
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8]> for $name {
|
||||
fn from(bytes: &[u8]) -> Self {
|
||||
Self(bytes.to_vec())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl fmt::Debug for EncryptionPrivateKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("EncryptionPrivateKey")
|
||||
.field("len", &self.0.len())
|
||||
.field("data", &"[REDACTED]")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for EncryptionPrivateKey {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for EncryptionPrivateKey {
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8]> for EncryptionPrivateKey {
|
||||
fn from(bytes: &[u8]) -> Self {
|
||||
Self(bytes.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||
#[derive(Zeroize, ZeroizeOnDrop)]
|
||||
pub struct SignaturePrivateKey(Vec<u8>);
|
||||
|
||||
impl SignaturePrivateKey {
|
||||
pub fn new(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SignaturePrivateKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("SignaturePrivateKey")
|
||||
.field("len", &self.0.len())
|
||||
.field("data", &"[REDACTED]")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for SignaturePrivateKey {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for SignaturePrivateKey {
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8]> for SignaturePrivateKey {
|
||||
fn from(bytes: &[u8]) -> Self {
|
||||
Self(bytes.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||
#[derive(Zeroize, ZeroizeOnDrop)]
|
||||
pub struct KemPrivateKey(Vec<u8>);
|
||||
|
||||
impl KemPrivateKey {
|
||||
pub fn new(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for KemPrivateKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("KemPrivateKey")
|
||||
.field("len", &self.0.len())
|
||||
.field("data", &"[REDACTED]")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for KemPrivateKey {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for KemPrivateKey {
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8]> for KemPrivateKey {
|
||||
fn from(bytes: &[u8]) -> Self {
|
||||
Self(bytes.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||
#[derive(Zeroize, ZeroizeOnDrop)]
|
||||
pub struct SignaturePqPrivateKey(Vec<u8>);
|
||||
|
||||
impl SignaturePqPrivateKey {
|
||||
pub fn new(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SignaturePqPrivateKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("SignaturePqPrivateKey")
|
||||
.field("len", &self.0.len())
|
||||
.field("data", &"[REDACTED]")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for SignaturePqPrivateKey {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for SignaturePqPrivateKey {
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8]> for SignaturePqPrivateKey {
|
||||
fn from(bytes: &[u8]) -> Self {
|
||||
Self(bytes.to_vec())
|
||||
}
|
||||
}
|
||||
impl_private_key!(EncryptionPrivateKey);
|
||||
impl_private_key!(SignaturePrivateKey);
|
||||
impl_private_key!(KemPrivateKey);
|
||||
impl_private_key!(SignaturePqPrivateKey);
|
||||
|
||||
// --- Public key types ---
|
||||
|
||||
|
|
@ -210,213 +90,64 @@ fn base64_to_bytes(s: &str) -> Result<Vec<u8>, crate::error::CryptoError> {
|
|||
.map_err(|_| crate::error::CryptoError::InvalidBase64)
|
||||
}
|
||||
|
||||
// ---
|
||||
macro_rules! impl_public_key {
|
||||
($name:ident) => {
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||
#[derive(Clone)]
|
||||
pub struct $name(Vec<u8>);
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||
#[derive(Clone)]
|
||||
pub struct EncryptionPublicKey(Vec<u8>);
|
||||
impl $name {
|
||||
pub fn new(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
pub fn to_hex(&self) -> String {
|
||||
bytes_to_hex(&self.0)
|
||||
}
|
||||
pub fn from_hex(s: &str) -> Result<Self, crate::error::CryptoError> {
|
||||
hex_to_bytes(s).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl EncryptionPublicKey {
|
||||
pub fn new(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
pub fn to_hex(&self) -> String {
|
||||
bytes_to_hex(&self.0)
|
||||
}
|
||||
pub fn from_hex(s: &str) -> Result<Self, crate::error::CryptoError> {
|
||||
hex_to_bytes(s).map(Self)
|
||||
}
|
||||
impl fmt::Debug for $name {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}({})", stringify!($name), self.to_hex())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for $name {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for $name {
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8]> for $name {
|
||||
fn from(bytes: &[u8]) -> Self {
|
||||
Self(bytes.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&$name> for Vec<u8> {
|
||||
fn from(key: &$name) -> Vec<u8> {
|
||||
key.0.clone()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl fmt::Debug for EncryptionPublicKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "EncryptionPublicKey({})", self.to_hex())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for EncryptionPublicKey {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for EncryptionPublicKey {
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8]> for EncryptionPublicKey {
|
||||
fn from(bytes: &[u8]) -> Self {
|
||||
Self(bytes.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&EncryptionPublicKey> for Vec<u8> {
|
||||
fn from(key: &EncryptionPublicKey) -> Vec<u8> {
|
||||
key.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||
#[derive(Clone)]
|
||||
pub struct SignaturePublicKey(Vec<u8>);
|
||||
|
||||
impl SignaturePublicKey {
|
||||
pub fn new(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
pub fn to_hex(&self) -> String {
|
||||
bytes_to_hex(&self.0)
|
||||
}
|
||||
pub fn from_hex(s: &str) -> Result<Self, crate::error::CryptoError> {
|
||||
hex_to_bytes(s).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SignaturePublicKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "SignaturePublicKey({})", self.to_hex())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for SignaturePublicKey {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for SignaturePublicKey {
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8]> for SignaturePublicKey {
|
||||
fn from(bytes: &[u8]) -> Self {
|
||||
Self(bytes.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&SignaturePublicKey> for Vec<u8> {
|
||||
fn from(key: &SignaturePublicKey) -> Vec<u8> {
|
||||
key.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||
#[derive(Clone)]
|
||||
pub struct KemPublicKey(Vec<u8>);
|
||||
|
||||
impl KemPublicKey {
|
||||
pub fn new(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
pub fn to_hex(&self) -> String {
|
||||
bytes_to_hex(&self.0)
|
||||
}
|
||||
pub fn from_hex(s: &str) -> Result<Self, crate::error::CryptoError> {
|
||||
hex_to_bytes(s).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for KemPublicKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "KemPublicKey({})", self.to_hex())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for KemPublicKey {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for KemPublicKey {
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8]> for KemPublicKey {
|
||||
fn from(bytes: &[u8]) -> Self {
|
||||
Self(bytes.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&KemPublicKey> for Vec<u8> {
|
||||
fn from(key: &KemPublicKey) -> Vec<u8> {
|
||||
key.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||
#[derive(Clone)]
|
||||
pub struct SignaturePqPublicKey(Vec<u8>);
|
||||
|
||||
impl SignaturePqPublicKey {
|
||||
pub fn new(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
pub fn to_hex(&self) -> String {
|
||||
bytes_to_hex(&self.0)
|
||||
}
|
||||
pub fn from_hex(s: &str) -> Result<Self, crate::error::CryptoError> {
|
||||
hex_to_bytes(s).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SignaturePqPublicKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "SignaturePqPublicKey({})", self.to_hex())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for SignaturePqPublicKey {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for SignaturePqPublicKey {
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8]> for SignaturePqPublicKey {
|
||||
fn from(bytes: &[u8]) -> Self {
|
||||
Self(bytes.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&SignaturePqPublicKey> for Vec<u8> {
|
||||
fn from(key: &SignaturePqPublicKey) -> Vec<u8> {
|
||||
key.0.clone()
|
||||
}
|
||||
}
|
||||
impl_public_key!(EncryptionPublicKey);
|
||||
impl_public_key!(SignaturePublicKey);
|
||||
impl_public_key!(KemPublicKey);
|
||||
impl_public_key!(SignaturePqPublicKey);
|
||||
|
||||
// --- Keyring ---
|
||||
|
||||
|
|
@ -477,7 +208,7 @@ impl Keyring {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
pub fn to_bytes(&self) -> Zeroizing<Vec<u8>> {
|
||||
let fields: &[&[u8]] = &[
|
||||
self.kem_public_key.as_bytes(),
|
||||
self.kem_secret_key.as_bytes(),
|
||||
|
|
@ -486,7 +217,7 @@ impl Keyring {
|
|||
self.sig_cl_public_key.as_bytes(),
|
||||
self.sig_cl_secret_key.as_bytes(),
|
||||
];
|
||||
let mut out = Vec::new();
|
||||
let mut out = Zeroizing::new(Vec::new());
|
||||
for f in fields {
|
||||
out.extend_from_slice(&(f.len() as u16).to_be_bytes());
|
||||
out.extend_from_slice(f);
|
||||
|
|
@ -549,12 +280,6 @@ impl TryFrom<&[u8]> for Keyring {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&Keyring> for Vec<u8> {
|
||||
fn from(keyring: &Keyring) -> Vec<u8> {
|
||||
keyring.to_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Keyring {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Keyring")
|
||||
|
|
@ -787,7 +512,7 @@ mod tests {
|
|||
SignaturePublicKey::new(vec![4u8; 16]),
|
||||
SignaturePrivateKey::new(vec![5u8; 16]),
|
||||
);
|
||||
let bytes: Vec<u8> = Vec::from(&keyring);
|
||||
let bytes = keyring.to_bytes();
|
||||
let recovered = Keyring::try_from(bytes.as_slice())?;
|
||||
assert_eq!(keyring.to_bytes(), recovered.to_bytes());
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ pub mod enc;
|
|||
|
||||
pub mod helper;
|
||||
|
||||
#[cfg(feature = "tls")]
|
||||
pub mod tls;
|
||||
|
||||
pub use aead::{AeadCipher, AeadDecrypt, AeadEncrypt};
|
||||
pub use error::CryptoError;
|
||||
pub use keypair::{
|
||||
|
|
@ -85,7 +88,9 @@ mod tests {
|
|||
use crate::aead::{AeadDecrypt, AeadEncrypt, ChaCha20Poly1305};
|
||||
let cipher_a = ChaCha20Poly1305::new([0xAB; 32]);
|
||||
let cipher_b = ChaCha20Poly1305::new([0xCD; 32]);
|
||||
let ct = cipher_a.encrypt(b"hello", b"").expect("encryption should succeed");
|
||||
let ct = cipher_a
|
||||
.encrypt(b"hello", b"")
|
||||
.expect("encryption should succeed");
|
||||
assert!(cipher_b.decrypt(&ct, b"").is_err());
|
||||
}
|
||||
|
||||
|
|
@ -106,11 +111,15 @@ mod tests {
|
|||
let (signer, sk, pk) = Ed25519Signer::generate();
|
||||
let msg = b"test message";
|
||||
let sig = signer.sign(msg).expect("signing should succeed");
|
||||
signer.verify(msg, &sig).expect("verification should succeed");
|
||||
signer
|
||||
.verify(msg, &sig)
|
||||
.expect("verification should succeed");
|
||||
verify_ed25519(&pk, msg, &sig).expect("verification should succeed");
|
||||
|
||||
let loaded = Ed25519Signer::new(&sk).expect("signer loading should succeed");
|
||||
loaded.verify(msg, &sig).expect("verification should succeed");
|
||||
loaded
|
||||
.verify(msg, &sig)
|
||||
.expect("verification should succeed");
|
||||
}
|
||||
|
||||
#[cfg(feature = "ed25519-dalek")]
|
||||
|
|
@ -128,11 +137,15 @@ mod tests {
|
|||
let (signer, sk, pk) = MlDsaSigner::generate();
|
||||
let msg = b"test message";
|
||||
let sig = signer.sign(msg).expect("signing should succeed");
|
||||
signer.verify(msg, &sig).expect("verification should succeed");
|
||||
signer
|
||||
.verify(msg, &sig)
|
||||
.expect("verification should succeed");
|
||||
verify_ml_dsa(&pk, msg, &sig).expect("verification should succeed");
|
||||
|
||||
let loaded = MlDsaSigner::new(&sk, &pk).expect("signer loading should succeed");
|
||||
loaded.verify(msg, &sig).expect("verification should succeed");
|
||||
loaded
|
||||
.verify(msg, &sig)
|
||||
.expect("verification should succeed");
|
||||
}
|
||||
|
||||
#[cfg(feature = "ml-dsa")]
|
||||
|
|
@ -183,8 +196,8 @@ mod tests {
|
|||
.expect("key derivation should succeed");
|
||||
assert_eq!(key.len(), 32);
|
||||
|
||||
let expanded = hkdf_expand(b"ikm", b"salt", b"info", 64)
|
||||
.expect("HKDF expansion should succeed");
|
||||
let expanded =
|
||||
hkdf_expand(b"ikm", b"salt", b"info", 64).expect("HKDF expansion should succeed");
|
||||
assert_eq!(expanded.len(), 64);
|
||||
}
|
||||
|
||||
|
|
@ -285,8 +298,7 @@ mod tests {
|
|||
let kr = Keyring::generate();
|
||||
let bundle = kr.public_key_bundle();
|
||||
let bytes = bundle.as_bytes();
|
||||
let loaded =
|
||||
PublicKeyBundle::from_bytes(&bytes).expect("bundle roundtrip should succeed");
|
||||
let loaded = PublicKeyBundle::from_bytes(&bytes).expect("bundle roundtrip should succeed");
|
||||
assert_eq!(
|
||||
bundle.kem_public_key.as_bytes(),
|
||||
loaded.kem_public_key.as_bytes()
|
||||
|
|
@ -306,7 +318,8 @@ mod tests {
|
|||
fn hybrid_kem_roundtrip() {
|
||||
let (sk, pk) = HybridKem::generate_keypair();
|
||||
let enc = HybridKem::encapsulate(&pk).expect("encapsulation should succeed");
|
||||
let ss = HybridKem::decapsulate(&sk, &enc.ciphertext).expect("decapsulation should succeed");
|
||||
let ss =
|
||||
HybridKem::decapsulate(&sk, &enc.ciphertext).expect("decapsulation should succeed");
|
||||
assert_eq!(enc.shared_secret, ss);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,9 +21,6 @@ impl SigAlgorithm {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ed25519-dalek")]
|
||||
use rand_core::RngCore;
|
||||
|
||||
#[cfg(feature = "ml-dsa")]
|
||||
use crate::keypair::{SignaturePqPrivateKey, SignaturePqPublicKey};
|
||||
|
||||
|
|
@ -50,9 +47,12 @@ impl Ed25519Signer {
|
|||
Ok(Self { secret, public })
|
||||
}
|
||||
|
||||
#[cfg(feature = "ed25519-dalek")]
|
||||
pub fn generate() -> (Self, SignaturePrivateKey, SignaturePublicKey) {
|
||||
use rand::RngExt;
|
||||
|
||||
let mut bytes = [0u8; 32];
|
||||
rand_core::OsRng.fill_bytes(&mut bytes);
|
||||
rand::rng().fill(&mut bytes);
|
||||
let secret = ed25519_dalek::SigningKey::from_bytes(&bytes);
|
||||
let public = secret.verifying_key();
|
||||
let priv_key = SignaturePrivateKey::new(secret.to_bytes().to_vec());
|
||||
|
|
|
|||
45
crypto/src/tls.rs
Normal file
45
crypto/src/tls.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
use rcgen::{CertificateParams, ExtendedKeyUsagePurpose, IsCa, KeyPair, KeyUsagePurpose, SanType};
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
use time::{Duration, OffsetDateTime};
|
||||
|
||||
use crate::CryptoError;
|
||||
|
||||
/// Generate a self-signed TLS certificate and private key for development.
|
||||
///
|
||||
/// Returns `(cert_pem, key_pem)` as byte vectors. The certificate is valid for
|
||||
/// the given domain name plus `127.0.0.1` and `::1`, uses ECDSA P-256, and is
|
||||
/// valid for 13 days from the time of generation.
|
||||
///
|
||||
/// Never panics; all errors are returned as [`CryptoError`].
|
||||
pub fn generate_self_signed_cert(domain: &str) -> Result<(Vec<u8>, Vec<u8>), CryptoError> {
|
||||
let key_pair = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256)
|
||||
.map_err(|e| CryptoError::Tls(format!("key generation failed: {e}")))?;
|
||||
|
||||
let mut params = CertificateParams::new(vec![domain.to_string()])
|
||||
.map_err(|e| CryptoError::Tls(format!("certificate params failed: {e}")))?;
|
||||
|
||||
params.not_before = OffsetDateTime::now_utc() - Duration::minutes(5);
|
||||
params.not_after = OffsetDateTime::now_utc() + Duration::days(13);
|
||||
|
||||
params
|
||||
.subject_alt_names
|
||||
.push(SanType::IpAddress(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))));
|
||||
params
|
||||
.subject_alt_names
|
||||
.push(SanType::IpAddress(IpAddr::V6(Ipv6Addr::new(
|
||||
0, 0, 0, 0, 0, 0, 0, 1,
|
||||
))));
|
||||
|
||||
params.key_usages = vec![KeyUsagePurpose::DigitalSignature];
|
||||
params.extended_key_usages = vec![ExtendedKeyUsagePurpose::ServerAuth];
|
||||
params.is_ca = IsCa::NoCa;
|
||||
|
||||
let cert = params
|
||||
.self_signed(&key_pair)
|
||||
.map_err(|e| CryptoError::Tls(format!("certificate signing failed: {e}")))?;
|
||||
|
||||
let cert_pem = cert.pem().into_bytes();
|
||||
let key_pem = key_pair.serialize_pem().into_bytes();
|
||||
|
||||
Ok((cert_pem, key_pem))
|
||||
}
|
||||
Loading…
Reference in a new issue