initial import of main veilid core

This commit is contained in:
John Smith
2021-11-22 11:28:30 -05:00
parent c4cd54e020
commit 9e94a6a96f
218 changed files with 34880 additions and 1 deletions

View File

@@ -0,0 +1,287 @@
use super::key::*;
use crate::intf::*;
use crate::xx::*;
use crate::*;
use chacha20poly1305 as ch;
use chacha20poly1305::aead::{AeadInPlace, NewAead};
use core::convert::TryInto;
use curve25519_dalek as cd;
use ed25519_dalek as ed;
use serde::{Deserialize, Serialize};
use serde_big_array::*;
use uluru;
use x25519_dalek as xd;
pub type SharedSecret = [u8; 32];
pub type Nonce = [u8; 24];
const DH_CACHE_SIZE: usize = 1024;
pub const ENCRYPTION_OVERHEAD: usize = 16;
big_array! {
BigArray;
DH_CACHE_SIZE
}
type DHCache = uluru::LRUCache<DHCacheEntry, DH_CACHE_SIZE>;
#[derive(Serialize, Deserialize)]
struct DHCacheEntry {
key: DHTKey,
secret: DHTKeySecret,
shared_secret: SharedSecret,
}
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.key.bytes);
out.extend(&e.secret.bytes);
out.extend(&e.shared_secret);
}
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 e = DHCacheEntry {
key: DHTKey::new(d[0..32].try_into().expect("asdf")),
secret: DHTKeySecret::new(d[32..64].try_into().expect("asdf")),
shared_secret: d[64..96].try_into().expect("asdf"),
};
cache.insert(e);
}
}
struct CryptoInner {
table_store: TableStore,
node_id: DHTKey,
node_id_secret: DHTKeySecret,
dh_cache: DHCache,
flush_future: Option<SystemPinBoxFuture<()>>,
}
#[derive(Clone)]
pub struct Crypto {
config: VeilidConfig,
inner: Arc<Mutex<CryptoInner>>,
}
impl Crypto {
fn new_inner(table_store: TableStore) -> CryptoInner {
CryptoInner {
table_store: table_store,
node_id: Default::default(),
node_id_secret: Default::default(),
dh_cache: DHCache::default(),
flush_future: None,
}
}
pub fn new(config: VeilidConfig, table_store: TableStore) -> Self {
Self {
config: config,
inner: Arc::new(Mutex::new(Self::new_inner(table_store))),
}
}
pub async fn init(&self) -> Result<(), String> {
trace!("Crypto::init");
// make local copy of node id for easy access
let mut inner = self.inner.lock();
let c = self.config.get();
inner.node_id = c.network.node_id;
inner.node_id_secret = c.network.node_id_secret;
// load caches if they are valid for this node id
let mut db = inner.table_store.open("crypto_caches", 1).await?;
let caches_valid = match db.load(0, b"node_id").await? {
Some(v) => v.as_slice() == inner.node_id.bytes,
None => false,
};
if caches_valid {
match db.load(0, b"dh_cache").await? {
Some(b) => {
bytes_to_cache(&b, &mut inner.dh_cache);
}
None => (),
};
} else {
drop(db);
inner.table_store.delete("crypto_caches").await?;
db = inner.table_store.open("crypto_caches", 1).await?;
db.store(0, b"node_id", &inner.node_id.bytes).await?;
}
// Schedule flushing
let this = self.clone();
inner.flush_future = Some(Box::pin(interval(60000, move || {
let this = this.clone();
async move {
if let Err(e) = this.flush().await {
warn!("flush failed: {}", e);
}
}
})));
Ok(())
}
pub async fn flush(&self) -> Result<(), String> {
//trace!("Crypto::flush");
let (table_store, cache_bytes) = {
let inner = self.inner.lock();
let cache_bytes = cache_to_bytes(&inner.dh_cache);
(inner.table_store.clone(), cache_bytes)
};
let db = table_store.open("crypto_caches", 1).await?;
db.store(0, b"dh_cache", &cache_bytes).await?;
Ok(())
}
pub async fn terminate(&self) {
trace!("Crypto::terminate");
let flush_future = self.inner.lock().flush_future.take();
if let Some(f) = flush_future {
f.await;
}
trace!("starting termination flush");
match self.flush().await {
Ok(_) => {
trace!("finished termination flush");
()
}
Err(e) => {
error!("failed termination flush: {}", e);
()
}
};
}
fn ed25519_to_x25519_pk(key: &ed::PublicKey) -> Result<xd::PublicKey, ()> {
let bytes = key.to_bytes();
let compressed = cd::edwards::CompressedEdwardsY(bytes);
let point = compressed.decompress().ok_or(())?;
let mp = point.to_montgomery();
Ok(xd::PublicKey::from(mp.to_bytes()))
}
fn ed25519_to_x25519_sk(key: &ed::SecretKey) -> Result<xd::StaticSecret, ()> {
let exp = ed::ExpandedSecretKey::from(key);
let bytes: [u8; ed::EXPANDED_SECRET_KEY_LENGTH] = exp.to_bytes();
let lowbytes: [u8; 32] = bytes[0..32].try_into().map_err(drop)?;
Ok(xd::StaticSecret::from(lowbytes))
}
pub fn cached_dh(&self, key: &DHTKey, secret: &DHTKeySecret) -> Result<SharedSecret, ()> {
if let Some(c) = self
.inner
.lock()
.dh_cache
.find(|entry| entry.key == *key && entry.secret == *secret)
{
return Ok(c.shared_secret);
}
let ss = Self::compute_dh(key, secret)?;
self.inner.lock().dh_cache.insert(DHCacheEntry {
key: key.clone(),
secret: secret.clone(),
shared_secret: ss.clone(),
});
Ok(ss)
}
///////////
// These are safe to use regardless of initialization status
pub fn compute_dh(key: &DHTKey, secret: &DHTKeySecret) -> Result<SharedSecret, ()> {
assert!(key.valid);
assert!(secret.valid);
let pk_ed = match ed::PublicKey::from_bytes(&key.bytes) {
Ok(v) => v,
Err(e) => {
trace!("compute_dh error: {:?}", e);
return Err(());
}
};
let pk_xd = Self::ed25519_to_x25519_pk(&pk_ed)?;
let sk_ed = match ed::SecretKey::from_bytes(&secret.bytes) {
Ok(v) => v,
Err(e) => {
trace!("compute_dh error: {:?}", e);
return Err(());
}
};
let sk_xd = Self::ed25519_to_x25519_sk(&sk_ed)?;
Ok(sk_xd.diffie_hellman(&pk_xd).to_bytes())
}
pub fn get_random_nonce() -> Nonce {
let mut nonce = [0u8; 24];
let _ = random_bytes(&mut nonce).unwrap();
nonce
}
pub fn get_random_secret() -> SharedSecret {
let mut s = [0u8; 32];
let _ = random_bytes(&mut s).unwrap();
s
}
pub fn decrypt_in_place(
body: &mut Vec<u8>,
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<(), ()> {
let key = ch::Key::from(shared_secret.clone());
let xnonce = ch::XNonce::from(nonce.clone());
let aead = ch::XChaCha20Poly1305::new(&key);
aead.decrypt_in_place(&xnonce, associated_data.unwrap_or(b""), body)
.map_err(|e| trace!("decryption failure: {}", e))
}
pub fn decrypt(
body: &[u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<Vec<u8>, ()> {
let mut out = body.to_vec();
let _ = Self::decrypt_in_place(&mut out, nonce, shared_secret, associated_data)?;
Ok(out)
}
pub fn encrypt_in_place(
body: &mut Vec<u8>,
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<(), ()> {
let key = ch::Key::from(shared_secret.clone());
let xnonce = ch::XNonce::from(nonce.clone());
let aead = ch::XChaCha20Poly1305::new(&key);
aead.encrypt_in_place(&xnonce, associated_data.unwrap_or(b""), body)
.map_err(|e| trace!("encryption failure: {}", e))
}
pub fn encrypt(
body: &[u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<Vec<u8>, ()> {
let mut out = body.to_vec();
let _ = Self::encrypt_in_place(&mut out, nonce, shared_secret, associated_data)?;
Ok(out)
}
}

View File

@@ -0,0 +1,267 @@
use super::crypto::*;
use super::key::*;
use crate::xx::*;
use core::convert::TryInto;
// #[repr(C, packed)]
// struct EnvelopeHeader {
// // Size is at least 8 bytes. Depending on the version specified, the size may vary and should be case to the appropriate struct
// magic: [u8; 4], // 0x00: 0x56 0x4C 0x49 0x44 ("VLID")
// version: u8, // 0x04: 0 = EnvelopeV0
// min_version: u8, // 0x05: 0 = EnvelopeV0
// max_version: u8, // 0x06: 0 = EnvelopeV0
// reserved: u8, // 0x07: Reserved for future use
// }
// #[repr(C, packed)]
// struct EnvelopeV0 {
// // Size is 106 bytes.
// magic: [u8; 4], // 0x00: 0x56 0x4C 0x49 0x44 ("VLID")
// version: u8, // 0x04: 0 = EnvelopeV0
// min_version: u8, // 0x05: 0 = EnvelopeV0
// max_version: u8, // 0x06: 0 = EnvelopeV0
// reserved: u8, // 0x07: Reserved for future use
// size: u16, // 0x08: Total size of the envelope including the encrypted operations message. Maximum size is 65,507 bytes, which is the data size limit for a single UDP message on IPv4.
// timestamp: u64, // 0x0A: Duration since UNIX_EPOCH in microseconds when this message is sent. Messages older than 10 seconds are dropped.
// nonce: [u8; 24], // 0x12: Random nonce for replay protection and for x25519
// sender_id: [u8; 32], // 0x2A: Node ID of the message source, which is the Ed25519 public key of the sender (must be verified with find_node if this is a new node_id/address combination)
// recipient_id: [u8; 32], // 0x4A: Node ID of the intended recipient, which is the Ed25519 public key of the recipient (must be the receiving node, or a relay lease holder)
// // 0x6A: message is appended (operations)
// // encrypted by XChaCha20Poly1305(nonce,x25519(recipient_id, sender_secret_key))
// // decryptable by XChaCha20Poly1305(nonce,x25519(sender_id, recipient_secret_key))
// // entire header needs to be included in message digest, relays are not allowed to modify the envelope without invalidating the signature.
// }
pub const MAX_ENVELOPE_SIZE: usize = 65507;
pub const MIN_ENVELOPE_SIZE: usize = 106;
pub const AEAD_ADDITIONAL_SIZE: usize = 16;
pub const ENVELOPE_MAGIC: &[u8; 4] = b"VLID";
pub const MIN_VERSION: u8 = 0u8;
pub const MAX_VERSION: u8 = 0u8;
pub type EnvelopeNonce = [u8; 24];
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Envelope {
version: u8,
min_version: u8,
max_version: u8,
timestamp: u64,
nonce: EnvelopeNonce,
sender_id: DHTKey,
recipient_id: DHTKey,
}
impl Envelope {
pub fn new(
version: u8,
timestamp: u64,
nonce: EnvelopeNonce,
sender_id: DHTKey,
recipient_id: DHTKey,
) -> Self {
assert!(sender_id.valid);
assert!(recipient_id.valid);
assert!(version >= MIN_VERSION);
assert!(version <= MAX_VERSION);
Self {
version: version,
min_version: MIN_VERSION,
max_version: MAX_VERSION,
timestamp: timestamp,
nonce: nonce,
sender_id: sender_id,
recipient_id: recipient_id,
}
}
pub fn from_data(data: &[u8]) -> Result<Envelope, ()> {
// Ensure we are at least the length of the envelope
if data.len() < MIN_ENVELOPE_SIZE {
trace!("envelope too small: len={}", data.len());
return Err(());
}
// Verify magic number
let magic: [u8; 4] = data[0x00..0x04].try_into().map_err(drop)?;
if magic != *ENVELOPE_MAGIC {
trace!("bad magic number: len={:?}", magic);
return Err(());
}
// Check version
let version = data[0x04];
if version > MAX_VERSION || version < MIN_VERSION {
trace!("unsupported protocol version: version={}", version);
return Err(());
}
// Get min version
let min_version = data[0x05];
if min_version > version {
trace!(
"invalid version information in envelope: min_version={}, version={}",
min_version,
version,
);
return Err(());
}
// Get max version
let max_version = data[0x06];
if version > max_version || min_version > max_version {
trace!(
"invalid version information in envelope: min_version={}, version={}, max_version={}",
min_version,
version,
max_version
);
return Err(());
}
// Get size and ensure it matches the size of the envelope and is less than the maximum message size
let size: u16 = u16::from_le_bytes(data[0x08..0x0A].try_into().map_err(drop)?);
if (size as usize) > MAX_ENVELOPE_SIZE {
trace!("envelope size is too large: size={}", size);
return Err(());
}
if (size as usize) != data.len() {
trace!(
"size doesn't match envelope size: size={} data.len()={}",
size,
data.len()
);
return Err(());
}
// Get the timestamp
let timestamp: u64 = u64::from_le_bytes(data[0x0A..0x12].try_into().map_err(drop)?);
// Get nonce and sender node id
let nonce: EnvelopeNonce = data[0x12..0x2A].try_into().map_err(drop)?;
let sender_id: [u8; 32] = data[0x2A..0x4A].try_into().map_err(drop)?;
let recipient_id: [u8; 32] = data[0x4A..0x6A].try_into().map_err(drop)?;
let sender_id_dhtkey = DHTKey::new(sender_id);
let recipient_id_dhtkey = DHTKey::new(recipient_id);
// Ensure sender_id and recipient_id are not the same
if sender_id_dhtkey == recipient_id_dhtkey {
trace!(
"sender_id should not be same as recipient_id: {}",
recipient_id_dhtkey.encode()
);
return Err(());
}
// Return envelope
Ok(Self {
version: version,
min_version: min_version,
max_version: max_version,
timestamp: timestamp,
nonce: nonce,
sender_id: sender_id_dhtkey,
recipient_id: recipient_id_dhtkey,
})
}
pub fn decrypt_body(
&self,
crypto: Crypto,
data: &[u8],
node_id_secret: &DHTKeySecret,
) -> Result<Vec<u8>, ()> {
// Get DH secret
let dh_secret = crypto.cached_dh(&self.sender_id, node_id_secret)?;
// Decrypt message and authenticate, including the envelope header as associated data to authenticate
let body = Crypto::decrypt(
&data[0x6A..],
&self.nonce,
&dh_secret,
Some(&data[0..MIN_ENVELOPE_SIZE]),
)?;
Ok(body)
}
pub fn to_encrypted_data(
&self,
crypto: Crypto,
body: &[u8],
node_id_secret: &DHTKeySecret,
) -> Result<Vec<u8>, ()> {
// Ensure sender node id is valid
if !self.sender_id.valid {
return Err(());
}
// Ensure recipient node id is valid
if !self.recipient_id.valid {
return Err(());
}
// Ensure body isn't too long
let envelope_size: usize = body.len() + MIN_ENVELOPE_SIZE + AEAD_ADDITIONAL_SIZE;
if envelope_size > MAX_ENVELOPE_SIZE {
return Err(());
}
let mut data: Vec<u8> = Vec::with_capacity(envelope_size);
data.resize(envelope_size, 0u8);
// Write magic
data[0x00..0x04].copy_from_slice(ENVELOPE_MAGIC);
// Write version
data[0x04] = self.version;
// Write min version
data[0x05] = self.min_version;
// Write max version
data[0x06] = self.max_version;
// Write size
data[0x08..0x0A].copy_from_slice(&(envelope_size as u16).to_le_bytes());
// Write timestamp
data[0x0A..0x12].copy_from_slice(&self.timestamp.to_le_bytes());
// Write nonce
data[0x12..0x2A].copy_from_slice(&self.nonce);
// Write sender node id
data[0x2A..0x4A].copy_from_slice(&self.sender_id.bytes);
// Write recipient node id
data[0x4A..0x6A].copy_from_slice(&self.recipient_id.bytes);
// Generate dh secret
let dh_secret = crypto
.cached_dh(&self.recipient_id, node_id_secret)
.map_err(drop)?;
// Encrypt and authenticate message
let encrypted_body =
Crypto::encrypt(body, &self.nonce, &dh_secret, Some(&data[0..0x6A])).map_err(drop)?;
// Write body
data[0x6A..].copy_from_slice(encrypted_body.as_slice());
Ok(data)
}
pub fn get_version(&self) -> u8 {
self.version
}
pub fn get_min_max_version(&self) -> (u8, u8) {
(self.min_version, self.max_version)
}
pub fn get_timestamp(&self) -> u64 {
self.timestamp
}
pub fn get_nonce(&self) -> EnvelopeNonce {
self.nonce
}
pub fn get_sender_id(&self) -> DHTKey {
self.sender_id
}
pub fn get_recipient_id(&self) -> DHTKey {
self.recipient_id
}
}

432
veilid-core/src/dht/key.rs Normal file
View File

@@ -0,0 +1,432 @@
use crate::xx::*;
use core::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
use core::convert::{TryFrom, TryInto};
use core::fmt;
use hex;
use crate::veilid_rng::*;
use ed25519_dalek::{Keypair, PublicKey, Signature};
use serde::{Deserialize, Serialize};
use data_encoding::BASE64URL_NOPAD;
use digest::generic_array::typenum::U64;
use digest::{Digest, Output};
use generic_array::GenericArray;
//////////////////////////////////////////////////////////////////////
#[allow(dead_code)]
pub const DHT_KEY_LENGTH: usize = 32;
#[allow(dead_code)]
pub const DHT_KEY_LENGTH_ENCODED: usize = 43;
#[allow(dead_code)]
pub const DHT_KEY_SECRET_LENGTH: usize = 32;
#[allow(dead_code)]
pub const DHT_KEY_SECRET_LENGTH_ENCODED: usize = 43;
#[allow(dead_code)]
pub const DHT_SIGNATURE_LENGTH: usize = 64;
#[allow(dead_code)]
pub const DHT_SIGNATURE_LENGTH_ENCODED: usize = 86;
//////////////////////////////////////////////////////////////////////
macro_rules! byte_array_type {
($name:ident, $size:expr) => {
#[derive(Clone, Copy)]
pub struct $name {
pub bytes: [u8; $size],
pub valid: bool,
}
impl Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let s: String;
if self.valid {
s = self.encode();
} else {
s = "".to_owned();
}
s.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
if s == "" {
return Ok($name::default());
}
$name::try_decode(s.as_str()).map_err(|e| serde::de::Error::custom(e))
}
}
impl $name {
pub fn new(bytes: [u8; $size]) -> Self {
Self {
bytes: bytes,
valid: true,
}
}
pub fn try_from_vec(v: Vec<u8>) -> Result<Self, String> {
let mut this = Self {
bytes: [0u8; $size],
valid: true,
};
if v.len() != $size {
return Err(format!(
"Expected a Vec of length {} but it was {}",
$size,
v.len()
));
}
for n in 0..v.len() {
this.bytes[n] = v[n];
}
Ok(this)
}
pub fn bit(&self, index: usize) -> bool {
assert!(index < ($size * 8));
let bi = index / 8;
let ti = 7 - (index % 8);
((self.bytes[bi] >> ti) & 1) != 0
}
pub fn first_nonzero_bit(&self) -> Option<usize> {
for i in 0..$size {
let b = self.bytes[i];
if b != 0 {
for n in 0..8 {
if ((b >> (7 - n)) & 1u8) != 0u8 {
return Some((i * 8) + n);
}
}
panic!("wtf")
}
}
None
}
pub fn nibble(&self, index: usize) -> u8 {
assert!(index < ($size * 2));
let bi = index / 2;
if index & 1 == 0 {
(self.bytes[bi] >> 4) & 0xFu8
} else {
self.bytes[bi] & 0xFu8
}
}
pub fn first_nonzero_nibble(&self) -> Option<(usize, u8)> {
for i in 0..($size * 2) {
let n = self.nibble(i);
if n != 0 {
return Some((i, n));
}
}
None
}
pub fn encode(&self) -> String {
assert!(self.valid);
BASE64URL_NOPAD.encode(&self.bytes)
}
pub fn try_decode(input: &str) -> Result<Self, String> {
let mut bytes = [0u8; $size];
let res = BASE64URL_NOPAD.decode_len(input.len());
match res {
Ok(v) => {
if v != $size {
return Err("Incorrect length in decode".to_owned());
}
}
Err(_) => {
return Err("Failed to decode".to_owned());
}
}
let res = BASE64URL_NOPAD.decode_mut(input.as_bytes(), &mut bytes);
match res {
Ok(_) => Ok(Self::new(bytes)),
Err(_) => Err("Failed to decode".to_owned()),
}
}
}
impl PartialOrd for $name {
fn partial_cmp(&self, other: &$name) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for $name {
fn cmp(&self, other: &$name) -> Ordering {
if !self.valid && !other.valid {
return Ordering::Equal;
}
if !self.valid && other.valid {
return Ordering::Less;
}
if self.valid && !other.valid {
return Ordering::Greater;
}
for n in 0..$size {
if self.bytes[n] < other.bytes[n] {
return Ordering::Less;
}
if self.bytes[n] > other.bytes[n] {
return Ordering::Greater;
}
}
Ordering::Equal
}
}
impl PartialEq<$name> for $name {
fn eq(&self, other: &$name) -> bool {
if self.valid != other.valid {
return false;
}
for n in 0..$size {
if self.bytes[n] != other.bytes[n] {
return false;
}
}
true
}
}
impl Eq for $name {}
impl Default for $name {
fn default() -> Self {
let mut this = $name::new([0u8; $size]);
this.valid = false;
this
}
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", String::from(self))
}
}
impl fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, concat!(stringify!($name), "("))?;
write!(f, "{}", String::from(self))?;
write!(f, ")")
}
}
impl From<&$name> for String {
fn from(value: &$name) -> Self {
if !value.valid {
return "".to_owned();
}
let mut s = String::new();
for n in 0..($size / 8) {
let b: [u8; 8] = value.bytes[n * 8..(n + 1) * 8].try_into().unwrap();
s.push_str(hex::encode(b).as_str());
}
s
}
}
impl TryFrom<String> for $name {
type Error = String;
fn try_from(value: String) -> Result<Self, Self::Error> {
$name::try_from(value.as_str())
}
}
impl TryFrom<&str> for $name {
type Error = String;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let mut out = $name::default();
if value == "" {
return Ok(out);
}
if value.len() != ($size * 2) {
return Err(concat!(stringify!($name), " is incorrect length").to_owned());
}
match hex::decode_to_slice(value, &mut out.bytes) {
Ok(_) => {
out.valid = true;
Ok(out)
}
Err(err) => Err(format!("{}", err)),
}
}
}
};
}
byte_array_type!(DHTKey, DHT_KEY_LENGTH);
byte_array_type!(DHTKeySecret, DHT_KEY_SECRET_LENGTH);
byte_array_type!(DHTSignature, DHT_SIGNATURE_LENGTH);
byte_array_type!(DHTKeyDistance, DHT_KEY_LENGTH);
/////////////////////////////////////////
struct Blake3Digest512 {
dig: blake3::Hasher,
}
impl Digest for Blake3Digest512 {
type OutputSize = U64;
fn new() -> Self {
Self {
dig: blake3::Hasher::new(),
}
}
fn update(&mut self, data: impl AsRef<[u8]>) {
self.dig.update(data.as_ref());
}
fn chain(mut self, data: impl AsRef<[u8]>) -> Self
where
Self: Sized,
{
self.update(data);
self
}
fn finalize(self) -> Output<Self> {
let mut b = [0u8; 64];
self.dig.finalize_xof().fill(&mut b);
let mut out = GenericArray::<u8, U64>::default();
for n in 0..64 {
out[n] = b[n];
}
out
}
fn finalize_reset(&mut self) -> Output<Self> {
let mut b = [0u8; 64];
self.dig.finalize_xof().fill(&mut b);
let mut out = GenericArray::<u8, U64>::default();
for n in 0..64 {
out[n] = b[n];
}
self.reset();
out
}
fn reset(&mut self) {
self.dig.reset();
}
fn output_size() -> usize {
64
}
fn digest(data: &[u8]) -> Output<Self> {
let mut dig = blake3::Hasher::new();
dig.update(data);
let mut b = [0u8; 64];
dig.finalize_xof().fill(&mut b);
let mut out = GenericArray::<u8, U64>::default();
for n in 0..64 {
out[n] = b[n];
}
out
}
}
/////////////////////////////////////////
pub fn generate_secret() -> (DHTKey, DHTKeySecret) {
let mut csprng = VeilidRng {};
let keypair = Keypair::generate(&mut csprng);
let dht_key = DHTKey::new(keypair.public.to_bytes());
let dht_key_secret = DHTKeySecret::new(keypair.secret.to_bytes());
(dht_key, dht_key_secret)
}
pub fn sign(
dht_key: &DHTKey,
dht_key_secret: &DHTKeySecret,
data: &[u8],
) -> Result<DHTSignature, String> {
assert!(dht_key.valid);
assert!(dht_key_secret.valid);
let mut kpb: [u8; DHT_KEY_SECRET_LENGTH + DHT_KEY_LENGTH] =
[0u8; DHT_KEY_SECRET_LENGTH + DHT_KEY_LENGTH];
kpb[..DHT_KEY_SECRET_LENGTH].copy_from_slice(&dht_key_secret.bytes);
kpb[DHT_KEY_SECRET_LENGTH..].copy_from_slice(&dht_key.bytes);
let keypair = Keypair::from_bytes(&kpb).map_err(|_| "Keypair is invalid".to_owned())?;
let mut dig = Blake3Digest512::new();
dig.update(data);
let sig = keypair
.sign_prehashed(dig, None)
.map_err(|_| "Signature failed".to_owned())?;
let dht_sig = DHTSignature::new(sig.to_bytes().clone());
Ok(dht_sig)
}
pub fn verify(dht_key: &DHTKey, data: &[u8], signature: &DHTSignature) -> Result<(), String> {
assert!(dht_key.valid);
assert!(signature.valid);
let pk =
PublicKey::from_bytes(&dht_key.bytes).map_err(|_| "Public key is invalid".to_owned())?;
let sig =
Signature::from_bytes(&signature.bytes).map_err(|_| "Signature is invalid".to_owned())?;
let mut dig = Blake3Digest512::new();
dig.update(data);
pk.verify_prehashed(dig, None, &sig)
.map_err(|_| "Verification failed".to_owned())?;
Ok(())
}
pub fn generate_hash(data: &[u8]) -> DHTKey {
DHTKey::new(*blake3::hash(data).as_bytes())
}
pub fn validate_hash(data: &[u8], dht_key: &DHTKey) -> bool {
assert!(dht_key.valid);
let bytes = *blake3::hash(data).as_bytes();
bytes == dht_key.bytes
}
pub fn validate_key(dht_key: &DHTKey, dht_key_secret: &DHTKeySecret) -> bool {
let data = vec![0u8; 512];
let sig = match sign(&dht_key, &dht_key_secret, &data) {
Ok(s) => s,
Err(_) => {
return false;
}
};
verify(&dht_key, &data, &sig).is_ok()
}
pub fn distance(key1: &DHTKey, key2: &DHTKey) -> DHTKeyDistance {
assert!(key1.valid);
assert!(key2.valid);
let mut bytes = [0u8; DHT_KEY_LENGTH];
for n in 0..DHT_KEY_LENGTH {
bytes[n] = key1.bytes[n] ^ key2.bytes[n];
}
DHTKeyDistance::new(bytes)
}

View File

@@ -0,0 +1,11 @@
pub mod crypto;
pub mod envelope;
pub mod key;
pub mod receipt;
pub mod value;
pub use crypto::*;
pub use envelope::*;
pub use key::*;
pub use receipt::*;
pub use value::*;

View File

@@ -0,0 +1,170 @@
use super::envelope::{MAX_VERSION, MIN_VERSION};
use super::key::*;
use crate::xx::*;
use core::convert::TryInto;
// #[repr(C, packed)]
// struct ReceiptHeader {
// // Size is at least 8 bytes. Depending on the version specified, the size may vary and should be case to the appropriate struct
// magic: [u8; 4], // 0x00: 0x52 0x43 0x50 0x54 ("RCPT")
// version: u8, // 0x04: 0 = ReceiptV0
// reserved: u8, // 0x05: Reserved for future use
// }
// #[repr(C, packed)]
// struct ReceiptV0 {
// // Size is 106 bytes.
// magic: [u8; 4], // 0x00: 0x52 0x43 0x50 0x54 ("RCPT")
// version: u8, // 0x04: 0 = ReceiptV0
// reserved: u8, // 0x05: Reserved for future use
// size: u16, // 0x06: Total size of the receipt including the extra data and the signature. Maximum size is 1152 bytes.
// nonce: [u8; 24], // 0x08: Randomly chosen bytes that represent a unique receipt. Could be used to encrypt the extra data, but it's not required.
// sender_id: [u8; 32], // 0x20: Node ID of the message source, which is the Ed25519 public key of the sender (must be verified with find_node if this is a new node_id/address combination)
// extra_data: [u8; ??], // 0x40: Extra data is appended (arbitrary extra data, not encrypted by receipt itself, maximum size is 1024 bytes)
// signature: [u8; 64], // 0x?? (end-0x40): Ed25519 signature of the entire receipt including header and extra data is appended to the packet
// }
pub const MAX_RECEIPT_SIZE: usize = 1152;
pub const MAX_EXTRA_DATA_SIZE: usize = 1024;
pub const MIN_RECEIPT_SIZE: usize = 128;
pub const RECEIPT_MAGIC: &[u8; 4] = b"RCPT";
pub type ReceiptNonce = [u8; 24];
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Receipt {
version: u8,
nonce: ReceiptNonce,
sender_id: DHTKey,
extra_data: Vec<u8>,
}
impl Receipt {
pub fn try_new<D: AsRef<[u8]>>(
version: u8,
nonce: ReceiptNonce,
sender_id: DHTKey,
extra_data: D,
) -> Result<Self, String> {
assert!(sender_id.valid);
if extra_data.as_ref().len() > MAX_EXTRA_DATA_SIZE {
return Err("extra data too large for receipt".to_owned());
}
Ok(Self {
version: version,
nonce: nonce,
sender_id: sender_id,
extra_data: Vec::from(extra_data.as_ref()),
})
}
pub fn from_signed_data(data: &[u8]) -> Result<Receipt, ()> {
// Ensure we are at least the length of the envelope
if data.len() < MIN_RECEIPT_SIZE {
trace!("receipt too small: len={}", data.len());
return Err(());
}
// Verify magic number
let magic: [u8; 4] = data[0x00..0x04].try_into().map_err(drop)?;
if magic != *RECEIPT_MAGIC {
trace!("bad magic number: len={:?}", magic);
return Err(());
}
// Check version
let version = data[0x04];
if version > MAX_VERSION || version < MIN_VERSION {
trace!("unsupported protocol version: version={}", version);
return Err(());
}
// Get size and ensure it matches the size of the envelope and is less than the maximum message size
let size: u16 = u16::from_le_bytes(data[0x06..0x08].try_into().map_err(drop)?);
if (size as usize) > MAX_RECEIPT_SIZE {
trace!("receipt size is too large: size={}", size);
return Err(());
}
if (size as usize) != data.len() {
trace!(
"size doesn't match receipt size: size={} data.len()={}",
size,
data.len()
);
return Err(());
}
// Get sender id
let sender_id_dhtkey = DHTKey::new(data[0x20..0x40].try_into().map_err(drop)?);
// Get signature
let signature = DHTSignature::new(data[(data.len() - 64)..].try_into().map_err(drop)?);
// Validate signature
verify(&sender_id_dhtkey, &data[0..(data.len() - 64)], &signature).map_err(drop)?;
// Get nonce
let nonce: ReceiptNonce = data[0x08..0x20].try_into().map_err(drop)?;
// Get extra data and signature
let extra_data: Vec<u8> = Vec::from(&data[0x40..(data.len() - 64)]);
// Return receipt
Ok(Self {
version: version,
nonce: nonce,
sender_id: sender_id_dhtkey,
extra_data: extra_data,
})
}
pub fn to_signed_data(&self, secret: &DHTKeySecret) -> Result<Vec<u8>, ()> {
// Ensure sender node id is valid
if !self.sender_id.valid {
return Err(());
}
// Ensure extra data isn't too long
let receipt_size: usize = self.extra_data.len() + MIN_RECEIPT_SIZE;
if receipt_size > MAX_RECEIPT_SIZE {
return Err(());
}
let mut data: Vec<u8> = Vec::with_capacity(receipt_size);
data.resize(receipt_size, 0u8);
// Write magic
data[0x00..0x04].copy_from_slice(RECEIPT_MAGIC);
// Write version
data[0x04] = self.version;
// Write size
data[0x06..0x08].copy_from_slice(&(receipt_size as u16).to_le_bytes());
// Write nonce
data[0x08..0x20].copy_from_slice(&self.nonce);
// Write sender node id
data[0x20..0x40].copy_from_slice(&self.sender_id.bytes);
// Write extra data
if self.extra_data.len() > 0 {
data[0x40..(receipt_size - 64)].copy_from_slice(self.extra_data.as_slice());
}
// Sign the receipt
let signature =
sign(&self.sender_id, secret, &data[0..(receipt_size - 64)]).map_err(drop)?;
// Append the signature
data[(receipt_size - 64)..].copy_from_slice(&signature.bytes);
Ok(data)
}
pub fn get_version(&self) -> u8 {
self.version
}
pub fn get_nonce(&self) -> ReceiptNonce {
self.nonce
}
pub fn get_sender_id(&self) -> DHTKey {
self.sender_id
}
pub fn get_extra_data(&self) -> &[u8] {
&self.extra_data
}
}

View File