changes
This commit is contained in:
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[target.wasm32-unknown-unknown]
|
||||||
|
rustflags = ['--cfg', 'getrandom_backend="wasm_js"']
|
||||||
19
Cargo.toml
19
Cargo.toml
@@ -6,9 +6,16 @@ description = "Post-quantum OPAQUE implementation using lattice-based cryptograp
|
|||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
pqcrypto-kyber = { version = "0.8", features = ["serialization"] }
|
# Native backend (C FFI - faster but not WASM compatible)
|
||||||
pqcrypto-dilithium = { version = "0.5", features = ["serialization"] }
|
pqcrypto-kyber = { version = "0.8", features = ["serialization"], optional = true }
|
||||||
pqcrypto-traits = "0.3"
|
pqcrypto-dilithium = { version = "0.5", features = ["serialization"], optional = true }
|
||||||
|
pqcrypto-traits = { version = "0.3", optional = true }
|
||||||
|
|
||||||
|
# WASM backend (pure Rust - WASM compatible)
|
||||||
|
fips203 = { version = "0.4", default-features = false, features = ["ml-kem-768", "default-rng"], optional = true }
|
||||||
|
fips204 = { version = "0.4", default-features = false, features = ["ml-dsa-65", "default-rng"], optional = true }
|
||||||
|
getrandom_03 = { package = "getrandom", version = "0.3", features = ["wasm_js"], optional = true }
|
||||||
|
getrandom_02 = { package = "getrandom", version = "0.2", features = ["js"], optional = true }
|
||||||
|
|
||||||
sha2 = "0.10"
|
sha2 = "0.10"
|
||||||
sha3 = "0.10"
|
sha3 = "0.10"
|
||||||
@@ -42,7 +49,11 @@ name = "timing_verification"
|
|||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = ["native"]
|
||||||
|
# Native backend using pqcrypto (C FFI) - faster, not WASM compatible
|
||||||
|
native = ["dep:pqcrypto-kyber", "dep:pqcrypto-dilithium", "dep:pqcrypto-traits"]
|
||||||
|
# WASM backend using fips203/fips204 (pure Rust) - WASM compatible
|
||||||
|
wasm = ["dep:fips203", "dep:fips204", "dep:getrandom_03", "dep:getrandom_02"]
|
||||||
server = ["dep:axum", "dep:tokio", "dep:tower-http"]
|
server = ["dep:axum", "dep:tokio", "dep:tower-http"]
|
||||||
debug-trace = []
|
debug-trace = []
|
||||||
|
|
||||||
|
|||||||
BIN
pdfs/GitBookV2.pdf
Normal file
BIN
pdfs/GitBookV2.pdf
Normal file
Binary file not shown.
BIN
pdfs/access1_git.pdf
Normal file
BIN
pdfs/access1_git.pdf
Normal file
Binary file not shown.
BIN
pdfs/advanced_git.pdf
Normal file
BIN
pdfs/advanced_git.pdf
Normal file
Binary file not shown.
BIN
pdfs/git_it_princeton.pdf
Normal file
BIN
pdfs/git_it_princeton.pdf
Normal file
Binary file not shown.
@@ -1,97 +1,217 @@
|
|||||||
use pqcrypto_dilithium::dilithium3;
|
#[cfg(feature = "native")]
|
||||||
use pqcrypto_traits::sign::{DetachedSignature, PublicKey, SecretKey};
|
mod native {
|
||||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
use pqcrypto_dilithium::dilithium3;
|
||||||
|
use pqcrypto_traits::sign::{DetachedSignature, PublicKey, SecretKey};
|
||||||
|
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||||
|
|
||||||
use crate::error::{OpaqueError, Result};
|
use crate::error::{OpaqueError, Result};
|
||||||
use crate::types::{DILITHIUM_PK_LEN, DILITHIUM_SIG_LEN, DILITHIUM_SK_LEN};
|
use crate::types::{DILITHIUM_PK_LEN, DILITHIUM_SIG_LEN, DILITHIUM_SK_LEN};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DilithiumPublicKey(dilithium3::PublicKey);
|
pub struct DilithiumPublicKey(pub(crate) dilithium3::PublicKey);
|
||||||
|
|
||||||
impl DilithiumPublicKey {
|
impl DilithiumPublicKey {
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
||||||
if bytes.len() != DILITHIUM_PK_LEN {
|
if bytes.len() != DILITHIUM_PK_LEN {
|
||||||
return Err(OpaqueError::InvalidKeyLength {
|
return Err(OpaqueError::InvalidKeyLength {
|
||||||
expected: DILITHIUM_PK_LEN,
|
expected: DILITHIUM_PK_LEN,
|
||||||
got: bytes.len(),
|
got: bytes.len(),
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
dilithium3::PublicKey::from_bytes(bytes)
|
||||||
|
.map(Self)
|
||||||
|
.map_err(|_| OpaqueError::Deserialization("Invalid Dilithium public key".into()))
|
||||||
}
|
}
|
||||||
dilithium3::PublicKey::from_bytes(bytes)
|
|
||||||
.map(Self)
|
|
||||||
.map_err(|_| OpaqueError::Deserialization("Invalid Dilithium public key".into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn as_bytes(&self) -> Vec<u8> {
|
pub fn as_bytes(&self) -> Vec<u8> {
|
||||||
self.0.as_bytes().to_vec()
|
self.0.as_bytes().to_vec()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
|
|
||||||
pub struct DilithiumSecretKey {
|
|
||||||
#[zeroize(skip)]
|
|
||||||
inner: dilithium3::SecretKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DilithiumSecretKey {
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
|
||||||
if bytes.len() != DILITHIUM_SK_LEN {
|
|
||||||
return Err(OpaqueError::InvalidKeyLength {
|
|
||||||
expected: DILITHIUM_SK_LEN,
|
|
||||||
got: bytes.len(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
dilithium3::SecretKey::from_bytes(bytes)
|
|
||||||
.map(|sk| Self { inner: sk })
|
|
||||||
.map_err(|_| OpaqueError::Deserialization("Invalid Dilithium secret key".into()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
|
||||||
pub fn as_bytes(&self) -> Vec<u8> {
|
pub struct DilithiumSecretKey {
|
||||||
self.inner.as_bytes().to_vec()
|
#[zeroize(skip)]
|
||||||
|
pub(crate) inner: dilithium3::SecretKey,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
impl DilithiumSecretKey {
|
||||||
pub struct DilithiumSignature(dilithium3::DetachedSignature);
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
||||||
|
if bytes.len() != DILITHIUM_SK_LEN {
|
||||||
impl DilithiumSignature {
|
return Err(OpaqueError::InvalidKeyLength {
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
expected: DILITHIUM_SK_LEN,
|
||||||
if bytes.len() != DILITHIUM_SIG_LEN {
|
got: bytes.len(),
|
||||||
return Err(OpaqueError::InvalidKeyLength {
|
});
|
||||||
expected: DILITHIUM_SIG_LEN,
|
}
|
||||||
got: bytes.len(),
|
dilithium3::SecretKey::from_bytes(bytes)
|
||||||
});
|
.map(|sk| Self { inner: sk })
|
||||||
|
.map_err(|_| OpaqueError::Deserialization("Invalid Dilithium secret key".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn as_bytes(&self) -> Vec<u8> {
|
||||||
|
self.inner.as_bytes().to_vec()
|
||||||
}
|
}
|
||||||
dilithium3::DetachedSignature::from_bytes(bytes)
|
|
||||||
.map(Self)
|
|
||||||
.map_err(|_| OpaqueError::Deserialization("Invalid Dilithium signature".into()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[derive(Clone)]
|
||||||
pub fn as_bytes(&self) -> Vec<u8> {
|
pub struct DilithiumSignature(pub(crate) dilithium3::DetachedSignature);
|
||||||
self.0.as_bytes().to_vec()
|
|
||||||
|
impl DilithiumSignature {
|
||||||
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
||||||
|
if bytes.len() != DILITHIUM_SIG_LEN {
|
||||||
|
return Err(OpaqueError::InvalidKeyLength {
|
||||||
|
expected: DILITHIUM_SIG_LEN,
|
||||||
|
got: bytes.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
dilithium3::DetachedSignature::from_bytes(bytes)
|
||||||
|
.map(Self)
|
||||||
|
.map_err(|_| OpaqueError::Deserialization("Invalid Dilithium signature".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn as_bytes(&self) -> Vec<u8> {
|
||||||
|
self.0.as_bytes().to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_keypair() -> (DilithiumPublicKey, DilithiumSecretKey) {
|
||||||
|
let (pk, sk) = dilithium3::keypair();
|
||||||
|
(DilithiumPublicKey(pk), DilithiumSecretKey { inner: sk })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sign(message: &[u8], sk: &DilithiumSecretKey) -> DilithiumSignature {
|
||||||
|
let sig = dilithium3::detached_sign(message, &sk.inner);
|
||||||
|
DilithiumSignature(sig)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verify(message: &[u8], sig: &DilithiumSignature, pk: &DilithiumPublicKey) -> Result<()> {
|
||||||
|
dilithium3::verify_detached_signature(&sig.0, message, &pk.0)
|
||||||
|
.map_err(|_| OpaqueError::SignatureVerificationFailed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_keypair() -> (DilithiumPublicKey, DilithiumSecretKey) {
|
#[cfg(feature = "wasm")]
|
||||||
let (pk, sk) = dilithium3::keypair();
|
mod wasm {
|
||||||
(DilithiumPublicKey(pk), DilithiumSecretKey { inner: sk })
|
use fips204::ml_dsa_65;
|
||||||
|
use fips204::traits::{SerDes, Signer, Verifier};
|
||||||
|
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||||
|
|
||||||
|
use crate::error::{OpaqueError, Result};
|
||||||
|
use crate::types::{DILITHIUM_PK_LEN, DILITHIUM_SIG_LEN, DILITHIUM_SK_LEN};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct DilithiumPublicKey(pub(crate) ml_dsa_65::PublicKey);
|
||||||
|
|
||||||
|
impl DilithiumPublicKey {
|
||||||
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
||||||
|
if bytes.len() != DILITHIUM_PK_LEN {
|
||||||
|
return Err(OpaqueError::InvalidKeyLength {
|
||||||
|
expected: DILITHIUM_PK_LEN,
|
||||||
|
got: bytes.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let arr: [u8; DILITHIUM_PK_LEN] = bytes
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| OpaqueError::Deserialization("Invalid Dilithium public key".into()))?;
|
||||||
|
ml_dsa_65::PublicKey::try_from_bytes(arr)
|
||||||
|
.map(Self)
|
||||||
|
.map_err(|_| OpaqueError::Deserialization("Invalid Dilithium public key".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn as_bytes(&self) -> Vec<u8> {
|
||||||
|
self.0.clone().into_bytes().to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
|
||||||
|
pub struct DilithiumSecretKey {
|
||||||
|
#[zeroize(skip)]
|
||||||
|
pub(crate) inner: ml_dsa_65::PrivateKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DilithiumSecretKey {
|
||||||
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
||||||
|
if bytes.len() != DILITHIUM_SK_LEN {
|
||||||
|
return Err(OpaqueError::InvalidKeyLength {
|
||||||
|
expected: DILITHIUM_SK_LEN,
|
||||||
|
got: bytes.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let arr: [u8; DILITHIUM_SK_LEN] = bytes
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| OpaqueError::Deserialization("Invalid Dilithium secret key".into()))?;
|
||||||
|
ml_dsa_65::PrivateKey::try_from_bytes(arr)
|
||||||
|
.map(|sk| Self { inner: sk })
|
||||||
|
.map_err(|_| OpaqueError::Deserialization("Invalid Dilithium secret key".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn as_bytes(&self) -> Vec<u8> {
|
||||||
|
self.inner.clone().into_bytes().to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct DilithiumSignature(pub(crate) [u8; DILITHIUM_SIG_LEN]);
|
||||||
|
|
||||||
|
impl DilithiumSignature {
|
||||||
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
||||||
|
if bytes.len() != DILITHIUM_SIG_LEN {
|
||||||
|
return Err(OpaqueError::InvalidKeyLength {
|
||||||
|
expected: DILITHIUM_SIG_LEN,
|
||||||
|
got: bytes.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let arr: [u8; DILITHIUM_SIG_LEN] = bytes
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| OpaqueError::Deserialization("Invalid Dilithium signature".into()))?;
|
||||||
|
Ok(Self(arr))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn as_bytes(&self) -> Vec<u8> {
|
||||||
|
self.0.to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_keypair() -> (DilithiumPublicKey, DilithiumSecretKey) {
|
||||||
|
let (pk, sk) = ml_dsa_65::try_keygen().expect("keygen should not fail with good RNG");
|
||||||
|
(DilithiumPublicKey(pk), DilithiumSecretKey { inner: sk })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sign(message: &[u8], sk: &DilithiumSecretKey) -> DilithiumSignature {
|
||||||
|
let sig = sk
|
||||||
|
.inner
|
||||||
|
.try_sign(message, &[])
|
||||||
|
.expect("signing should not fail");
|
||||||
|
DilithiumSignature(sig)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verify(message: &[u8], sig: &DilithiumSignature, pk: &DilithiumPublicKey) -> Result<()> {
|
||||||
|
if pk.0.verify(message, &sig.0, &[]) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(OpaqueError::SignatureVerificationFailed)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sign(message: &[u8], sk: &DilithiumSecretKey) -> DilithiumSignature {
|
#[cfg(all(feature = "native", feature = "wasm"))]
|
||||||
let sig = dilithium3::detached_sign(message, &sk.inner);
|
compile_error!("Features 'native' and 'wasm' are mutually exclusive. Enable only one.");
|
||||||
DilithiumSignature(sig)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn verify(message: &[u8], sig: &DilithiumSignature, pk: &DilithiumPublicKey) -> Result<()> {
|
#[cfg(all(feature = "native", not(feature = "wasm")))]
|
||||||
dilithium3::verify_detached_signature(&sig.0, message, &pk.0)
|
pub use native::*;
|
||||||
.map_err(|_| OpaqueError::SignatureVerificationFailed)
|
|
||||||
}
|
#[cfg(all(feature = "wasm", not(feature = "native")))]
|
||||||
|
pub use wasm::*;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::types::{DILITHIUM_PK_LEN, DILITHIUM_SIG_LEN, DILITHIUM_SK_LEN};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_keypair_generation() {
|
fn test_keypair_generation() {
|
||||||
@@ -138,7 +258,7 @@ mod tests {
|
|||||||
let bytes = sig.as_bytes();
|
let bytes = sig.as_bytes();
|
||||||
assert_eq!(bytes.len(), DILITHIUM_SIG_LEN);
|
assert_eq!(bytes.len(), DILITHIUM_SIG_LEN);
|
||||||
|
|
||||||
let sig2 = DilithiumSignature::from_bytes(&bytes).unwrap();
|
let sig2 = DilithiumSignature::from_bytes(&bytes).expect("deserialization should succeed");
|
||||||
assert_eq!(sig.as_bytes(), sig2.as_bytes());
|
assert_eq!(sig.as_bytes(), sig2.as_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,7 +266,7 @@ mod tests {
|
|||||||
fn test_public_key_serialization() {
|
fn test_public_key_serialization() {
|
||||||
let (pk, _) = generate_keypair();
|
let (pk, _) = generate_keypair();
|
||||||
let bytes = pk.as_bytes();
|
let bytes = pk.as_bytes();
|
||||||
let pk2 = DilithiumPublicKey::from_bytes(&bytes).unwrap();
|
let pk2 = DilithiumPublicKey::from_bytes(&bytes).expect("deserialization should succeed");
|
||||||
assert_eq!(pk.as_bytes(), pk2.as_bytes());
|
assert_eq!(pk.as_bytes(), pk2.as_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
326
src/ake/kyber.rs
326
src/ake/kyber.rs
@@ -1,118 +1,258 @@
|
|||||||
use pqcrypto_kyber::kyber768;
|
#[cfg(feature = "native")]
|
||||||
use pqcrypto_traits::kem::{Ciphertext, PublicKey, SecretKey, SharedSecret};
|
mod native {
|
||||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
use pqcrypto_kyber::kyber768;
|
||||||
|
use pqcrypto_traits::kem::{Ciphertext, PublicKey, SecretKey, SharedSecret};
|
||||||
|
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||||
|
|
||||||
use crate::error::{OpaqueError, Result};
|
use crate::error::{OpaqueError, Result};
|
||||||
use crate::types::{KYBER_CT_LEN, KYBER_PK_LEN, KYBER_SK_LEN, KYBER_SS_LEN};
|
use crate::types::{KYBER_CT_LEN, KYBER_PK_LEN, KYBER_SK_LEN, KYBER_SS_LEN};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct KyberPublicKey(kyber768::PublicKey);
|
pub struct KyberPublicKey(pub(crate) kyber768::PublicKey);
|
||||||
|
|
||||||
impl KyberPublicKey {
|
impl KyberPublicKey {
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
||||||
if bytes.len() != KYBER_PK_LEN {
|
if bytes.len() != KYBER_PK_LEN {
|
||||||
return Err(OpaqueError::InvalidKeyLength {
|
return Err(OpaqueError::InvalidKeyLength {
|
||||||
expected: KYBER_PK_LEN,
|
expected: KYBER_PK_LEN,
|
||||||
got: bytes.len(),
|
got: bytes.len(),
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
kyber768::PublicKey::from_bytes(bytes)
|
||||||
|
.map(Self)
|
||||||
|
.map_err(|_| OpaqueError::Deserialization("Invalid Kyber public key".into()))
|
||||||
}
|
}
|
||||||
kyber768::PublicKey::from_bytes(bytes)
|
|
||||||
.map(Self)
|
|
||||||
.map_err(|_| OpaqueError::Deserialization("Invalid Kyber public key".into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn as_bytes(&self) -> Vec<u8> {
|
pub fn as_bytes(&self) -> Vec<u8> {
|
||||||
self.0.as_bytes().to_vec()
|
self.0.as_bytes().to_vec()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
|
|
||||||
pub struct KyberSecretKey {
|
|
||||||
#[zeroize(skip)]
|
|
||||||
inner: kyber768::SecretKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KyberSecretKey {
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
|
||||||
if bytes.len() != KYBER_SK_LEN {
|
|
||||||
return Err(OpaqueError::InvalidKeyLength {
|
|
||||||
expected: KYBER_SK_LEN,
|
|
||||||
got: bytes.len(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
kyber768::SecretKey::from_bytes(bytes)
|
|
||||||
.map(|sk| Self { inner: sk })
|
|
||||||
.map_err(|_| OpaqueError::Deserialization("Invalid Kyber secret key".into()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
|
||||||
pub fn as_bytes(&self) -> Vec<u8> {
|
pub struct KyberSecretKey {
|
||||||
self.inner.as_bytes().to_vec()
|
#[zeroize(skip)]
|
||||||
|
pub(crate) inner: kyber768::SecretKey,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
impl KyberSecretKey {
|
||||||
pub struct KyberCiphertext(kyber768::Ciphertext);
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
||||||
|
if bytes.len() != KYBER_SK_LEN {
|
||||||
impl KyberCiphertext {
|
return Err(OpaqueError::InvalidKeyLength {
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
expected: KYBER_SK_LEN,
|
||||||
if bytes.len() != KYBER_CT_LEN {
|
got: bytes.len(),
|
||||||
return Err(OpaqueError::InvalidKeyLength {
|
});
|
||||||
expected: KYBER_CT_LEN,
|
}
|
||||||
got: bytes.len(),
|
kyber768::SecretKey::from_bytes(bytes)
|
||||||
});
|
.map(|sk| Self { inner: sk })
|
||||||
|
.map_err(|_| OpaqueError::Deserialization("Invalid Kyber secret key".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn as_bytes(&self) -> Vec<u8> {
|
||||||
|
self.inner.as_bytes().to_vec()
|
||||||
}
|
}
|
||||||
kyber768::Ciphertext::from_bytes(bytes)
|
|
||||||
.map(Self)
|
|
||||||
.map_err(|_| OpaqueError::Deserialization("Invalid Kyber ciphertext".into()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[derive(Clone)]
|
||||||
pub fn as_bytes(&self) -> Vec<u8> {
|
pub struct KyberCiphertext(pub(crate) kyber768::Ciphertext);
|
||||||
self.0.as_bytes().to_vec()
|
|
||||||
|
impl KyberCiphertext {
|
||||||
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
||||||
|
if bytes.len() != KYBER_CT_LEN {
|
||||||
|
return Err(OpaqueError::InvalidKeyLength {
|
||||||
|
expected: KYBER_CT_LEN,
|
||||||
|
got: bytes.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
kyber768::Ciphertext::from_bytes(bytes)
|
||||||
|
.map(Self)
|
||||||
|
.map_err(|_| OpaqueError::Deserialization("Invalid Kyber ciphertext".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn as_bytes(&self) -> Vec<u8> {
|
||||||
|
self.0.as_bytes().to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
|
||||||
|
pub struct KyberSharedSecret {
|
||||||
|
#[zeroize(skip)]
|
||||||
|
pub(crate) inner: kyber768::SharedSecret,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KyberSharedSecret {
|
||||||
|
#[must_use]
|
||||||
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
|
self.inner.as_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn to_array(&self) -> [u8; KYBER_SS_LEN] {
|
||||||
|
let bytes = self.inner.as_bytes();
|
||||||
|
let mut arr = [0u8; KYBER_SS_LEN];
|
||||||
|
arr.copy_from_slice(bytes);
|
||||||
|
arr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_keypair() -> (KyberPublicKey, KyberSecretKey) {
|
||||||
|
let (pk, sk) = kyber768::keypair();
|
||||||
|
(KyberPublicKey(pk), KyberSecretKey { inner: sk })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encapsulate(pk: &KyberPublicKey) -> Result<(KyberSharedSecret, KyberCiphertext)> {
|
||||||
|
let (ss, ct) = kyber768::encapsulate(&pk.0);
|
||||||
|
Ok((KyberSharedSecret { inner: ss }, KyberCiphertext(ct)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decapsulate(ct: &KyberCiphertext, sk: &KyberSecretKey) -> Result<KyberSharedSecret> {
|
||||||
|
let ss = kyber768::decapsulate(&ct.0, &sk.inner);
|
||||||
|
Ok(KyberSharedSecret { inner: ss })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
|
#[cfg(feature = "wasm")]
|
||||||
pub struct KyberSharedSecret {
|
mod wasm {
|
||||||
#[zeroize(skip)]
|
use fips203::ml_kem_768;
|
||||||
inner: kyber768::SharedSecret,
|
use fips203::traits::{Decaps, Encaps, KeyGen, SerDes};
|
||||||
}
|
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||||
|
|
||||||
impl KyberSharedSecret {
|
use crate::error::{OpaqueError, Result};
|
||||||
#[must_use]
|
use crate::types::{KYBER_CT_LEN, KYBER_PK_LEN, KYBER_SK_LEN, KYBER_SS_LEN};
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
|
||||||
self.inner.as_bytes()
|
#[derive(Clone)]
|
||||||
|
pub struct KyberPublicKey(pub(crate) ml_kem_768::EncapsKey);
|
||||||
|
|
||||||
|
impl KyberPublicKey {
|
||||||
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
||||||
|
if bytes.len() != KYBER_PK_LEN {
|
||||||
|
return Err(OpaqueError::InvalidKeyLength {
|
||||||
|
expected: KYBER_PK_LEN,
|
||||||
|
got: bytes.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let arr: [u8; KYBER_PK_LEN] = bytes
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| OpaqueError::Deserialization("Invalid Kyber public key".into()))?;
|
||||||
|
ml_kem_768::EncapsKey::try_from_bytes(arr)
|
||||||
|
.map(Self)
|
||||||
|
.map_err(|_| OpaqueError::Deserialization("Invalid Kyber public key".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn as_bytes(&self) -> Vec<u8> {
|
||||||
|
self.0.clone().into_bytes().to_vec()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
|
||||||
pub fn to_array(&self) -> [u8; KYBER_SS_LEN] {
|
pub struct KyberSecretKey {
|
||||||
let bytes = self.inner.as_bytes();
|
#[zeroize(skip)]
|
||||||
let mut arr = [0u8; KYBER_SS_LEN];
|
pub(crate) inner: ml_kem_768::DecapsKey,
|
||||||
arr.copy_from_slice(bytes);
|
}
|
||||||
arr
|
|
||||||
|
impl KyberSecretKey {
|
||||||
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
||||||
|
if bytes.len() != KYBER_SK_LEN {
|
||||||
|
return Err(OpaqueError::InvalidKeyLength {
|
||||||
|
expected: KYBER_SK_LEN,
|
||||||
|
got: bytes.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let arr: [u8; KYBER_SK_LEN] = bytes
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| OpaqueError::Deserialization("Invalid Kyber secret key".into()))?;
|
||||||
|
ml_kem_768::DecapsKey::try_from_bytes(arr)
|
||||||
|
.map(|sk| Self { inner: sk })
|
||||||
|
.map_err(|_| OpaqueError::Deserialization("Invalid Kyber secret key".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn as_bytes(&self) -> Vec<u8> {
|
||||||
|
self.inner.clone().into_bytes().to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct KyberCiphertext(pub(crate) ml_kem_768::CipherText);
|
||||||
|
|
||||||
|
impl KyberCiphertext {
|
||||||
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
||||||
|
if bytes.len() != KYBER_CT_LEN {
|
||||||
|
return Err(OpaqueError::InvalidKeyLength {
|
||||||
|
expected: KYBER_CT_LEN,
|
||||||
|
got: bytes.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let arr: [u8; KYBER_CT_LEN] = bytes
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| OpaqueError::Deserialization("Invalid Kyber ciphertext".into()))?;
|
||||||
|
ml_kem_768::CipherText::try_from_bytes(arr)
|
||||||
|
.map(Self)
|
||||||
|
.map_err(|_| OpaqueError::Deserialization("Invalid Kyber ciphertext".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn as_bytes(&self) -> Vec<u8> {
|
||||||
|
self.0.clone().into_bytes().to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
|
||||||
|
pub struct KyberSharedSecret {
|
||||||
|
pub(crate) inner: [u8; KYBER_SS_LEN],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KyberSharedSecret {
|
||||||
|
#[must_use]
|
||||||
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn to_array(&self) -> [u8; KYBER_SS_LEN] {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_keypair() -> (KyberPublicKey, KyberSecretKey) {
|
||||||
|
let (ek, dk) = ml_kem_768::KG::try_keygen().expect("keygen should not fail with good RNG");
|
||||||
|
(KyberPublicKey(ek), KyberSecretKey { inner: dk })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encapsulate(pk: &KyberPublicKey) -> Result<(KyberSharedSecret, KyberCiphertext)> {
|
||||||
|
let (ssk, ct) =
|
||||||
|
pk.0.try_encaps()
|
||||||
|
.map_err(|_| OpaqueError::EncapsulationFailed)?;
|
||||||
|
let ss_bytes: [u8; KYBER_SS_LEN] = ssk.into_bytes().into();
|
||||||
|
Ok((KyberSharedSecret { inner: ss_bytes }, KyberCiphertext(ct)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decapsulate(ct: &KyberCiphertext, sk: &KyberSecretKey) -> Result<KyberSharedSecret> {
|
||||||
|
let ssk = sk
|
||||||
|
.inner
|
||||||
|
.try_decaps(&ct.0)
|
||||||
|
.map_err(|_| OpaqueError::DecapsulationFailed)?;
|
||||||
|
let ss_bytes: [u8; KYBER_SS_LEN] = ssk.into_bytes().into();
|
||||||
|
Ok(KyberSharedSecret { inner: ss_bytes })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_keypair() -> (KyberPublicKey, KyberSecretKey) {
|
#[cfg(all(feature = "native", feature = "wasm"))]
|
||||||
let (pk, sk) = kyber768::keypair();
|
compile_error!("Features 'native' and 'wasm' are mutually exclusive. Enable only one.");
|
||||||
(KyberPublicKey(pk), KyberSecretKey { inner: sk })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encapsulate(pk: &KyberPublicKey) -> Result<(KyberSharedSecret, KyberCiphertext)> {
|
#[cfg(all(feature = "native", not(feature = "wasm")))]
|
||||||
let (ss, ct) = kyber768::encapsulate(&pk.0);
|
pub use native::*;
|
||||||
Ok((KyberSharedSecret { inner: ss }, KyberCiphertext(ct)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decapsulate(ct: &KyberCiphertext, sk: &KyberSecretKey) -> Result<KyberSharedSecret> {
|
#[cfg(all(feature = "wasm", not(feature = "native")))]
|
||||||
let ss = kyber768::decapsulate(&ct.0, &sk.inner);
|
pub use wasm::*;
|
||||||
Ok(KyberSharedSecret { inner: ss })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::types::{KYBER_CT_LEN, KYBER_PK_LEN, KYBER_SK_LEN, KYBER_SS_LEN};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_keypair_generation() {
|
fn test_keypair_generation() {
|
||||||
@@ -125,8 +265,8 @@ mod tests {
|
|||||||
fn test_encapsulate_decapsulate() {
|
fn test_encapsulate_decapsulate() {
|
||||||
let (pk, sk) = generate_keypair();
|
let (pk, sk) = generate_keypair();
|
||||||
|
|
||||||
let (ss1, ct) = encapsulate(&pk).unwrap();
|
let (ss1, ct) = encapsulate(&pk).expect("encapsulation should succeed");
|
||||||
let ss2 = decapsulate(&ct, &sk).unwrap();
|
let ss2 = decapsulate(&ct, &sk).expect("decapsulation should succeed");
|
||||||
|
|
||||||
assert_eq!(ss1.as_bytes(), ss2.as_bytes());
|
assert_eq!(ss1.as_bytes(), ss2.as_bytes());
|
||||||
assert_eq!(ss1.as_bytes().len(), KYBER_SS_LEN);
|
assert_eq!(ss1.as_bytes().len(), KYBER_SS_LEN);
|
||||||
@@ -136,7 +276,7 @@ mod tests {
|
|||||||
fn test_public_key_serialization() {
|
fn test_public_key_serialization() {
|
||||||
let (pk, _) = generate_keypair();
|
let (pk, _) = generate_keypair();
|
||||||
let bytes = pk.as_bytes();
|
let bytes = pk.as_bytes();
|
||||||
let pk2 = KyberPublicKey::from_bytes(&bytes).unwrap();
|
let pk2 = KyberPublicKey::from_bytes(&bytes).expect("deserialization should succeed");
|
||||||
assert_eq!(pk.as_bytes(), pk2.as_bytes());
|
assert_eq!(pk.as_bytes(), pk2.as_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,16 +284,16 @@ mod tests {
|
|||||||
fn test_secret_key_serialization() {
|
fn test_secret_key_serialization() {
|
||||||
let (_, sk) = generate_keypair();
|
let (_, sk) = generate_keypair();
|
||||||
let bytes = sk.as_bytes();
|
let bytes = sk.as_bytes();
|
||||||
let sk2 = KyberSecretKey::from_bytes(&bytes).unwrap();
|
let sk2 = KyberSecretKey::from_bytes(&bytes).expect("deserialization should succeed");
|
||||||
assert_eq!(sk.as_bytes(), sk2.as_bytes());
|
assert_eq!(sk.as_bytes(), sk2.as_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ciphertext_serialization() {
|
fn test_ciphertext_serialization() {
|
||||||
let (pk, _) = generate_keypair();
|
let (pk, _) = generate_keypair();
|
||||||
let (_, ct) = encapsulate(&pk).unwrap();
|
let (_, ct) = encapsulate(&pk).expect("encapsulation should succeed");
|
||||||
let bytes = ct.as_bytes();
|
let bytes = ct.as_bytes();
|
||||||
let ct2 = KyberCiphertext::from_bytes(&bytes).unwrap();
|
let ct2 = KyberCiphertext::from_bytes(&bytes).expect("deserialization should succeed");
|
||||||
assert_eq!(ct.as_bytes(), ct2.as_bytes());
|
assert_eq!(ct.as_bytes(), ct2.as_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user