checkpoint

This commit is contained in:
John Smith
2023-02-11 15:54:55 -05:00
parent 064e6c018c
commit 1ba0cdb9cf
42 changed files with 655 additions and 350 deletions

View 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);
}
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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!(