Skip to content

Commit

Permalink
runtime/src/common/crypto: Add X25519 private/public key types
Browse files Browse the repository at this point in the history
  • Loading branch information
peternose committed Jan 10, 2023
1 parent d935e04 commit 19a7334
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 92 deletions.
1 change: 1 addition & 0 deletions .changelog/5121.internal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
crypto/x25519: Add type-safe X25519 private/public key types
45 changes: 24 additions & 21 deletions keymanager/src/crypto/kdf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@ use lru::LruCache;
use rand::{rngs::OsRng, Rng};
use sgx_isa::Keypolicy;
use sp800_185::{CShake, KMac};
use x25519_dalek;
use zeroize::Zeroize;

use oasis_core_runtime::{
common::{
crypto::{
mrae::deoxysii::{DeoxysII, NONCE_SIZE, TAG_SIZE},
signature,
signature, x25519,
},
namespace::Namespace,
sgx::egetkey::egetkey,
Expand All @@ -37,7 +36,7 @@ use crate::{
ReplicateResponse, SignedInitResponse,
},
client::{KeyManagerClient, RemoteClient},
crypto::{KeyPair, MasterSecret, PrivateKey, PublicKey, SignedPublicKey, StateKey},
crypto::{KeyPair, MasterSecret, SignedPublicKey, StateKey},
policy::Policy,
runtime::context::Context as KmContext,
};
Expand Down Expand Up @@ -203,16 +202,11 @@ impl Inner {

// Public/private keypair.
xof.squeeze(&mut k);
let sk = x25519_dalek::StaticSecret::from(k);
let sk = x25519::PrivateKey::from(k);
k.zeroize();
let pk = x25519_dalek::PublicKey::from(&sk);
let pk = x25519::PublicKey::from(&sk);

Ok(KeyPair::new(
PublicKey(*pk.as_bytes()),
PrivateKey(sk.to_bytes()),
state_key,
checksum,
))
Ok(KeyPair::new(pk, sk, state_key, checksum))
}

fn derive_secret(&self, kdf_custom: &[u8], seed: &[u8]) -> Result<Vec<u8>> {
Expand Down Expand Up @@ -429,15 +423,15 @@ impl Kdf {
}

/// Get the public part of the key.
pub fn get_public_key(&self, req: &impl KeyRequest) -> Result<PublicKey> {
pub fn get_public_key(&self, req: &impl KeyRequest) -> Result<x25519::PublicKey> {
let keys = self.get_or_create_keys(req)?;
Ok(keys.input_keypair.pk)
}

/// Signs the public key using the key manager key.
pub fn sign_public_key(
&self,
key: PublicKey,
key: x25519::PublicKey,
runtime_id: Namespace,
key_pair_id: KeyPairId,
epoch: Option<EpochTime>,
Expand Down Expand Up @@ -562,11 +556,14 @@ mod tests {
use lru::LruCache;
use rustc_hex::{FromHex, ToHex};

use oasis_core_runtime::common::{crypto::signature::PrivateKey, namespace::Namespace};
use oasis_core_runtime::common::{
crypto::{signature::PrivateKey, x25519},
namespace::Namespace,
};

use crate::{
api::{EphemeralKeyRequest, LongTermKeyRequest},
crypto::{KeyPairId, MasterSecret, PublicKey},
crypto::{KeyPairId, MasterSecret},
};

use super::{
Expand Down Expand Up @@ -631,7 +628,10 @@ mod tests {
.get_or_create_keys(&req)
.expect("private key should be created");

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

Expand All @@ -649,7 +649,10 @@ mod tests {
.get_or_create_keys(&req)
.expect("private key should be created");

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

Expand All @@ -671,7 +674,7 @@ mod tests {
fn public_key_signature_is_valid() {
let kdf = Kdf::default();

let pk = PublicKey::from(vec![1u8; 32]);
let pk = x25519::PublicKey::from([1u8; 32]);
let runtime_id = Namespace::from(vec![1u8; 32]);
let key_pair_id = KeyPairId::from(vec![1u8; 32]);
let epoch = Some(10);
Expand All @@ -681,7 +684,7 @@ mod tests {
.sign_public_key(pk, runtime_id, key_pair_id, epoch)
.expect("public key should be signed");

let mut body = pk.as_ref().to_vec();
let mut body = pk.0.to_bytes().to_vec();
let checksum = kdf.inner.into_inner().unwrap().checksum.unwrap();
body.extend_from_slice(&checksum);

Expand Down Expand Up @@ -869,8 +872,8 @@ mod tests {
.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);
assert_eq!(sk.input_keypair.sk.0.to_bytes().to_hex::<String>(), v.sk);
assert_eq!(sk.input_keypair.pk.0.to_bytes().to_hex::<String>(), v.pk);
}
}
}
76 changes: 36 additions & 40 deletions keymanager/src/crypto/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@ use std::sync::Arc;
use anyhow::Result;
use rand::{rngs::OsRng, Rng};
use thiserror::Error;
use x25519_dalek;
use zeroize::Zeroize;

use oasis_core_runtime::{
common::{
crypto::signature::{PublicKey as EdPublicKey, Signature, Signer},
crypto::{
signature::{self, Signature, Signer},
x25519,
},
namespace::Namespace,
},
consensus::beacon::EpochTime,
impl_bytes,
};

impl_bytes!(KeyPairId, 32, "A 256-bit key pair identifier.");
impl_bytes!(PublicKey, 32, "A public key.");

/// Context used for the public key signature.
const PUBLIC_KEY_SIGNATURE_CONTEXT: &[u8] = b"oasis-core/keymanager: pk signature";
Expand All @@ -27,12 +28,6 @@ const MAX_SIGNED_EPHEMERAL_PUBLIC_KEY_AGE: EpochTime = 10;
/// The size of the key manager state checksum.
const CHECKSUM_SIZE: usize = 32;

/// A private key.
#[derive(Clone, Default, cbor::Encode, cbor::Decode, Zeroize)]
#[cbor(transparent)]
#[zeroize(drop)]
pub struct PrivateKey(pub [u8; 32]);

/// A state encryption key.
#[derive(Clone, Default, cbor::Encode, cbor::Decode, Zeroize)]
#[cbor(transparent)]
Expand Down Expand Up @@ -71,49 +66,49 @@ pub struct KeyPair {
impl KeyPair {
/// Generate a new random key (for testing).
pub fn generate_mock() -> Self {
let mut rng = OsRng {};
let sk = x25519_dalek::StaticSecret::new(&mut rng);
let pk = x25519_dalek::PublicKey::from(&sk);
let sk = x25519::PrivateKey::generate();
let pk = x25519::PublicKey::from(&sk);

let mut rng = OsRng {};
let mut state_key = StateKey::default();
rng.fill(&mut state_key.0);

KeyPair::new(
PublicKey(*pk.as_bytes()),
PrivateKey(sk.to_bytes()),
state_key,
vec![],
)
KeyPair::new(pk, sk, state_key, vec![])
}

/// Create a `KeyPair`.
pub fn new(pk: PublicKey, sk: PrivateKey, k: StateKey, sum: Vec<u8>) -> Self {
pub fn new(
pk: x25519::PublicKey,
sk: x25519::PrivateKey,
state_key: StateKey,
checksum: Vec<u8>,
) -> Self {
Self {
input_keypair: InputKeyPair { pk, sk },
state_key: k,
checksum: sum,
state_key,
checksum,
}
}

/// Create a `KeyPair` with only the public key.
pub fn from_public_key(k: PublicKey, sum: Vec<u8>) -> Self {
pub fn from_public_key(pk: x25519::PublicKey, checksum: Vec<u8>) -> Self {
Self {
input_keypair: InputKeyPair {
pk: k,
sk: PrivateKey::default(),
pk,
..Default::default()
},
state_key: StateKey::default(),
checksum: sum,
checksum,
..Default::default()
}
}
}

#[derive(Clone, Default, cbor::Encode, cbor::Decode)]
pub struct InputKeyPair {
/// Public key.
pub pk: PublicKey,
pub pk: x25519::PublicKey,
/// Private key.
pub sk: PrivateKey,
pub sk: x25519::PrivateKey,
}

/// Signed public key error.
Expand All @@ -133,7 +128,7 @@ enum SignedPublicKeyError {
#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Encode, cbor::Decode)]
pub struct SignedPublicKey {
/// Public key.
pub key: PublicKey,
pub key: x25519::PublicKey,
/// Checksum of the key manager state.
pub checksum: Vec<u8>,
/// Sign(sk, (key || checksum || runtime id || key pair id || epoch || expiration epoch)) from
Expand All @@ -147,7 +142,7 @@ pub struct SignedPublicKey {
impl SignedPublicKey {
/// Create a new signed public key.
pub fn new(
key: PublicKey,
key: x25519::PublicKey,
checksum: Vec<u8>,
runtime_id: Namespace,
key_pair_id: KeyPairId,
Expand Down Expand Up @@ -177,7 +172,7 @@ impl SignedPublicKey {
key_pair_id: KeyPairId,
epoch: Option<EpochTime>,
now: Option<EpochTime>,
pk: &EdPublicKey,
pk: &signature::PublicKey,
) -> Result<()> {
// Checksum validation.
if self.checksum.len() != CHECKSUM_SIZE {
Expand Down Expand Up @@ -212,14 +207,14 @@ impl SignedPublicKey {
}

fn body(
key: PublicKey,
key: x25519::PublicKey,
checksum: &[u8],
runtime_id: Namespace,
key_pair_id: KeyPairId,
epoch: Option<EpochTime>,
expiration: Option<EpochTime>,
) -> Vec<u8> {
let mut body = key.as_ref().to_vec();
let mut body = key.0.as_bytes().to_vec();
body.extend_from_slice(checksum);
body.extend_from_slice(runtime_id.as_ref());
body.extend_from_slice(key_pair_id.as_ref());
Expand All @@ -239,15 +234,16 @@ mod test {

use oasis_core_runtime::{
common::{
crypto::signature::{PrivateKey, Signer},
crypto::{
signature::{self, Signer},
x25519,
},
namespace::Namespace,
},
consensus::beacon::EpochTime,
};

use crate::crypto::{
types::MAX_SIGNED_EPHEMERAL_PUBLIC_KEY_AGE, KeyPairId, PublicKey, SignedPublicKey,
};
use crate::crypto::{types::MAX_SIGNED_EPHEMERAL_PUBLIC_KEY_AGE, KeyPairId, SignedPublicKey};

#[test]
fn test_signed_public_key_with_epoch() {
Expand All @@ -260,10 +256,10 @@ mod test {
}

fn test_signed_public_key(epoch: Option<EpochTime>, now: Option<EpochTime>) {
let sk = Arc::new(PrivateKey::from_test_seed("seed".to_string()));
let sk = Arc::new(signature::PrivateKey::from_test_seed("seed".to_string()));
let pk = sk.public_key();

let key = PublicKey([1u8; 32]);
let key = x25519::PublicKey::from([1u8; 32]);
let checksum = [1u8; 32].to_vec();
let runtime_id = Namespace::from(vec![1u8; 32]);
let key_pair_id = KeyPairId::from(vec![1u8; 32]);
Expand Down Expand Up @@ -366,7 +362,7 @@ mod test {

// Verify the signature with different key.
let invalid_signed_pk = SignedPublicKey {
key: PublicKey([2u8; 32]),
key: x25519::PublicKey::from([2u8; 32]),
checksum: signed_pk.checksum.clone(),
signature: signed_pk.signature.clone(),
expiration: signed_pk.expiration,
Expand Down
1 change: 1 addition & 0 deletions runtime/src/common/crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
pub mod hash;
pub mod mrae;
pub mod signature;
pub mod x25519;
Loading

0 comments on commit 19a7334

Please sign in to comment.