Merge branch 'windows-fix' into 'main'
0.1.10 Release : Correct Windows DNS resolution and Crypto issues Closes #259, #260, and #273 See merge request veilid/veilid!138
This commit is contained in:
		| @@ -1,3 +1,12 @@ | ||||
| **Changes in Veilid 0.1.10** | ||||
| - BREAKING CHANGE: ALL MUST UPDATE | ||||
|   * VLD0 now adds a BLAKE3 hash round on the DH output to further separate it from the raw key exchange | ||||
|   * Bootstraps are fixed now due to DH issue | ||||
| - Windows crate update caused build and nul termination issues for DNS resolver | ||||
| - Fix for network key on the veilid-server command line | ||||
| - Strict verification for Ed25519 enabled | ||||
| - Domain separation for VLD0 signing and crypt | ||||
|    | ||||
| **Changes in Veilid 0.1.9** | ||||
| - SECURITY FIX | ||||
|   * DESCRIPTION: Decompression was occurring in an unbounded way upon envelope receipt. | ||||
|   | ||||
| @@ -288,7 +288,7 @@ impl Envelope { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Encrypt and authenticate message | ||||
|         // Encrypt message | ||||
|         let encrypted_body = vcrypto.crypt_no_auth_unaligned(&body, &self.nonce.bytes, &dh_secret); | ||||
|  | ||||
|         // Write body | ||||
|   | ||||
| @@ -139,7 +139,9 @@ pub async fn test_no_auth(vcrypto: CryptoSystemVersion) { | ||||
| pub async fn test_dh(vcrypto: CryptoSystemVersion) { | ||||
|     trace!("test_dh"); | ||||
|     let (dht_key, dht_key_secret) = vcrypto.generate_keypair().into_split(); | ||||
|     assert!(vcrypto.validate_keypair(&dht_key, &dht_key_secret)); | ||||
|     let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair().into_split(); | ||||
|     assert!(vcrypto.validate_keypair(&dht_key2, &dht_key_secret2)); | ||||
|  | ||||
|     let r1 = vcrypto.compute_dh(&dht_key, &dht_key_secret2).unwrap(); | ||||
|     let r2 = vcrypto.compute_dh(&dht_key2, &dht_key_secret).unwrap(); | ||||
|   | ||||
| @@ -9,35 +9,39 @@ use chacha20::XChaCha20; | ||||
| use chacha20poly1305 as ch; | ||||
| use chacha20poly1305::aead::AeadInPlace; | ||||
| use chacha20poly1305::KeyInit; | ||||
| use core::convert::TryInto; | ||||
| use curve25519_dalek::digest::Digest; | ||||
| use ed25519_dalek as ed; | ||||
| use x25519_dalek as xd; | ||||
|  | ||||
| const VEILID_DOMAIN_SIGN: &[u8] = b"VLD0_SIGN"; | ||||
| const VEILID_DOMAIN_CRYPT: &[u8] = b"VLD0_CRYPT"; | ||||
|  | ||||
| const AEAD_OVERHEAD: usize = 16; | ||||
| pub const CRYPTO_KIND_VLD0: CryptoKind = FourCC(*b"VLD0"); | ||||
|  | ||||
| fn ed25519_to_x25519_pk(key: &ed::VerifyingKey) -> VeilidAPIResult<xd::PublicKey> { | ||||
|     let mp = key.to_montgomery(); | ||||
|     Ok(xd::PublicKey::from(mp.to_bytes())) | ||||
| fn public_to_x25519_pk(public: &PublicKey) -> VeilidAPIResult<xd::PublicKey> { | ||||
|     let pk_ed = ed::VerifyingKey::from_bytes(&public.bytes).map_err(VeilidAPIError::internal)?; | ||||
|     Ok(xd::PublicKey::from(*pk_ed.to_montgomery().as_bytes())) | ||||
| } | ||||
| fn ed25519_to_x25519_sk(key: &ed::SigningKey) -> VeilidAPIResult<xd::StaticSecret> { | ||||
|     Ok(xd::StaticSecret::from(*key.to_scalar().as_bytes())) | ||||
| fn secret_to_x25519_sk(secret: &SecretKey) -> VeilidAPIResult<xd::StaticSecret> { | ||||
|     // NOTE: ed::SigningKey.to_scalar() does not produce an unreduced scalar, we want the raw bytes here | ||||
|     // See https://github.com/dalek-cryptography/curve25519-dalek/issues/565 | ||||
|     let hash: [u8; SIGNATURE_LENGTH] = ed::Sha512::default() | ||||
|         .chain_update(secret.bytes) | ||||
|         .finalize() | ||||
|         .into(); | ||||
|     let mut output = [0u8; SECRET_KEY_LENGTH]; | ||||
|     output.copy_from_slice(&hash[..SECRET_KEY_LENGTH]); | ||||
|  | ||||
|     Ok(xd::StaticSecret::from(output)) | ||||
| } | ||||
|  | ||||
| pub fn vld0_generate_keypair() -> KeyPair { | ||||
|     let mut csprng = VeilidRng {}; | ||||
|     let keypair = ed::SigningKey::generate(&mut csprng); | ||||
|     let dht_key = PublicKey::new( | ||||
|         keypair.to_keypair_bytes()[ed::SECRET_KEY_LENGTH..] | ||||
|             .try_into() | ||||
|             .expect("should fit"), | ||||
|     ); | ||||
|     let dht_key_secret = SecretKey::new( | ||||
|         keypair.to_keypair_bytes()[0..ed::SECRET_KEY_LENGTH] | ||||
|             .try_into() | ||||
|             .expect("should fit"), | ||||
|     ); | ||||
|     let signing_key = ed::SigningKey::generate(&mut csprng); | ||||
|     let verifying_key = signing_key.verifying_key(); | ||||
|     let dht_key = PublicKey::new(verifying_key.to_bytes()); | ||||
|     let dht_key_secret = SecretKey::new(signing_key.to_bytes()); | ||||
|  | ||||
|     KeyPair::new(dht_key, dht_key_secret) | ||||
| } | ||||
| @@ -130,11 +134,17 @@ impl CryptoSystem for CryptoSystemVLD0 { | ||||
|         SharedSecret::new(s) | ||||
|     } | ||||
|     fn compute_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult<SharedSecret> { | ||||
|         let pk_ed = ed::VerifyingKey::from_bytes(&key.bytes).map_err(VeilidAPIError::internal)?; | ||||
|         let pk_xd = ed25519_to_x25519_pk(&pk_ed)?; | ||||
|         let sk_ed = ed::SigningKey::from_bytes(&secret.bytes); | ||||
|         let sk_xd = ed25519_to_x25519_sk(&sk_ed)?; | ||||
|         Ok(SharedSecret::new(sk_xd.diffie_hellman(&pk_xd).to_bytes())) | ||||
|         let pk_xd = public_to_x25519_pk(&key)?; | ||||
|         let sk_xd = secret_to_x25519_sk(&secret)?; | ||||
|  | ||||
|         let dh_bytes = sk_xd.diffie_hellman(&pk_xd).to_bytes(); | ||||
|  | ||||
|         let mut hasher = blake3::Hasher::new(); | ||||
|         hasher.update(VEILID_DOMAIN_CRYPT); | ||||
|         hasher.update(&dh_bytes); | ||||
|         let output = hasher.finalize(); | ||||
|  | ||||
|         Ok(SharedSecret::new(*output.as_bytes())) | ||||
|     } | ||||
|     fn generate_keypair(&self) -> KeyPair { | ||||
|         vld0_generate_keypair() | ||||
| @@ -200,11 +210,11 @@ impl CryptoSystem for CryptoSystemVLD0 { | ||||
|         let keypair = ed::SigningKey::from_keypair_bytes(&kpb) | ||||
|             .map_err(|e| VeilidAPIError::parse_error("Keypair is invalid", e))?; | ||||
|  | ||||
|         let mut dig = Blake3Digest512::new(); | ||||
|         let mut dig: ed::Sha512 = ed::Sha512::default(); | ||||
|         dig.update(data); | ||||
|  | ||||
|         let sig_bytes = keypair | ||||
|             .sign_prehashed(dig, None) | ||||
|             .sign_prehashed(dig, Some(VEILID_DOMAIN_SIGN)) | ||||
|             .map_err(VeilidAPIError::internal)?; | ||||
|  | ||||
|         let sig = Signature::new(sig_bytes.to_bytes()); | ||||
| @@ -222,10 +232,11 @@ impl CryptoSystem for CryptoSystemVLD0 { | ||||
|         let pk = ed::VerifyingKey::from_bytes(&dht_key.bytes) | ||||
|             .map_err(|e| VeilidAPIError::parse_error("Public key is invalid", e))?; | ||||
|         let sig = ed::Signature::from_bytes(&signature.bytes); | ||||
|         let mut dig = Blake3Digest512::new(); | ||||
|  | ||||
|         let mut dig: ed::Sha512 = ed::Sha512::default(); | ||||
|         dig.update(data); | ||||
|  | ||||
|         pk.verify_prehashed_strict(dig, None, &sig) | ||||
|         pk.verify_prehashed_strict(dig, Some(VEILID_DOMAIN_SIGN), &sig) | ||||
|             .map_err(|e| VeilidAPIError::parse_error("Verification failed", e))?; | ||||
|         Ok(()) | ||||
|     } | ||||
|   | ||||
| @@ -79,21 +79,19 @@ pub async fn txt_lookup<S: AsRef<str>>(host: S) -> EyreResult<Vec<String>> { | ||||
|     cfg_if! { | ||||
|         if #[cfg(target_os = "windows")] { | ||||
|             use core::ffi::c_void; | ||||
|             use windows::core::PSTR; | ||||
|             use std::ffi::CStr; | ||||
|             use windows::core::{PSTR,PCSTR}; | ||||
|             use std::ffi::{CStr, CString}; | ||||
|             use windows::Win32::NetworkManagement::Dns::{DnsQuery_UTF8, DnsFree, DNS_TYPE_TEXT, DNS_QUERY_STANDARD, DNS_RECORDA, DnsFreeRecordList}; | ||||
|  | ||||
|             let mut out = Vec::new(); | ||||
|             unsafe { | ||||
|                 let mut p_query_results: *mut DNS_RECORDA = core::ptr::null_mut(); | ||||
|                 let status = DnsQuery_UTF8(host.as_ref(), DNS_TYPE_TEXT as u16, DNS_QUERY_STANDARD, core::ptr::null_mut(), &mut p_query_results as *mut *mut DNS_RECORDA, core::ptr::null_mut()); | ||||
|                 if status != 0 { | ||||
|                     bail!("Failed to resolve TXT record"); | ||||
|                 } | ||||
|                 let host = CString::new(host.as_ref()).wrap_err("invalid host string")?; | ||||
|                 DnsQuery_UTF8(PCSTR::from_raw(host.as_bytes_with_nul().as_ptr()), DNS_TYPE_TEXT, DNS_QUERY_STANDARD, None, &mut p_query_results as *mut *mut DNS_RECORDA, None).wrap_err("Failed to resolve TXT record")?; | ||||
|  | ||||
|                 let mut p_record: *mut DNS_RECORDA = p_query_results; | ||||
|                 while !p_record.is_null() { | ||||
|                     if (*p_record).wType == DNS_TYPE_TEXT as u16 { | ||||
|                     if (*p_record).wType == DNS_TYPE_TEXT.0 { | ||||
|                         let count:usize = (*p_record).Data.TXT.dwStringCount.try_into().unwrap(); | ||||
|                         let string_array: *const PSTR = &(*p_record).Data.TXT.pStringArray[0]; | ||||
|                         for n in 0..count { | ||||
| @@ -107,7 +105,7 @@ pub async fn txt_lookup<S: AsRef<str>>(host: S) -> EyreResult<Vec<String>> { | ||||
|                     } | ||||
|                     p_record = (*p_record).pNext; | ||||
|                 } | ||||
|                 DnsFree(p_query_results as *const c_void, DnsFreeRecordList); | ||||
|                 DnsFree(Some(p_query_results as *const c_void), DnsFreeRecordList); | ||||
|             } | ||||
|             Ok(out) | ||||
|  | ||||
| @@ -139,8 +137,8 @@ pub async fn ptr_lookup(ip_addr: IpAddr) -> EyreResult<String> { | ||||
|     cfg_if! { | ||||
|         if #[cfg(target_os = "windows")] { | ||||
|             use core::ffi::c_void; | ||||
|             use windows::core::PSTR; | ||||
|             use std::ffi::CStr; | ||||
|             use windows::core::{PSTR,PCSTR}; | ||||
|             use std::ffi::{CStr, CString}; | ||||
|             use windows::Win32::NetworkManagement::Dns::{DnsQuery_UTF8, DnsFree, DNS_TYPE_PTR, DNS_QUERY_STANDARD, DNS_RECORDA, DnsFreeRecordList}; | ||||
|  | ||||
|             let host = match ip_addr { | ||||
| @@ -159,14 +157,12 @@ pub async fn ptr_lookup(ip_addr: IpAddr) -> EyreResult<String> { | ||||
|  | ||||
|             unsafe { | ||||
|                 let mut p_query_results: *mut DNS_RECORDA = core::ptr::null_mut(); | ||||
|                 let status = DnsQuery_UTF8(host, DNS_TYPE_PTR as u16, DNS_QUERY_STANDARD, core::ptr::null_mut(), &mut p_query_results as *mut *mut DNS_RECORDA, core::ptr::null_mut()); | ||||
|                 if status != 0 { | ||||
|                     bail!("Failed to resolve PTR record"); | ||||
|                 } | ||||
|                 let host = CString::new(host).wrap_err("invalid host string")?; | ||||
|                 DnsQuery_UTF8(PCSTR::from_raw(host.as_bytes_with_nul().as_ptr()), DNS_TYPE_PTR, DNS_QUERY_STANDARD, None, &mut p_query_results as *mut *mut DNS_RECORDA, None).wrap_err("Failed to resolve PTR record")?; | ||||
|  | ||||
|                 let mut p_record: *mut DNS_RECORDA = p_query_results; | ||||
|                 while !p_record.is_null() { | ||||
|                     if (*p_record).wType == DNS_TYPE_PTR as u16 { | ||||
|                     if (*p_record).wType == DNS_TYPE_PTR.0 { | ||||
|                         let p_name_host: PSTR = (*p_record).Data.PTR.pNameHost; | ||||
|                         let c_str: &CStr = CStr::from_ptr(p_name_host.0 as *const i8); | ||||
|                         if let Ok(str_slice) = c_str.to_str() { | ||||
| @@ -176,7 +172,7 @@ pub async fn ptr_lookup(ip_addr: IpAddr) -> EyreResult<String> { | ||||
|                     } | ||||
|                     p_record = (*p_record).pNext; | ||||
|                 } | ||||
|                 DnsFree(p_query_results as *const c_void, DnsFreeRecordList); | ||||
|                 DnsFree(Some(p_query_results as *const c_void), DnsFreeRecordList); | ||||
|             } | ||||
|             bail!("No records returned"); | ||||
|         } else { | ||||
|   | ||||
| @@ -97,20 +97,16 @@ cfg_if! { | ||||
|  | ||||
|         pub fn setup() { | ||||
|             SETUP_ONCE.call_once(|| { | ||||
|                 cfg_if! { | ||||
|                     if #[cfg(feature = "tracing")] { | ||||
|                         use tracing_subscriber::{filter, fmt, prelude::*}; | ||||
|                         let mut filters = filter::Targets::new().with_default(filter::LevelFilter::TRACE); | ||||
|                         for ig in DEFAULT_LOG_IGNORE_LIST { | ||||
|                             filters = filters.with_target(ig, filter::LevelFilter::OFF); | ||||
|                         } | ||||
|                         let fmt_layer = fmt::layer(); | ||||
|                         tracing_subscriber::registry() | ||||
|                             .with(fmt_layer) | ||||
|                             .with(filters) | ||||
|                             .init(); | ||||
|                     } | ||||
|                 use tracing_subscriber::{filter, fmt, prelude::*}; | ||||
|                 let mut filters = filter::Targets::new().with_default(filter::LevelFilter::INFO); | ||||
|                 for ig in DEFAULT_LOG_IGNORE_LIST { | ||||
|                     filters = filters.with_target(ig, filter::LevelFilter::OFF); | ||||
|                 } | ||||
|                 let fmt_layer = fmt::layer(); | ||||
|                 tracing_subscriber::registry() | ||||
|                     .with(fmt_layer) | ||||
|                     .with(filters) | ||||
|                     .init(); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -1,8 +1,6 @@ | ||||
| use super::*; | ||||
| use lz4_flex::block; | ||||
|  | ||||
| use crate::apibail_generic; | ||||
|  | ||||
| pub fn compress_prepend_size(input: &[u8]) -> Vec<u8> { | ||||
|     block::compress_prepend_size(input) | ||||
| } | ||||
|   | ||||
| @@ -61,10 +61,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: collection | ||||
|       sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" | ||||
|       sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.17.1" | ||||
|     version: "1.17.2" | ||||
|   convert: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -168,14 +168,6 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.0.0" | ||||
|   js: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: js | ||||
|       sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.6.7" | ||||
|   json_annotation: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -212,18 +204,18 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: matcher | ||||
|       sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" | ||||
|       sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.12.15" | ||||
|     version: "0.12.16" | ||||
|   material_color_utilities: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: material_color_utilities | ||||
|       sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 | ||||
|       sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.2.0" | ||||
|     version: "0.5.0" | ||||
|   meta: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -329,10 +321,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: source_span | ||||
|       sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 | ||||
|       sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.9.1" | ||||
|     version: "1.10.0" | ||||
|   stack_trace: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -385,10 +377,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: test_api | ||||
|       sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb | ||||
|       sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.5.1" | ||||
|     version: "0.6.0" | ||||
|   typed_data: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -411,7 +403,15 @@ packages: | ||||
|       path: ".." | ||||
|       relative: true | ||||
|     source: path | ||||
|     version: "0.1.7" | ||||
|     version: "0.1.9" | ||||
|   web: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: web | ||||
|       sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.1.4-beta" | ||||
|   win32: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -437,5 +437,5 @@ packages: | ||||
|     source: hosted | ||||
|     version: "3.5.0" | ||||
| sdks: | ||||
|   dart: ">=3.0.0 <4.0.0" | ||||
|   dart: ">=3.1.0-185.0.dev <4.0.0" | ||||
|   flutter: ">=3.10.6" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user