General & Crypto
This commit is contained in:
parent
0bbcab5727
commit
02f94993c7
27 changed files with 1881 additions and 47 deletions
171
crypto/src/aead.rs
Normal file
171
crypto/src/aead.rs
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
use crate::error::CryptoError;
|
||||
|
||||
#[cfg(any(feature = "chacha20poly1305", feature = "aes-gcm"))]
|
||||
use rand_core::OsRng;
|
||||
|
||||
#[cfg(any(feature = "chacha20poly1305", feature = "aes-gcm"))]
|
||||
use rand_core::RngCore;
|
||||
|
||||
pub trait AeadEncrypt {
|
||||
fn encrypt(&self, plaintext: &[u8], aad: &[u8]) -> Result<Vec<u8>, CryptoError>;
|
||||
}
|
||||
|
||||
pub trait AeadDecrypt {
|
||||
fn decrypt(&self, ciphertext: &[u8], aad: &[u8]) -> Result<Vec<u8>, CryptoError>;
|
||||
}
|
||||
|
||||
pub trait AeadCipher: AeadEncrypt + AeadDecrypt {
|
||||
fn key_size() -> usize;
|
||||
}
|
||||
|
||||
#[cfg(feature = "chacha20poly1305")]
|
||||
pub struct ChaCha20Poly1305 {
|
||||
key: [u8; 32],
|
||||
}
|
||||
|
||||
#[cfg(feature = "chacha20poly1305")]
|
||||
impl ChaCha20Poly1305 {
|
||||
pub fn new(key: [u8; 32]) -> Self {
|
||||
Self { key }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "chacha20poly1305")]
|
||||
impl AeadEncrypt for ChaCha20Poly1305 {
|
||||
fn encrypt(&self, plaintext: &[u8], aad: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
use chacha20poly1305::aead::{Aead, KeyInit, Payload};
|
||||
use chacha20poly1305::XChaCha20Poly1305;
|
||||
use chacha20poly1305::XNonce;
|
||||
|
||||
let key = chacha20poly1305::Key::from_slice(&self.key);
|
||||
let cipher = XChaCha20Poly1305::new(key);
|
||||
|
||||
let mut nonce = [0u8; 24];
|
||||
OsRng.fill_bytes(&mut nonce);
|
||||
let nonce_ref = XNonce::from_slice(&nonce);
|
||||
|
||||
let payload = Payload {
|
||||
msg: plaintext,
|
||||
aad,
|
||||
};
|
||||
|
||||
let mut ciphertext = cipher
|
||||
.encrypt(nonce_ref, payload)
|
||||
.map_err(|_| CryptoError::EncryptionFailed)?;
|
||||
|
||||
let mut out = Vec::with_capacity(nonce.len() + ciphertext.len());
|
||||
out.extend_from_slice(&nonce);
|
||||
out.append(&mut ciphertext);
|
||||
Ok(out)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "chacha20poly1305")]
|
||||
impl AeadDecrypt for ChaCha20Poly1305 {
|
||||
fn decrypt(&self, ciphertext: &[u8], aad: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
use chacha20poly1305::aead::{Aead, KeyInit, Payload};
|
||||
use chacha20poly1305::XChaCha20Poly1305;
|
||||
use chacha20poly1305::XNonce;
|
||||
|
||||
if ciphertext.len() < 24 {
|
||||
return Err(CryptoError::InvalidNonceLength);
|
||||
}
|
||||
|
||||
let (nonce, ct) = ciphertext.split_at(24);
|
||||
let key = chacha20poly1305::Key::from_slice(&self.key);
|
||||
let cipher = XChaCha20Poly1305::new(key);
|
||||
let nonce_ref = XNonce::from_slice(nonce);
|
||||
|
||||
let payload = Payload {
|
||||
msg: ct,
|
||||
aad,
|
||||
};
|
||||
|
||||
cipher
|
||||
.decrypt(nonce_ref, payload)
|
||||
.map_err(|_| CryptoError::DecryptionFailed)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "chacha20poly1305")]
|
||||
impl AeadCipher for ChaCha20Poly1305 {
|
||||
fn key_size() -> usize {
|
||||
32
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "aes-gcm")]
|
||||
pub struct Aes256Gcm {
|
||||
key: [u8; 32],
|
||||
}
|
||||
|
||||
#[cfg(feature = "aes-gcm")]
|
||||
impl Aes256Gcm {
|
||||
pub fn new(key: [u8; 32]) -> Self {
|
||||
Self { key }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "aes-gcm")]
|
||||
impl AeadEncrypt for Aes256Gcm {
|
||||
fn encrypt(&self, plaintext: &[u8], aad: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
use aes_gcm::aead::{Aead, KeyInit, Payload};
|
||||
use aes_gcm::Aes256Gcm as AesGcmInner;
|
||||
use aes_gcm::Nonce;
|
||||
|
||||
let key = aes_gcm::Key::<AesGcmInner>::from_slice(&self.key);
|
||||
let cipher = AesGcmInner::new(key);
|
||||
|
||||
let mut nonce = [0u8; 12];
|
||||
OsRng.fill_bytes(&mut nonce);
|
||||
let nonce_ref = Nonce::from_slice(&nonce);
|
||||
|
||||
let payload = Payload {
|
||||
msg: plaintext,
|
||||
aad,
|
||||
};
|
||||
|
||||
let mut ciphertext = cipher
|
||||
.encrypt(nonce_ref, payload)
|
||||
.map_err(|_| CryptoError::EncryptionFailed)?;
|
||||
|
||||
let mut out = Vec::with_capacity(nonce.len() + ciphertext.len());
|
||||
out.extend_from_slice(&nonce);
|
||||
out.append(&mut ciphertext);
|
||||
Ok(out)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "aes-gcm")]
|
||||
impl AeadDecrypt for Aes256Gcm {
|
||||
fn decrypt(&self, ciphertext: &[u8], aad: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
use aes_gcm::aead::{Aead, KeyInit, Payload};
|
||||
use aes_gcm::Aes256Gcm as AesGcmInner;
|
||||
use aes_gcm::Nonce;
|
||||
|
||||
if ciphertext.len() < 12 {
|
||||
return Err(CryptoError::InvalidNonceLength);
|
||||
}
|
||||
|
||||
let (nonce, ct) = ciphertext.split_at(12);
|
||||
let key = aes_gcm::Key::<AesGcmInner>::from_slice(&self.key);
|
||||
let cipher = AesGcmInner::new(key);
|
||||
let nonce_ref = Nonce::from_slice(nonce);
|
||||
|
||||
let payload = Payload {
|
||||
msg: ct,
|
||||
aad,
|
||||
};
|
||||
|
||||
cipher
|
||||
.decrypt(nonce_ref, payload)
|
||||
.map_err(|_| CryptoError::DecryptionFailed)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "aes-gcm")]
|
||||
impl AeadCipher for Aes256Gcm {
|
||||
fn key_size() -> usize {
|
||||
32
|
||||
}
|
||||
}
|
||||
38
crypto/src/error.rs
Normal file
38
crypto/src/error.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CryptoError {
|
||||
EncryptionFailed,
|
||||
DecryptionFailed,
|
||||
InvalidKeyLength,
|
||||
InvalidNonceLength,
|
||||
InvalidSignature,
|
||||
SigningFailed,
|
||||
VerificationFailed,
|
||||
KeyGenerationFailed,
|
||||
KdfError,
|
||||
KemEncapsulationFailed,
|
||||
KemDecapsulationFailed,
|
||||
UnknownAlgorithm,
|
||||
}
|
||||
|
||||
impl fmt::Display for CryptoError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
CryptoError::EncryptionFailed => write!(f, "encryption failed"),
|
||||
CryptoError::DecryptionFailed => write!(f, "decryption failed"),
|
||||
CryptoError::InvalidKeyLength => write!(f, "invalid key length"),
|
||||
CryptoError::InvalidNonceLength => write!(f, "invalid nonce length"),
|
||||
CryptoError::InvalidSignature => write!(f, "invalid signature"),
|
||||
CryptoError::SigningFailed => write!(f, "signing failed"),
|
||||
CryptoError::VerificationFailed => write!(f, "verification failed"),
|
||||
CryptoError::KeyGenerationFailed => write!(f, "key generation failed"),
|
||||
CryptoError::KdfError => write!(f, "KDF error"),
|
||||
CryptoError::KemEncapsulationFailed => write!(f, "KEM encapsulation failed"),
|
||||
CryptoError::KemDecapsulationFailed => write!(f, "KEM decapsulation failed"),
|
||||
CryptoError::UnknownAlgorithm => write!(f, "unknown algorithm"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for CryptoError {}
|
||||
29
crypto/src/hash.rs
Normal file
29
crypto/src/hash.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
use sha2::Digest;
|
||||
|
||||
pub fn sha256(data: &[u8]) -> [u8; 32] {
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(data);
|
||||
let result = hasher.finalize();
|
||||
result.into()
|
||||
}
|
||||
|
||||
pub fn sha256_double(data: &[u8]) -> [u8; 32] {
|
||||
sha256(&sha256(data))
|
||||
}
|
||||
|
||||
pub struct Sha256Hasher(sha2::Sha256);
|
||||
|
||||
impl Sha256Hasher {
|
||||
pub fn new() -> Self {
|
||||
Self(sha2::Sha256::new())
|
||||
}
|
||||
|
||||
pub fn update(&mut self, data: &[u8]) {
|
||||
self.0.update(data);
|
||||
}
|
||||
|
||||
pub fn finalize(self) -> [u8; 32] {
|
||||
let result = self.0.finalize();
|
||||
result.into()
|
||||
}
|
||||
}
|
||||
34
crypto/src/kdf.rs
Normal file
34
crypto/src/kdf.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
use crate::error::CryptoError;
|
||||
use hkdf::Hkdf;
|
||||
use sha2::Sha256;
|
||||
|
||||
pub fn hkdf_expand(
|
||||
ikm: &[u8],
|
||||
salt: &[u8],
|
||||
info: &[u8],
|
||||
okm_len: usize,
|
||||
) -> Result<Vec<u8>, CryptoError> {
|
||||
let hk = Hkdf::<Sha256>::new(Some(salt), ikm);
|
||||
let mut okm = vec![0u8; okm_len];
|
||||
hk.expand(info, &mut okm)
|
||||
.map_err(|_| CryptoError::KdfError)?;
|
||||
Ok(okm)
|
||||
}
|
||||
|
||||
pub fn hkdf_extract(ikm: &[u8], salt: &[u8]) -> [u8; 32] {
|
||||
let (_, hk) = Hkdf::<Sha256>::extract(Some(salt), ikm);
|
||||
let mut okm = [0u8; 32];
|
||||
hk.expand(&[], &mut okm).expect("hkdf expand failed");
|
||||
okm
|
||||
}
|
||||
|
||||
pub fn derive_encryption_key(
|
||||
ikm: &[u8],
|
||||
salt: &[u8],
|
||||
context: &[u8],
|
||||
) -> Result<[u8; 32], CryptoError> {
|
||||
let key = hkdf_expand(ikm, salt, context, 32)?;
|
||||
let mut out = [0u8; 32];
|
||||
out.copy_from_slice(&key);
|
||||
Ok(out)
|
||||
}
|
||||
45
crypto/src/kem.rs
Normal file
45
crypto/src/kem.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
use crate::error::CryptoError;
|
||||
use crate::keypair::{KemPrivateKey, KemPublicKey};
|
||||
|
||||
pub struct Encapsulated {
|
||||
pub ciphertext: Vec<u8>,
|
||||
pub shared_secret: Vec<u8>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "mlkem-tls")]
|
||||
pub struct HybridKem;
|
||||
|
||||
#[cfg(feature = "mlkem-tls")]
|
||||
impl HybridKem {
|
||||
pub fn generate_keypair() -> (KemPrivateKey, KemPublicKey) {
|
||||
let (dk, ek) =
|
||||
mlkem_tls::X25519MlKem768::keygen(&mut rand_core::OsRng);
|
||||
(
|
||||
KemPrivateKey::new(dk.as_bytes().to_vec()),
|
||||
KemPublicKey::new(ek.as_bytes().to_vec()),
|
||||
)
|
||||
}
|
||||
|
||||
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);
|
||||
Ok(Encapsulated {
|
||||
ciphertext: ct.as_bytes().to_vec(),
|
||||
shared_secret: ss.as_bytes().to_vec(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn decapsulate(
|
||||
recipient_sk: &KemPrivateKey,
|
||||
ciphertext: &[u8],
|
||||
) -> Result<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())
|
||||
}
|
||||
}
|
||||
212
crypto/src/keypair.rs
Normal file
212
crypto/src/keypair.rs
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
#[derive(Zeroize, ZeroizeOnDrop)]
|
||||
pub struct EncryptionPrivateKey(Vec<u8>);
|
||||
|
||||
impl EncryptionPrivateKey {
|
||||
pub fn new(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for EncryptionPrivateKey {
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[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 From<Vec<u8>> for SignaturePrivateKey {
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EncryptionPublicKey(Vec<u8>);
|
||||
|
||||
impl EncryptionPublicKey {
|
||||
pub fn new(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for EncryptionPublicKey {
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for SignaturePublicKey {
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ZeroizeOnDrop)]
|
||||
pub struct KeyGroup {
|
||||
#[zeroize(skip)]
|
||||
pub encryption_public_key: EncryptionPublicKey,
|
||||
pub encryption_private_key: EncryptionPrivateKey,
|
||||
#[zeroize(skip)]
|
||||
pub signature_public_key: SignaturePublicKey,
|
||||
pub signature_private_key: SignaturePrivateKey,
|
||||
}
|
||||
|
||||
impl KeyGroup {
|
||||
pub fn new(
|
||||
encryption_public_key: EncryptionPublicKey,
|
||||
encryption_private_key: EncryptionPrivateKey,
|
||||
signature_public_key: SignaturePublicKey,
|
||||
signature_private_key: SignaturePrivateKey,
|
||||
) -> Self {
|
||||
Self {
|
||||
encryption_public_key,
|
||||
encryption_private_key,
|
||||
signature_public_key,
|
||||
signature_private_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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 From<Vec<u8>> for KemPrivateKey {
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for KemPublicKey {
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for SignaturePqPublicKey {
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[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 From<Vec<u8>> for SignaturePqPrivateKey {
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ZeroizeOnDrop)]
|
||||
pub struct Keyring {
|
||||
#[zeroize(skip)]
|
||||
pub kem_public_key: KemPublicKey,
|
||||
pub kem_secret_key: KemPrivateKey,
|
||||
#[zeroize(skip)]
|
||||
pub sig_pq_public_key: SignaturePqPublicKey,
|
||||
pub sig_pq_secret_key: SignaturePqPrivateKey,
|
||||
#[zeroize(skip)]
|
||||
pub sig_cl_public_key: SignaturePublicKey,
|
||||
pub sig_cl_secret_key: SignaturePrivateKey,
|
||||
}
|
||||
|
||||
impl Keyring {
|
||||
pub fn new(
|
||||
kem_public_key: KemPublicKey,
|
||||
kem_secret_key: KemPrivateKey,
|
||||
sig_pq_public_key: SignaturePqPublicKey,
|
||||
sig_pq_secret_key: SignaturePqPrivateKey,
|
||||
sig_cl_public_key: SignaturePublicKey,
|
||||
sig_cl_secret_key: SignaturePrivateKey,
|
||||
) -> Self {
|
||||
Self {
|
||||
kem_public_key,
|
||||
kem_secret_key,
|
||||
sig_pq_public_key,
|
||||
sig_pq_secret_key,
|
||||
sig_cl_public_key,
|
||||
sig_cl_secret_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,46 @@
|
|||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
pub mod aead;
|
||||
pub mod error;
|
||||
pub mod keypair;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[cfg(feature = "sha2")]
|
||||
pub mod hash;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "hkdf")]
|
||||
pub mod kdf;
|
||||
|
||||
#[cfg(any(feature = "ed25519-dalek", feature = "ml-dsa"))]
|
||||
pub mod sign;
|
||||
|
||||
#[cfg(feature = "mlkem-tls")]
|
||||
pub mod kem;
|
||||
|
||||
pub use aead::{AeadCipher, AeadDecrypt, AeadEncrypt};
|
||||
pub use error::CryptoError;
|
||||
pub use keypair::{
|
||||
EncryptionPrivateKey, EncryptionPublicKey, KemPrivateKey, KemPublicKey, KeyGroup, Keyring,
|
||||
SignaturePqPrivateKey, SignaturePqPublicKey, SignaturePrivateKey, SignaturePublicKey,
|
||||
};
|
||||
|
||||
#[cfg(feature = "chacha20poly1305")]
|
||||
pub use aead::ChaCha20Poly1305;
|
||||
|
||||
#[cfg(feature = "aes-gcm")]
|
||||
pub use aead::Aes256Gcm;
|
||||
|
||||
#[cfg(feature = "ed25519-dalek")]
|
||||
pub use sign::{verify_ed25519, Ed25519Signer, SignatureScheme};
|
||||
|
||||
#[cfg(feature = "ml-dsa")]
|
||||
pub use sign::{verify_ml_dsa, MlDsaSigner};
|
||||
|
||||
#[cfg(all(feature = "ed25519-dalek", feature = "ml-dsa"))]
|
||||
pub use sign::{sign_dual, DualSignature};
|
||||
|
||||
#[cfg(feature = "sha2")]
|
||||
pub use hash::{sha256, sha256_double, Sha256Hasher};
|
||||
|
||||
#[cfg(feature = "hkdf")]
|
||||
pub use kdf::{derive_encryption_key, hkdf_expand, hkdf_extract};
|
||||
|
||||
#[cfg(feature = "mlkem-tls")]
|
||||
pub use kem::HybridKem;
|
||||
|
|
|
|||
232
crypto/src/sign.rs
Normal file
232
crypto/src/sign.rs
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
use crate::error::CryptoError;
|
||||
|
||||
#[cfg(feature = "ed25519-dalek")]
|
||||
use crate::keypair::{SignaturePrivateKey, SignaturePublicKey};
|
||||
|
||||
#[cfg(feature = "ed25519-dalek")]
|
||||
use rand_core::RngCore;
|
||||
|
||||
#[cfg(feature = "ml-dsa")]
|
||||
use crate::keypair::{SignaturePqPrivateKey, SignaturePqPublicKey};
|
||||
|
||||
pub trait SignatureScheme {
|
||||
fn sign(&self, msg: &[u8]) -> Result<Vec<u8>, CryptoError>;
|
||||
fn verify(&self, msg: &[u8], signature: &[u8]) -> Result<(), CryptoError>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "ed25519-dalek")]
|
||||
pub struct Ed25519Signer {
|
||||
secret: ed25519_dalek::SigningKey,
|
||||
public: ed25519_dalek::VerifyingKey,
|
||||
}
|
||||
|
||||
#[cfg(feature = "ed25519-dalek")]
|
||||
impl Ed25519Signer {
|
||||
pub fn new(secret_key: &SignaturePrivateKey) -> Result<Self, CryptoError> {
|
||||
let bytes: [u8; 32] = secret_key
|
||||
.as_bytes()
|
||||
.try_into()
|
||||
.map_err(|_| CryptoError::KeyGenerationFailed)?;
|
||||
let secret = ed25519_dalek::SigningKey::from_bytes(&bytes);
|
||||
let public = secret.verifying_key();
|
||||
Ok(Self { secret, public })
|
||||
}
|
||||
|
||||
pub fn generate() -> (Self, SignaturePrivateKey, SignaturePublicKey) {
|
||||
let mut bytes = [0u8; 32];
|
||||
rand_core::OsRng.fill_bytes(&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());
|
||||
let pub_key = SignaturePublicKey::new(public.to_bytes().to_vec());
|
||||
let signer = Self { secret, public };
|
||||
(signer, priv_key, pub_key)
|
||||
}
|
||||
|
||||
pub fn public_key(&self) -> SignaturePublicKey {
|
||||
SignaturePublicKey::new(self.public.to_bytes().to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ed25519-dalek")]
|
||||
impl SignatureScheme for Ed25519Signer {
|
||||
fn sign(&self, msg: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
use ed25519_dalek::Signer;
|
||||
let signature = self.secret.sign(msg).to_bytes().to_vec();
|
||||
Ok(signature)
|
||||
}
|
||||
|
||||
fn verify(&self, msg: &[u8], signature: &[u8]) -> Result<(), CryptoError> {
|
||||
use ed25519_dalek::Verifier;
|
||||
let sig_bytes: [u8; 64] = signature
|
||||
.try_into()
|
||||
.map_err(|_| CryptoError::InvalidSignature)?;
|
||||
let sig = ed25519_dalek::Signature::from_bytes(&sig_bytes);
|
||||
self.public
|
||||
.verify(msg, &sig)
|
||||
.map_err(|_| CryptoError::VerificationFailed)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ed25519-dalek")]
|
||||
pub fn verify_ed25519(
|
||||
public_key: &SignaturePublicKey,
|
||||
msg: &[u8],
|
||||
signature: &[u8],
|
||||
) -> Result<(), CryptoError> {
|
||||
use ed25519_dalek::Verifier;
|
||||
|
||||
let pub_bytes: [u8; 32] = public_key
|
||||
.as_bytes()
|
||||
.try_into()
|
||||
.map_err(|_| CryptoError::InvalidSignature)?;
|
||||
let public = ed25519_dalek::VerifyingKey::from_bytes(&pub_bytes)
|
||||
.map_err(|_| CryptoError::InvalidSignature)?;
|
||||
let sig_bytes: [u8; 64] = signature
|
||||
.try_into()
|
||||
.map_err(|_| CryptoError::InvalidSignature)?;
|
||||
let sig = ed25519_dalek::Signature::from_bytes(&sig_bytes);
|
||||
|
||||
public
|
||||
.verify(msg, &sig)
|
||||
.map_err(|_| CryptoError::VerificationFailed)
|
||||
}
|
||||
|
||||
#[cfg(feature = "ml-dsa")]
|
||||
pub struct MlDsaSigner {
|
||||
secret: ml_dsa::SigningKey<ml_dsa::MlDsa65>,
|
||||
public: ml_dsa::VerifyingKey<ml_dsa::MlDsa65>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "ml-dsa")]
|
||||
impl MlDsaSigner {
|
||||
pub fn new(
|
||||
secret_key: &SignaturePqPrivateKey,
|
||||
public_key: &SignaturePqPublicKey,
|
||||
) -> Result<Self, CryptoError> {
|
||||
let encoded_sk =
|
||||
ml_dsa::EncodedSigningKey::<ml_dsa::MlDsa65>::try_from(secret_key.as_bytes())
|
||||
.map_err(|_| CryptoError::KeyGenerationFailed)?;
|
||||
let secret = ml_dsa::SigningKey::<ml_dsa::MlDsa65>::decode(&encoded_sk);
|
||||
let encoded_pk =
|
||||
ml_dsa::EncodedVerifyingKey::<ml_dsa::MlDsa65>::try_from(public_key.as_bytes())
|
||||
.map_err(|_| CryptoError::KeyGenerationFailed)?;
|
||||
let public = ml_dsa::VerifyingKey::<ml_dsa::MlDsa65>::decode(&encoded_pk);
|
||||
Ok(Self { secret, public })
|
||||
}
|
||||
|
||||
pub fn generate() -> (Self, SignaturePqPrivateKey, SignaturePqPublicKey) {
|
||||
use ml_dsa::KeyGen;
|
||||
let kp = ml_dsa::MlDsa65::key_gen(&mut rand_core::OsRng);
|
||||
let secret = kp.signing_key().clone();
|
||||
let public = kp.verifying_key().clone();
|
||||
let priv_key = SignaturePqPrivateKey::new(secret.encode().to_vec());
|
||||
let pub_key = SignaturePqPublicKey::new(public.encode().to_vec());
|
||||
let signer = Self { secret, public };
|
||||
(signer, priv_key, pub_key)
|
||||
}
|
||||
|
||||
pub fn public_key(&self) -> SignaturePqPublicKey {
|
||||
SignaturePqPublicKey::new(self.public.encode().to_vec())
|
||||
}
|
||||
|
||||
pub fn verifying_key(&self) -> &ml_dsa::VerifyingKey<ml_dsa::MlDsa65> {
|
||||
&self.public
|
||||
}
|
||||
|
||||
pub fn signing_key(&self) -> &ml_dsa::SigningKey<ml_dsa::MlDsa65> {
|
||||
&self.secret
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ml-dsa")]
|
||||
impl SignatureScheme for MlDsaSigner {
|
||||
fn sign(&self, msg: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
use ml_dsa::signature::Signer;
|
||||
let signature = self.secret.sign(msg);
|
||||
Ok(signature.encode().to_vec())
|
||||
}
|
||||
|
||||
fn verify(&self, msg: &[u8], signature: &[u8]) -> Result<(), CryptoError> {
|
||||
use ml_dsa::signature::Verifier;
|
||||
let encoded_sig =
|
||||
ml_dsa::EncodedSignature::<ml_dsa::MlDsa65>::try_from(signature)
|
||||
.map_err(|_| CryptoError::InvalidSignature)?;
|
||||
let sig = ml_dsa::Signature::<ml_dsa::MlDsa65>::decode(&encoded_sig)
|
||||
.ok_or(CryptoError::InvalidSignature)?;
|
||||
self.public
|
||||
.verify(msg, &sig)
|
||||
.map_err(|_| CryptoError::VerificationFailed)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ml-dsa")]
|
||||
pub fn verify_ml_dsa(
|
||||
public_key: &SignaturePqPublicKey,
|
||||
msg: &[u8],
|
||||
signature: &[u8],
|
||||
) -> Result<(), CryptoError> {
|
||||
use ml_dsa::signature::Verifier;
|
||||
|
||||
let encoded_pk =
|
||||
ml_dsa::EncodedVerifyingKey::<ml_dsa::MlDsa65>::try_from(public_key.as_bytes())
|
||||
.map_err(|_| CryptoError::InvalidSignature)?;
|
||||
let public = ml_dsa::VerifyingKey::<ml_dsa::MlDsa65>::decode(&encoded_pk);
|
||||
let encoded_sig =
|
||||
ml_dsa::EncodedSignature::<ml_dsa::MlDsa65>::try_from(signature)
|
||||
.map_err(|_| CryptoError::InvalidSignature)?;
|
||||
let sig = ml_dsa::Signature::<ml_dsa::MlDsa65>::decode(&encoded_sig)
|
||||
.ok_or(CryptoError::InvalidSignature)?;
|
||||
|
||||
public
|
||||
.verify(msg, &sig)
|
||||
.map_err(|_| CryptoError::VerificationFailed)
|
||||
}
|
||||
|
||||
pub struct DualSignature {
|
||||
pub ed25519: Vec<u8>,
|
||||
pub mldsa: Vec<u8>,
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "ed25519-dalek", feature = "ml-dsa"))]
|
||||
pub fn sign_dual(
|
||||
ed25519_sk: &ed25519_dalek::SigningKey,
|
||||
mldsa_sk: &ml_dsa::SigningKey<ml_dsa::MlDsa65>,
|
||||
message: &[u8],
|
||||
) -> DualSignature {
|
||||
use ed25519_dalek::Signer;
|
||||
|
||||
DualSignature {
|
||||
ed25519: ed25519_sk.sign(message).to_bytes().to_vec(),
|
||||
mldsa: mldsa_sk.sign(message).encode().to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
impl DualSignature {
|
||||
#[cfg(all(feature = "ed25519-dalek", feature = "ml-dsa"))]
|
||||
pub fn verify(
|
||||
&self,
|
||||
ed25519_vk: &ed25519_dalek::VerifyingKey,
|
||||
mldsa_vk: &ml_dsa::VerifyingKey<ml_dsa::MlDsa65>,
|
||||
message: &[u8],
|
||||
) -> Result<(), CryptoError> {
|
||||
use ed25519_dalek::Verifier;
|
||||
|
||||
let ed_sig = ed25519_dalek::Signature::from_slice(&self.ed25519)
|
||||
.map_err(|_| CryptoError::InvalidSignature)?;
|
||||
ed25519_vk
|
||||
.verify(message, &ed_sig)
|
||||
.map_err(|_| CryptoError::VerificationFailed)?;
|
||||
|
||||
let encoded_sig =
|
||||
ml_dsa::EncodedSignature::<ml_dsa::MlDsa65>::try_from(self.mldsa.as_slice())
|
||||
.map_err(|_| CryptoError::InvalidSignature)?;
|
||||
let ml_sig = ml_dsa::Signature::<ml_dsa::MlDsa65>::decode(&encoded_sig)
|
||||
.ok_or(CryptoError::InvalidSignature)?;
|
||||
mldsa_vk
|
||||
.verify(message, &ml_sig)
|
||||
.map_err(|_| CryptoError::VerificationFailed)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue