checkpoint
This commit is contained in:
45
veilid-core/src/crypto/dh_cache.rs
Normal file
45
veilid-core/src/crypto/dh_cache.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use super::*;
|
||||
use crate::*;
|
||||
|
||||
// Diffie-Hellman key exchange cache
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
pub struct DHCacheKey {
|
||||
pub key: PublicKey,
|
||||
pub secret: SecretKey,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct DHCacheValue {
|
||||
pub shared_secret: SharedSecret,
|
||||
}
|
||||
|
||||
pub type DHCache = LruCache<DHCacheKey, DHCacheValue>;
|
||||
pub const DH_CACHE_SIZE: usize = 4096;
|
||||
|
||||
pub fn cache_to_bytes(cache: &DHCache) -> Vec<u8> {
|
||||
let cnt: usize = cache.len();
|
||||
let mut out: Vec<u8> = Vec::with_capacity(cnt * (32 + 32 + 32));
|
||||
for e in cache.iter() {
|
||||
out.extend(&e.0.key.bytes);
|
||||
out.extend(&e.0.secret.bytes);
|
||||
out.extend(&e.1.shared_secret.bytes);
|
||||
}
|
||||
let mut rev: Vec<u8> = Vec::with_capacity(out.len());
|
||||
for d in out.chunks(32 + 32 + 32).rev() {
|
||||
rev.extend(d);
|
||||
}
|
||||
rev
|
||||
}
|
||||
|
||||
pub fn bytes_to_cache(bytes: &[u8], cache: &mut DHCache) {
|
||||
for d in bytes.chunks(32 + 32 + 32) {
|
||||
let k = DHCacheKey {
|
||||
key: PublicKey::new(d[0..32].try_into().expect("asdf")),
|
||||
secret: SecretKey::new(d[32..64].try_into().expect("asdf")),
|
||||
};
|
||||
let v = DHCacheValue {
|
||||
shared_secret: SharedSecret::new(d[64..96].try_into().expect("asdf")),
|
||||
};
|
||||
cache.insert(k, v);
|
||||
}
|
||||
}
|
@@ -37,7 +37,7 @@ pub const ENVELOPE_MAGIC: &[u8; 3] = b"VLD";
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub struct Envelope {
|
||||
version: u8,
|
||||
version: EnvelopeVersion,
|
||||
crypto_kind: CryptoKind,
|
||||
timestamp: Timestamp,
|
||||
nonce: Nonce,
|
||||
@@ -47,15 +47,14 @@ pub struct Envelope {
|
||||
|
||||
impl Envelope {
|
||||
pub fn new(
|
||||
version: u8,
|
||||
version: EnvelopeVersion,
|
||||
crypto_kind: CryptoKind,
|
||||
timestamp: Timestamp,
|
||||
nonce: Nonce,
|
||||
sender_id: PublicKey,
|
||||
recipient_id: PublicKey,
|
||||
) -> Self {
|
||||
assert!(version >= MIN_ENVELOPE_VERSION);
|
||||
assert!(version <= MAX_ENVELOPE_VERSION);
|
||||
assert!(VALID_ENVELOPE_VERSIONS.contains(&version));
|
||||
assert!(VALID_CRYPTO_KINDS.contains(&crypto_kind));
|
||||
Self {
|
||||
version,
|
||||
@@ -84,7 +83,7 @@ impl Envelope {
|
||||
|
||||
// Check envelope version
|
||||
let version = data[0x03];
|
||||
if version > MAX_ENVELOPE_VERSION || version < MIN_ENVELOPE_VERSION {
|
||||
if !VALID_ENVELOPE_VERSIONS.contains(&version) {
|
||||
apibail_parse_error!("unsupported envelope version", version);
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
mod byte_array_types;
|
||||
mod dh_cache;
|
||||
mod envelope;
|
||||
mod receipt;
|
||||
mod types;
|
||||
@@ -10,6 +11,7 @@ pub mod vld0;
|
||||
|
||||
pub use byte_array_types::*;
|
||||
pub use crypto_system::*;
|
||||
pub use dh_cache::*;
|
||||
pub use envelope::*;
|
||||
pub use receipt::*;
|
||||
pub use types::*;
|
||||
@@ -22,52 +24,20 @@ use hashlink::linked_hash_map::Entry;
|
||||
use hashlink::LruCache;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// Handle to a particular cryptosystem
|
||||
pub type CryptoSystemVersion = Arc<dyn CryptoSystem + Send + Sync>;
|
||||
|
||||
/// Crypto kinds in order of preference, best cryptosystem is the first one, worst is the last one
|
||||
pub const VALID_CRYPTO_KINDS: [CryptoKind; 1] = [CRYPTO_KIND_VLD0];
|
||||
|
||||
pub const MIN_ENVELOPE_VERSION: u8 = 0u8;
|
||||
pub const MAX_ENVELOPE_VERSION: u8 = 0u8;
|
||||
|
||||
const DH_CACHE_SIZE: usize = 4096;
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
struct DHCacheKey {
|
||||
key: PublicKey,
|
||||
secret: SecretKey,
|
||||
pub fn best_crypto_kind() -> CryptoKind {
|
||||
VALID_CRYPTO_KINDS[0]
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct DHCacheValue {
|
||||
shared_secret: SharedSecret,
|
||||
}
|
||||
type DHCache = LruCache<DHCacheKey, DHCacheValue>;
|
||||
|
||||
fn cache_to_bytes(cache: &DHCache) -> Vec<u8> {
|
||||
let cnt: usize = cache.len();
|
||||
let mut out: Vec<u8> = Vec::with_capacity(cnt * (32 + 32 + 32));
|
||||
for e in cache.iter() {
|
||||
out.extend(&e.0.key.bytes);
|
||||
out.extend(&e.0.secret.bytes);
|
||||
out.extend(&e.1.shared_secret.bytes);
|
||||
}
|
||||
let mut rev: Vec<u8> = Vec::with_capacity(out.len());
|
||||
for d in out.chunks(32 + 32 + 32).rev() {
|
||||
rev.extend(d);
|
||||
}
|
||||
rev
|
||||
}
|
||||
|
||||
fn bytes_to_cache(bytes: &[u8], cache: &mut DHCache) {
|
||||
for d in bytes.chunks(32 + 32 + 32) {
|
||||
let k = DHCacheKey {
|
||||
key: PublicKey::new(d[0..32].try_into().expect("asdf")),
|
||||
secret: SecretKey::new(d[32..64].try_into().expect("asdf")),
|
||||
};
|
||||
let v = DHCacheValue {
|
||||
shared_secret: SharedSecret::new(d[64..96].try_into().expect("asdf")),
|
||||
};
|
||||
cache.insert(k, v);
|
||||
}
|
||||
/// Envelope versions in order of preference, best envelope version is the first one, worst is the last one
|
||||
pub type EnvelopeVersion = u8;
|
||||
pub const VALID_ENVELOPE_VERSIONS: [EnvelopeVersion; 1] = [0u8];
|
||||
pub fn best_envelope_version() -> EnvelopeVersion {
|
||||
VALID_ENVELOPE_VERSIONS[0]
|
||||
}
|
||||
|
||||
struct CryptoInner {
|
||||
@@ -227,6 +197,11 @@ impl Crypto {
|
||||
}
|
||||
}
|
||||
|
||||
// Factory method to get the best crypto version
|
||||
pub fn best(&self) -> CryptoSystemVersion {
|
||||
self.get(best_crypto_kind()).unwrap()
|
||||
}
|
||||
|
||||
/// Signature set verification
|
||||
/// Returns the set of signature cryptokinds that validate and are supported
|
||||
/// If any cryptokinds are supported and do not validate, the whole operation
|
||||
|
@@ -50,8 +50,7 @@ impl Receipt {
|
||||
sender_id: PublicKey,
|
||||
extra_data: D,
|
||||
) -> Result<Self, VeilidAPIError> {
|
||||
assert!(version >= MIN_ENVELOPE_VERSION);
|
||||
assert!(version <= MAX_ENVELOPE_VERSION);
|
||||
assert!(VALID_ENVELOPE_VERSIONS.contains(&version));
|
||||
assert!(VALID_CRYPTO_KINDS.contains(&crypto_kind));
|
||||
|
||||
if extra_data.as_ref().len() > MAX_EXTRA_DATA_SIZE {
|
||||
@@ -85,7 +84,7 @@ impl Receipt {
|
||||
|
||||
// Check version
|
||||
let version = data[0x03];
|
||||
if version > MAX_ENVELOPE_VERSION || version < MIN_ENVELOPE_VERSION {
|
||||
if !VALID_ENVELOPE_VERSIONS.contains(&version) {
|
||||
apibail_parse_error!("unsupported envelope version", version);
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,9 @@
|
||||
use super::*;
|
||||
|
||||
pub async fn test_envelope_round_trip(vcrypto: CryptoSystemVersion) {
|
||||
pub async fn test_envelope_round_trip(
|
||||
envelope_version: EnvelopeVersion,
|
||||
vcrypto: CryptoSystemVersion,
|
||||
) {
|
||||
info!("--- test envelope round trip ---");
|
||||
|
||||
// Create envelope
|
||||
@@ -9,7 +12,7 @@ pub async fn test_envelope_round_trip(vcrypto: CryptoSystemVersion) {
|
||||
let (sender_id, sender_secret) = vcrypto.generate_keypair();
|
||||
let (recipient_id, recipient_secret) = vcrypto.generate_keypair();
|
||||
let envelope = Envelope::new(
|
||||
MAX_ENVELOPE_VERSION,
|
||||
envelope_version,
|
||||
vcrypto.kind(),
|
||||
ts,
|
||||
nonce,
|
||||
@@ -53,7 +56,10 @@ pub async fn test_envelope_round_trip(vcrypto: CryptoSystemVersion) {
|
||||
);
|
||||
}
|
||||
|
||||
pub async fn test_receipt_round_trip(vcrypto: CryptoSystemVersion) {
|
||||
pub async fn test_receipt_round_trip(
|
||||
envelope_version: EnvelopeVersion,
|
||||
vcrypto: CryptoSystemVersion,
|
||||
) {
|
||||
info!("--- test receipt round trip ---");
|
||||
// Create arbitrary body
|
||||
let body = b"This is an arbitrary body";
|
||||
@@ -61,7 +67,7 @@ pub async fn test_receipt_round_trip(vcrypto: CryptoSystemVersion) {
|
||||
// Create receipt
|
||||
let nonce = vcrypto.random_nonce();
|
||||
let (sender_id, sender_secret) = vcrypto.generate_keypair();
|
||||
let receipt = Receipt::try_new(MAX_ENVELOPE_VERSION, vcrypto.kind(), nonce, sender_id, body)
|
||||
let receipt = Receipt::try_new(envelope_version, vcrypto.kind(), nonce, sender_id, body)
|
||||
.expect("should not fail");
|
||||
|
||||
// Serialize to bytes
|
||||
@@ -87,11 +93,13 @@ pub async fn test_all() {
|
||||
let crypto = api.crypto().unwrap();
|
||||
|
||||
// Test versions
|
||||
for v in VALID_CRYPTO_KINDS {
|
||||
let vcrypto = crypto.get(v).unwrap();
|
||||
for ev in VALID_ENVELOPE_VERSIONS {
|
||||
for v in VALID_CRYPTO_KINDS {
|
||||
let vcrypto = crypto.get(v).unwrap();
|
||||
|
||||
test_envelope_round_trip(vcrypto.clone()).await;
|
||||
test_receipt_round_trip(vcrypto).await;
|
||||
test_envelope_round_trip(ev, vcrypto.clone()).await;
|
||||
test_receipt_round_trip(ev, vcrypto).await;
|
||||
}
|
||||
}
|
||||
|
||||
crypto_tests_shutdown(api.clone()).await;
|
||||
|
@@ -10,6 +10,23 @@ use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as
|
||||
/// Cryptography version fourcc code
|
||||
pub type CryptoKind = FourCC;
|
||||
|
||||
/// Sort best crypto kinds first
|
||||
pub fn compare_crypto_kind(a: CryptoKind, b: CryptoKind) -> cmp::Ordering {
|
||||
let a_idx = VALID_CRYPTO_KINDS.iter().position(|&k| k == a);
|
||||
let b_idx = VALID_CRYPTO_KINDS.iter().position(|&k| k == b);
|
||||
if let Some(a_idx) = a_idx {
|
||||
if let Some(b_idx) = b_idx {
|
||||
a_idx.cmp(&b_idx)
|
||||
} else {
|
||||
cmp::Ordering::Less
|
||||
}
|
||||
} else if let Some(b_idx) = b_idx {
|
||||
cmp::Ordering::Greater
|
||||
} else {
|
||||
a.cmp(&b)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
@@ -43,8 +60,6 @@ impl KeyPair {
|
||||
Debug,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
@@ -63,6 +78,21 @@ impl TypedKey {
|
||||
Self { kind, key }
|
||||
}
|
||||
}
|
||||
impl PartialOrd for TypedKey {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for TypedKey {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
let x = compare_crypto_kind(self.kind, other.kind);
|
||||
if x != cmp::Ordering::Equal {
|
||||
return x;
|
||||
}
|
||||
self.key.cmp(&other.key)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TypedKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
@@ -84,13 +114,121 @@ impl FromStr for TypedKey {
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
RkyvArchive,
|
||||
RkyvSerialize,
|
||||
RkyvDeserialize,
|
||||
)]
|
||||
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||
pub struct TypedKeySet {
|
||||
items: Vec<TypedKey>,
|
||||
}
|
||||
|
||||
impl TypedKeySet {
|
||||
pub fn new() -> Self {
|
||||
Self { items: Vec::new() }
|
||||
}
|
||||
pub fn with_capacity(cap: usize) -> Self {
|
||||
Self {
|
||||
items: Vec::with_capacity(cap),
|
||||
}
|
||||
}
|
||||
pub fn get(&self, kind: CryptoKind) -> Option<TypedKey> {
|
||||
self.items.iter().find(|x| x.kind == kind).copied()
|
||||
}
|
||||
pub fn add(&mut self, typed_key: TypedKey) {
|
||||
for x in &mut self.items {
|
||||
if x.kind == typed_key.kind {
|
||||
*x = typed_key;
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.items.push(typed_key);
|
||||
self.items.sort()
|
||||
}
|
||||
pub fn remove(&self, kind: CryptoKind) {
|
||||
if let Some(idx) = self.items.iter().position(|x| x.kind == kind) {
|
||||
self.items.remove(idx);
|
||||
}
|
||||
}
|
||||
pub fn best(&self) -> Option<TypedKey> {
|
||||
self.items.first().copied()
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
self.items.len()
|
||||
}
|
||||
pub fn iter(&self) -> core::slice::Iter<'_, TypedKey> {
|
||||
self.items.iter()
|
||||
}
|
||||
pub fn contains(&self, typed_key: &TypedKey) -> bool {
|
||||
self.items.contains(typed_key)
|
||||
}
|
||||
pub fn contains_any(&self, typed_keys: &[TypedKey]) -> bool {
|
||||
for typed_key in typed_keys {
|
||||
if self.items.contains(typed_key) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::Deref for TypedKeySet {
|
||||
type Target = [TypedKey];
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &[TypedKey] {
|
||||
&self.items
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TypedKeySet {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
write!(f, "[")?;
|
||||
let mut first = true;
|
||||
for x in &self.items {
|
||||
if !first {
|
||||
write!(f, ",")?;
|
||||
first = false;
|
||||
}
|
||||
write!(f, "{}", x)?;
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
}
|
||||
impl FromStr for TypedKeySet {
|
||||
type Err = VeilidAPIError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut items = Vec::new();
|
||||
if s.len() < 2 {
|
||||
apibail_parse_error!("invalid length", s);
|
||||
}
|
||||
if &s[0..1] != "[" || &s[(s.len() - 1)..] != "]" {
|
||||
apibail_parse_error!("invalid format", s);
|
||||
}
|
||||
for x in s[1..s.len() - 1].split(",") {
|
||||
let tk = TypedKey::from_str(x.trim())?;
|
||||
items.push(tk);
|
||||
}
|
||||
|
||||
Ok(Self { items })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
RkyvArchive,
|
||||
@@ -110,6 +248,26 @@ impl TypedKeyPair {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for TypedKeyPair {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for TypedKeyPair {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
let x = compare_crypto_kind(self.kind, other.kind);
|
||||
if x != cmp::Ordering::Equal {
|
||||
return x;
|
||||
}
|
||||
let x = self.key.cmp(&other.key);
|
||||
if x != cmp::Ordering::Equal {
|
||||
return x;
|
||||
}
|
||||
self.secret.cmp(&other.secret)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TypedKeyPair {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
write!(
|
||||
@@ -142,8 +300,6 @@ impl FromStr for TypedKeyPair {
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
@@ -176,6 +332,22 @@ impl TypedSignature {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for TypedSignature {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for TypedSignature {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
let x = compare_crypto_kind(self.kind, other.kind);
|
||||
if x != cmp::Ordering::Equal {
|
||||
return x;
|
||||
}
|
||||
self.signature.cmp(&other.signature)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TypedSignature {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
write!(f, "{}:{}", self.kind, self.signature.encode())
|
||||
@@ -198,8 +370,6 @@ impl FromStr for TypedSignature {
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
@@ -232,6 +402,26 @@ impl TypedKeySignature {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for TypedKeySignature {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for TypedKeySignature {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
let x = compare_crypto_kind(self.kind, other.kind);
|
||||
if x != cmp::Ordering::Equal {
|
||||
return x;
|
||||
}
|
||||
let x = self.key.cmp(&other.key);
|
||||
if x != cmp::Ordering::Equal {
|
||||
return x;
|
||||
}
|
||||
self.signature.cmp(&other.signature)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TypedKeySignature {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
write!(
|
||||
|
Reference in New Issue
Block a user