Skip to content

Commit

Permalink
keymanager: Add vector test for key manager's kdf
Browse files Browse the repository at this point in the history
  • Loading branch information
peternose committed Aug 26, 2022
1 parent 24c061d commit 2e028e0
Show file tree
Hide file tree
Showing 3 changed files with 232 additions and 64 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions keymanager-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ tiny-keccak = { version = "2.0.2", features = ["sha3"] }
x25519-dalek = "1.1.0"
tokio = { version = "1", features = ["rt"] }
zeroize = "1.4"
rustc-hex = "2.0.1"
294 changes: 230 additions & 64 deletions keymanager-lib/src/kdf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -535,25 +535,73 @@ impl Kdf {
#[cfg(test)]
mod tests {
use lru::LruCache;
use std::sync::{Arc, RwLock};
use rustc_hex::{FromHex, ToHex};
use std::{
collections::HashSet,
convert::TryInto,
sync::{Arc, RwLock},
};

use oasis_core_keymanager_api_common::{
EphemeralKeyRequest, KeyPairId, LongTermKeyRequest, MasterSecret, PublicKey,
PUBLIC_KEY_CONTEXT,
};
use oasis_core_runtime::common::{crypto::signature::PrivateKey, namespace::Namespace};

use super::{Inner, KeyRequest};
use super::{
Inner, KeyRequest, EPHEMERAL_KDF_CUSTOM, EPHEMERAL_XOF_CUSTOM, RUNTIME_KDF_CUSTOM,
RUNTIME_XOF_CUSTOM,
};
use crate::kdf::Kdf;

struct SimpleKeyRequest<'a> {
seed: &'a str,
kdf_custom: &'a [u8],
xof_custom: &'a [u8],
}

impl Default for SimpleKeyRequest<'_> {
fn default() -> Self {
Self {
seed: "8842befd88ea1952decf60f5c7430ee479b84a9b2472b2cc1fc796d35d5d71c3",
kdf_custom: b"kdf_custom",
xof_custom: b"xof_custom",
}
}
}

impl KeyRequest for SimpleKeyRequest<'_> {
fn seed(&self) -> Vec<u8> {
self.seed.from_hex().unwrap()
}

fn kdf_custom(&self) -> &[u8] {
self.kdf_custom
}

fn xof_custom(&self) -> &[u8] {
self.xof_custom
}
}

impl Default for Kdf {
fn default() -> Self {
Self {
inner: RwLock::new(Inner {
master_secret: Some(MasterSecret([1u8; 32])),
checksum: Some(vec![2u8; 32]),
runtime_id: Some(Namespace([3u8; 32])),
signer: Some(Arc::new(PrivateKey::from_bytes(vec![4u8; 32]))),
cache: LruCache::new(1),
}),
}
}
}

#[test]
fn key_generation_is_deterministic() {
let kdf = kdf();

let req = LongTermKeyRequest {
runtime_id: Namespace::from(vec![1u8; 32]),
key_pair_id: KeyPairId::from(vec![1u8; 32]),
};
let kdf = Kdf::default();
let req = SimpleKeyRequest::default();

let sk1 = kdf
.get_or_create_keys(&req)
Expand All @@ -564,87 +612,55 @@ mod tests {
.expect("private key should be created");

assert_eq!(sk1.input_keypair.sk.0, sk2.input_keypair.sk.0);
assert_eq!(sk1.input_keypair.pk.0, sk2.input_keypair.pk.0);
}

#[test]
fn private_keys_are_unique() {
let kdf = kdf();

let mut req = LongTermKeyRequest {
runtime_id: Namespace::from(vec![1u8; 32]),
key_pair_id: KeyPairId::from(vec![1u8; 32]),
};
let kdf = Kdf::default();
let mut req = SimpleKeyRequest::default();

let sk1 = kdf
.get_or_create_keys(&req)
.expect("private key should be created");

req.runtime_id = Namespace::from(vec![2u8; 32]);

req.seed = "eeffe4ab608f4adad8b5168163ab95fab43a818321ad49fba897fcb435097099";
let sk2 = kdf
.get_or_create_keys(&req)
.expect("private key should be created");

req.key_pair_id = KeyPairId::from(vec![2u8; 32]);

let sk3 = kdf
.get_or_create_keys(&req)
.expect("private key should be created");

assert_ne!(sk1.input_keypair.pk.0, sk2.input_keypair.sk.0);
assert_ne!(sk1.input_keypair.pk.0, sk3.input_keypair.sk.0);
assert_ne!(sk2.input_keypair.pk.0, sk3.input_keypair.sk.0);
assert_ne!(sk1.input_keypair.sk.0, sk2.input_keypair.sk.0);
assert_ne!(sk1.input_keypair.pk.0, sk2.input_keypair.pk.0);
}

#[test]
fn private_and_public_key_match() {
let kdf = kdf();

let req = LongTermKeyRequest {
runtime_id: Namespace::from(vec![1u8; 32]),
key_pair_id: KeyPairId::from(vec![1u8; 32]),
};
let kdf = Kdf::default();
let req = SimpleKeyRequest::default();

let sk1 = kdf
let sk = kdf
.get_or_create_keys(&req)
.expect("private key should be created");

let pk1 = kdf
let pk = kdf
.get_public_key(&req)
.unwrap()
.expect("public key should be fetched");

let req = EphemeralKeyRequest {
epoch: 1,
runtime_id: Namespace::from(vec![1u8; 32]),
key_pair_id: KeyPairId::from(vec![1u8; 32]),
};

let sk2 = kdf
.get_or_create_keys(&req)
.expect("private key should be created");

let pk2 = kdf
.get_public_key(&req)
.unwrap()
.expect("public key should be fetched");

assert_eq!(sk1.input_keypair.pk, pk1);
assert_eq!(sk2.input_keypair.pk, pk2);
assert_ne!(pk1, pk2);
assert_eq!(sk.input_keypair.pk, pk);
}

#[test]
fn public_key_signature_is_valid() {
let kdf = kdf();
let kdf = Kdf::default();

let pk = PublicKey::from(vec![1u8; 32]);
let sig = kdf
.sign_public_key(pk)
.expect("public key should be signed");

let mut body = pk.as_ref().to_vec();
let checksum = [2u8; 32];
let checksum = kdf.inner.into_inner().unwrap().checksum.unwrap();
body.extend_from_slice(&checksum);

let pk = PrivateKey::from_bytes(vec![4u8; 32]).public_key();
Expand All @@ -655,32 +671,182 @@ mod tests {

#[test]
fn master_secret_can_be_replicated() {
let kdf = kdf();
let kdf = Kdf::default();
let ms = kdf
.replicate_master_secret()
.expect("master secret should be replicated");

assert_eq!(ms.master_secret.0, [1u8; 32]);
assert_eq!(
ms.master_secret.0,
kdf.inner.into_inner().unwrap().master_secret.unwrap().0
);
}

#[test]
fn requests_have_unique_customs() {
// All key requests must have unique customs otherwise they can
// derivate keys from the same KMac and/or CShake!!!
let runtime_req = LongTermKeyRequest::default();
let ephemeral_req = EphemeralKeyRequest::default();

assert_ne!(runtime_req.xof_custom(), ephemeral_req.xof_custom());
assert_ne!(runtime_req.kdf_custom(), ephemeral_req.kdf_custom());
}

fn kdf() -> Kdf {
Kdf {
inner: RwLock::new(Inner {
master_secret: Some(MasterSecret([1u8; 32])),
checksum: Some(vec![2u8; 32]),
runtime_id: Some(Namespace([3u8; 32])),
signer: Some(Arc::new(PrivateKey::from_bytes(vec![4u8; 32]))),
cache: LruCache::new(1),
}),
#[test]
fn requests_generate_unique_seeds() {
// Default values.
let runtime_id = Namespace::from(vec![1u8; 32]);
let key_pair_id = KeyPairId::from(vec![1u8; 32]);
let epoch = 1;

// LongTermKeyRequest's seed should depend on runtime_id and
// key_pair_id.
let req1 = LongTermKeyRequest {
runtime_id,
key_pair_id,
};
let mut req2 = req1.clone();
let mut req3 = req1.clone();
req2.runtime_id = Namespace::from(vec![2u8; 32]);
req3.key_pair_id = KeyPairId::from(vec![3u8; 32]);

let mut seeds = HashSet::new();
seeds.insert(req1.seed());
seeds.insert(req2.seed());
seeds.insert(req3.seed());

assert_eq!(seeds.len(), 3);

// EphemeralKeyRequest's seed should depend on runtime_id, key_pair_id
// and epoch.
let req1 = EphemeralKeyRequest {
epoch,
runtime_id,
key_pair_id,
};
let mut req2 = req1.clone();
let mut req3 = req1.clone();
let mut req4 = req1.clone();
req2.runtime_id = Namespace::from(vec![2u8; 32]);
req3.key_pair_id = KeyPairId::from(vec![3u8; 32]);
req4.epoch = 2;

let mut seeds = HashSet::new();
seeds.insert(req1.seed());
seeds.insert(req2.seed());
seeds.insert(req3.seed());
seeds.insert(req4.seed());

assert_eq!(seeds.len(), 4);
}

#[test]
fn vector_test() {
struct TestVector<'a> {
master_secret: &'a str,
seed: &'a str,
kdf_custom: &'a [u8],
xof_custom: &'a [u8],
sk: &'a str,
pk: &'a str,
}

let kdf_custom = b"54761365b5a007c2bd13cf880a8a58263f56bb7057f7548b7467e871f6f4bb1f";
let xof_custom = b"a0caee5ecac1a287f06ff8748cf37e809f049cf6866d668ecdbed3ce05b40f2c";

let vectors = vec![
// A random vector.
TestVector {
master_secret: "3fa39161fe106fb770503558dad41bea3221888c09477d864507e615094f5d38",
seed: "2a9d51ed0380d11b26255ecc894dfc3aa48196c9b0da463a3073ea820a65a090",
kdf_custom: &RUNTIME_KDF_CUSTOM,
xof_custom: &RUNTIME_XOF_CUSTOM,
sk: "c818f97228a53b201e2afa383b64199c732c7ba1a67ac55ea2cb3b8fba367740",
pk: "fb99076f6a5a8c52bcf562cae5152d0410f47a615fc7bc4966dc9a4f477bd217",
},
// Different master secret.
TestVector {
master_secret: "54083e90f05860653161bc1575765b3311f6a55d1cab84919cb1e2b02c8351ec",
seed: "2a9d51ed0380d11b26255ecc894dfc3aa48196c9b0da463a3073ea820a65a090",
kdf_custom: &RUNTIME_KDF_CUSTOM,
xof_custom: &RUNTIME_XOF_CUSTOM,
sk: "b8092ab767ddc47cb56807d4d1fd0e66eae0b02e289d1e38d4dfa900619edc47",
pk: "6782329a12e821697f1d7cc44ba5644a2d81bd827fe09208549296be51da8b66",
},
// Different seed.
TestVector {
master_secret: "3fa39161fe106fb770503558dad41bea3221888c09477d864507e615094f5d38",
seed: "6cc9f7dfe776c7e531331eaf5fd8717d638cae65a4ebb0d6f128b1873f0f9c22",
kdf_custom: &RUNTIME_KDF_CUSTOM,
xof_custom: &RUNTIME_XOF_CUSTOM,
sk: "f0d5bf36b424f7b168fa37e1a1407f191a260dda149d78676d4ca077b1e8614c",
pk: "9099b1e19c482927eb2785c77b34ef2c4b95f812e089e3b2cd6f2a0a743b7561",
},
// Different kdf custom.
TestVector {
master_secret: "3fa39161fe106fb770503558dad41bea3221888c09477d864507e615094f5d38",
seed: "2a9d51ed0380d11b26255ecc894dfc3aa48196c9b0da463a3073ea820a65a090",
kdf_custom: &EPHEMERAL_KDF_CUSTOM,
xof_custom: &RUNTIME_XOF_CUSTOM,
sk: "18789f16d8a8fbd75f529f0a1de3e95469eb537c283dc66eb296a6681a46c066",
pk: "1aa3e6d86c0779afde8a367dbbb025538fb77e410624ece8b25a6be9e1a5170d",
},
// Different xof custom.
TestVector {
master_secret: "3fa39161fe106fb770503558dad41bea3221888c09477d864507e615094f5d38",
seed: "2a9d51ed0380d11b26255ecc894dfc3aa48196c9b0da463a3073ea820a65a090",
kdf_custom: &RUNTIME_KDF_CUSTOM,
xof_custom: &EPHEMERAL_XOF_CUSTOM,
sk: "e0e6faa3172634c8fdc47554f0541c7141f7ddfe07333b0dab1801bb5dcd755e",
pk: "455f877b6c977c51580e947a5a2ae57b4b1ec0a379cf6509be07f10aa88df735",
},
// Another random vector.
TestVector {
master_secret: "b573fa13ec26d21a7a2b5117eb124dcab5883eb63ec0a5a9cc0b1d1948bcfc71",
seed: "774cbb9c1e8c634e7e136357d9994f65d72fc0e7d72c1e9ad766db1a74e0bb8c",
kdf_custom,
xof_custom,
sk: "70c077d8bd0c5c2be66d08fbacac4f1a996001883baaf62bd933e2f7de390e5f",
pk: "2d62567e9061a7cc64df3793fbee65c863862b8f0acc992e2a39e32757f2c743",
},
];

// Values that don't effect key derivation.
let checksum = "100246b37912e916e71ff79982b2f62be892ddf1025cde84804f67e7f5713b75";
let runtime_id = "8000000000000000000000000000000000000000000000000000000000000000";
let signer = "08e35ef2b23fc2d27281117f8ad3fa0cb4d52e803a070ebb20e7ac9aa8d1f84a";

// Test all vectors.
for v in vectors {
let kdf = Kdf {
inner: RwLock::new(Inner {
master_secret: Some(MasterSecret(
v.master_secret
.from_hex::<Vec<u8>>()
.unwrap()
.try_into()
.unwrap(),
)),
checksum: Some(checksum.from_hex().unwrap()),
runtime_id: Some(Namespace::from(runtime_id)),
signer: Some(Arc::new(PrivateKey::from_bytes(signer.from_hex().unwrap()))),
cache: LruCache::new(1),
}),
};

let req = SimpleKeyRequest {
seed: v.seed,
kdf_custom: v.kdf_custom,
xof_custom: v.xof_custom,
};

let sk = kdf
.get_or_create_keys(&req)
.expect("private key should be created");

assert_eq!(sk.input_keypair.sk.0.to_hex::<String>(), v.sk);
assert_eq!(sk.input_keypair.pk.0.to_hex::<String>(), v.pk);
}
}
}

0 comments on commit 2e028e0

Please sign in to comment.