use riptering::kdf::{HkdfParams, Pbkdf2Params};
use riptering::{
AesKeySize, EcCurve, HashAlgorithm, KeyAlgorithm, KeyWrapAlgorithm, Operation,
SignatureAlgorithm, SoftwareKey, SoftwareSigner, SoftwareVerifier,
};
use riptering::{Signer, Verifier};
fn decode(hex_value: &str) -> Vec<u8> {
hex::decode(hex_value).expect("valid test vector")
}
fn assert_refused<T>(result: riptering::Result<T>, operation: Operation) {
match result {
Err(riptering::Error::UnsupportedAlgorithm {
operation: actual, ..
}) if actual == operation => {}
Err(error) => panic!("{operation:?} failed without being refused: {error}"),
Ok(_) => panic!("{operation:?} was accepted"),
}
}
#[test]
fn raw_symmetric_import_rejects_asymmetric_families() {
riptering::initialize_backend().expect("provider initialization");
for algorithm in [
KeyAlgorithm::Rsa,
KeyAlgorithm::Ec(EcCurve::P256),
KeyAlgorithm::Ed25519,
KeyAlgorithm::X25519,
KeyAlgorithm::Dh,
] {
assert!(
SoftwareKey::from_symmetric_bytes(algorithm, b"not a raw symmetric key").is_err(),
"{algorithm:?} accepted raw bytes"
);
}
assert!(SoftwareKey::from_symmetric_bytes(KeyAlgorithm::Hmac, b"secret").is_ok());
assert!(SoftwareKey::from_symmetric_bytes(KeyAlgorithm::Aes, &[0; 32]).is_ok());
assert!(SoftwareKey::from_symmetric_bytes(KeyAlgorithm::Aes, &[0; 20]).is_err());
}
#[test]
fn x25519_agreement_matches_rfc7748_and_checks_key_type() {
riptering::initialize_backend().expect("provider initialization");
let alice_private = decode("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
let alice_public = decode("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a");
let bob_public = decode("de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f");
let shared = decode("4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742");
let aes = SoftwareKey::from_symmetric_bytes(KeyAlgorithm::Aes, &[7; 32]).unwrap();
if cfg!(feature = "fips") {
assert_refused(
riptering::keyagreement::ecdh_x25519(&bob_public, &alice_private),
Operation::X25519Agreement,
);
assert_refused(
SoftwareKey::from_x25519(Some(&alice_private), &alice_public),
Operation::KeyImport(KeyAlgorithm::X25519),
);
assert_refused(
riptering::keyagreement::agree_x25519(&bob_public, &aes),
Operation::X25519Agreement,
);
} else {
assert_eq!(
riptering::keyagreement::ecdh_x25519(&bob_public, &alice_private).unwrap(),
shared
);
let alice = SoftwareKey::from_x25519(Some(&alice_private), &alice_public).unwrap();
assert_eq!(
riptering::keyagreement::agree_x25519(&bob_public, &alice).unwrap(),
shared
);
assert!(riptering::keyagreement::agree_x25519(&bob_public, &aes).is_err());
assert!(riptering::keyagreement::ecdh_x25519(&bob_public, &alice_private[..31]).is_err());
}
}
#[test]
fn signers_reject_keys_of_another_family() {
riptering::initialize_backend().expect("provider initialization");
let hmac = SoftwareKey::from_symmetric_bytes(KeyAlgorithm::Hmac, b"secret").unwrap();
for algorithm in [
SignatureAlgorithm::RsaPkcs1v15(HashAlgorithm::Sha256),
SignatureAlgorithm::Ecdsa(EcCurve::P256, HashAlgorithm::Sha256),
SignatureAlgorithm::Ed25519,
] {
assert!(SoftwareSigner::new(algorithm, hmac.clone()).is_err());
assert!(SoftwareVerifier::new(algorithm, hmac.clone()).is_err());
}
if cfg!(feature = "fips") {
assert_refused(
SoftwareSigner::new(SignatureAlgorithm::Ed25519, hmac.clone()),
Operation::Sign(SignatureAlgorithm::Ed25519),
);
assert_refused(
SoftwareVerifier::new(SignatureAlgorithm::Ed25519, hmac),
Operation::Verify(SignatureAlgorithm::Ed25519),
);
}
}
fn p256_der(raw: &[u8]) -> Vec<u8> {
p256::ecdsa::Signature::from_slice(raw)
.unwrap()
.to_der()
.as_bytes()
.to_vec()
}
#[test]
fn ecdsa_verify_accepts_the_same_encodings() {
use p256::pkcs8::{EncodePrivateKey, EncodePublicKey};
riptering::initialize_backend().expect("provider initialization");
let private = p256::SecretKey::random(&mut rand::rngs::OsRng);
let key = SoftwareKey::from_pkcs8_der(
KeyAlgorithm::Ec(EcCurve::P256),
private.to_pkcs8_der().unwrap().as_bytes(),
)
.unwrap();
let public = SoftwareKey::from_spki_der(
KeyAlgorithm::Ec(EcCurve::P256),
private.public_key().to_public_key_der().unwrap().as_bytes(),
)
.unwrap();
let algorithm = SignatureAlgorithm::Ecdsa(EcCurve::P256, HashAlgorithm::Sha256);
let raw = SoftwareSigner::new(algorithm, key)
.unwrap()
.sign(b"parity")
.unwrap();
assert_eq!(raw.len(), 64);
let verifier = SoftwareVerifier::new(algorithm, public).unwrap();
let mut padded = vec![0];
padded.extend_from_slice(&raw[..32]);
padded.push(0);
padded.extend_from_slice(&raw[32..]);
for (name, encoding) in [
("raw", raw.clone()),
("DER", p256_der(&raw)),
("padded", padded),
] {
assert!(verifier.verify(b"parity", &encoding).unwrap(), "{name}");
assert!(!verifier.verify(b"tampered", &encoding).unwrap(), "{name}");
}
assert!(verifier.verify(b"parity", &[0; 64]).is_err());
assert!(verifier.verify(b"parity", &raw[..63]).is_err());
}
#[test]
fn ecdsa_verifies_cross_curve_digest_pairs() {
use p384::ecdsa::signature::hazmat::PrehashSigner;
use p384::pkcs8::EncodePublicKey;
riptering::initialize_backend().expect("provider initialization");
let signing = p384::ecdsa::SigningKey::random(&mut rand::rngs::OsRng);
let public = SoftwareKey::from_spki_der(
KeyAlgorithm::Ec(EcCurve::P384),
signing
.verifying_key()
.to_public_key_der()
.unwrap()
.as_bytes(),
)
.unwrap();
let digest = riptering::digest::digest(HashAlgorithm::Sha256, b"cross").unwrap();
let signature: p384::ecdsa::Signature = signing.sign_prehash(&digest).unwrap();
let verifier = SoftwareVerifier::new(
SignatureAlgorithm::Ecdsa(EcCurve::P384, HashAlgorithm::Sha256),
public,
)
.unwrap();
assert!(verifier.verify(b"cross", &signature.to_bytes()).unwrap());
assert!(!verifier.verify(b"tampered", &signature.to_bytes()).unwrap());
}
#[test]
fn rsa_keys_below_2048_bits_are_rejected() {
use rsa::pkcs8::{EncodePrivateKey, EncodePublicKey};
riptering::initialize_backend().expect("provider initialization");
let private = rsa::RsaPrivateKey::new(&mut rand::rngs::OsRng, 1024).unwrap();
let spki = private.to_public_key().to_public_key_der().unwrap();
let pkcs8 = private.to_pkcs8_der().unwrap();
assert!(SoftwareKey::from_spki_der(KeyAlgorithm::Rsa, spki.as_bytes()).is_err());
assert!(SoftwareKey::from_pkcs8_der(KeyAlgorithm::Rsa, pkcs8.as_bytes()).is_err());
}
#[test]
fn aes_cbc_rejects_iv_only_input() {
riptering::initialize_backend().expect("provider initialization");
for size in [AesKeySize::Aes128, AesKeySize::Aes256] {
let key = vec![3; size.key_len()];
assert!(riptering::hazmat::aes_cbc::decrypt(size, &key, &[9; 16]).is_err());
let sealed = riptering::hazmat::aes_cbc::encrypt(size, &key, b"").unwrap();
assert_eq!(sealed.len(), 32);
assert!(riptering::hazmat::aes_cbc::decrypt(size, &key, &sealed)
.unwrap()
.is_empty());
}
}
#[test]
fn aes_key_wrap_matches_rfc3394_and_rejects_short_input() {
riptering::initialize_backend().expect("provider initialization");
let kek128 = decode("000102030405060708090A0B0C0D0E0F");
let kek256 = decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
let data = decode("00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F");
for (size, kek, input, expected) in [
(
AesKeySize::Aes128,
&kek128,
&data[..16],
"1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5",
),
(
AesKeySize::Aes256,
&kek256,
&data[..],
"28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD21",
),
] {
let algorithm = KeyWrapAlgorithm::AesKw(size);
let wrapped = riptering::keywrap::wrap(algorithm, kek, input).unwrap();
assert_eq!(wrapped, decode(expected));
assert_eq!(
riptering::keywrap::unwrap(algorithm, kek, &wrapped).unwrap(),
input
);
let mut tampered = wrapped.clone();
tampered[0] ^= 1;
assert!(riptering::keywrap::unwrap(algorithm, kek, &tampered).is_err());
assert!(riptering::keywrap::wrap(algorithm, kek, &[]).is_err());
assert!(riptering::keywrap::wrap(algorithm, kek, &[1; 8]).is_err());
assert!(riptering::keywrap::wrap(algorithm, kek, &[1; 20]).is_err());
assert!(riptering::keywrap::unwrap(algorithm, kek, &wrapped[..16]).is_err());
}
}
#[test]
fn kdfs_match_rfc_vectors() {
riptering::initialize_backend().expect("provider initialization");
let a2 = riptering::kdf::hkdf_derive(
&(0u8..=0x4f).collect::<Vec<_>>(),
82,
&HkdfParams {
hash: HashAlgorithm::Sha256,
salt: Some((0x60u8..=0xaf).collect()),
info: Some((0xb0u8..=0xff).collect()),
key_length_bits: 0,
},
)
.unwrap();
assert_eq!(
a2,
decode(
"b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c\
59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71\
cc30c58179ec3e87c14c01d5c1f3434f1d87"
)
);
let a3 = riptering::kdf::hkdf_derive(&[0x0b; 22], 42, &HkdfParams::default()).unwrap();
assert_eq!(
a3,
decode(
"8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d\
9d201395faa4b61a96c8"
)
);
assert!(riptering::kdf::hkdf_derive(&[1], 255 * 32 + 1, &HkdfParams::default()).is_err());
for (hash, len, expected) in [
(HashAlgorithm::Sha256, 64, "2ecc2dfd549e0925a0e4a0b860368e7492b6e65339188d3e1e9e43799b90ff64cf800fb11ff3602b51ed7c8c766643c32be8af72829b3146642d9ddbdf8d7d6d"),
(HashAlgorithm::Sha512, 40, "fee75217fa12304834340c9e672f9aaa9cc4bb229a9fd37edcdcd6ae57b42ad8ca5ea636c777c33c"),
] {
let output = riptering::kdf::pbkdf2_derive(
b"Password",
&Pbkdf2Params {
hash,
salt: b"NaCl1234".to_vec(),
iteration_count: 1000,
key_length: len,
},
);
if cfg!(feature = "fips") {
assert!(
matches!(
output,
Err(riptering::Error::Crypto(ref message))
if message.contains("SP 800-132")
),
"{hash:?}: {output:?}"
);
} else {
assert_eq!(output.unwrap(), decode(expected), "{hash:?}");
}
}
for (hash, len, expected) in [
(HashAlgorithm::Sha256, 64, "1e30cde84a0370317564c82ece78efb738195387129590d2fd3d7b48714a40c874de73f494b275b7147b58171cc17101b3cba6a0bd0a3766c839dd7e98f71aed"),
(HashAlgorithm::Sha512, 40, "20ae6a68e7f944784e186fa29c12c6ae0926c736f4cdb5f025becc9c70a0e12cf39145acd1ad6113"),
] {
let output = riptering::kdf::pbkdf2_derive(
b"PasswordPassword",
&Pbkdf2Params {
hash,
salt: b"NaCl1234NaCl1234".to_vec(),
iteration_count: 1000,
key_length: len,
},
)
.unwrap();
assert_eq!(output, decode(expected), "{hash:?}");
}
let recommended = Pbkdf2Params::recommended(HashAlgorithm::Sha512, b"salt".to_vec(), 32);
assert_eq!(recommended.hash, HashAlgorithm::Sha512);
assert_eq!(recommended.iteration_count, 210_000);
}