mirror of
https://gitlab.com/veilid/veilid.git
synced 2024-11-22 00:47:28 -06:00
Merge branch veilid:main into dev-setup-macos/fix-java-version-detection
This commit is contained in:
commit
05d63f1eb6
@ -46,7 +46,7 @@ pub trait CryptoSystem {
|
||||
|
||||
// Authentication
|
||||
fn sign(&self, key: &PublicKey, secret: &SecretKey, data: &[u8]) -> VeilidAPIResult<Signature>;
|
||||
fn verify(&self, key: &PublicKey, data: &[u8], signature: &Signature) -> VeilidAPIResult<()>;
|
||||
fn verify(&self, key: &PublicKey, data: &[u8], signature: &Signature) -> VeilidAPIResult<bool>;
|
||||
|
||||
// AEAD Encrypt/Decrypt
|
||||
fn aead_overhead(&self) -> usize;
|
||||
|
@ -172,9 +172,12 @@ impl Envelope {
|
||||
);
|
||||
|
||||
// Validate signature
|
||||
vcrypto
|
||||
if !vcrypto
|
||||
.verify(&sender_id, &data[0..(data.len() - 64)], &signature)
|
||||
.map_err(VeilidAPIError::internal)?;
|
||||
.map_err(VeilidAPIError::internal)?
|
||||
{
|
||||
apibail_parse_error!("signature verification of envelope failed", signature);
|
||||
}
|
||||
|
||||
// Return envelope
|
||||
Ok(Self {
|
||||
|
@ -238,27 +238,28 @@ impl Crypto {
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// returns an error
|
||||
/// Returns Some() the set of signature cryptokinds that validate and are supported
|
||||
/// Returns None if any cryptokinds are supported and do not validate
|
||||
pub fn verify_signatures(
|
||||
&self,
|
||||
node_ids: &[TypedKey],
|
||||
public_keys: &[TypedKey],
|
||||
data: &[u8],
|
||||
typed_signatures: &[TypedSignature],
|
||||
) -> VeilidAPIResult<TypedKeyGroup> {
|
||||
let mut out = TypedKeyGroup::with_capacity(node_ids.len());
|
||||
) -> VeilidAPIResult<Option<TypedKeyGroup>> {
|
||||
let mut out = TypedKeyGroup::with_capacity(public_keys.len());
|
||||
for sig in typed_signatures {
|
||||
for nid in node_ids {
|
||||
for nid in public_keys {
|
||||
if nid.kind == sig.kind {
|
||||
if let Some(vcrypto) = self.get(sig.kind) {
|
||||
vcrypto.verify(&nid.value, data, &sig.value)?;
|
||||
if !vcrypto.verify(&nid.value, data, &sig.value)? {
|
||||
return Ok(None);
|
||||
}
|
||||
out.add(*nid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
Ok(Some(out))
|
||||
}
|
||||
|
||||
/// Signature set generation
|
||||
|
@ -143,13 +143,13 @@ impl CryptoSystem for CryptoSystemNONE {
|
||||
// Validation
|
||||
fn validate_keypair(&self, dht_key: &PublicKey, dht_key_secret: &SecretKey) -> bool {
|
||||
let data = vec![0u8; 512];
|
||||
let sig = match self.sign(dht_key, dht_key_secret, &data) {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
return false;
|
||||
}
|
||||
let Ok(sig) = self.sign(dht_key, dht_key_secret, &data) else {
|
||||
return false;
|
||||
};
|
||||
self.verify(dht_key, &data, &sig).is_ok()
|
||||
let Ok(v) = self.verify(dht_key, &data, &sig) else {
|
||||
return false;
|
||||
};
|
||||
v
|
||||
}
|
||||
fn validate_hash(&self, data: &[u8], dht_key: &PublicKey) -> bool {
|
||||
let bytes = *blake3::hash(data).as_bytes();
|
||||
@ -205,7 +205,7 @@ impl CryptoSystem for CryptoSystemNONE {
|
||||
dht_key: &PublicKey,
|
||||
data: &[u8],
|
||||
signature: &Signature,
|
||||
) -> VeilidAPIResult<()> {
|
||||
) -> VeilidAPIResult<bool> {
|
||||
let mut dig = Blake3Digest512::new();
|
||||
dig.update(data);
|
||||
let sig = dig.finalize();
|
||||
@ -217,19 +217,13 @@ impl CryptoSystem for CryptoSystemNONE {
|
||||
.copy_from_slice(&do_xor_32(&in_sig_bytes[32..64], &signature.bytes[32..64]));
|
||||
|
||||
if !is_bytes_eq_32(&verify_bytes[0..32], 0u8) {
|
||||
return Err(VeilidAPIError::parse_error(
|
||||
"Verification failed",
|
||||
"signature 0..32 is invalid",
|
||||
));
|
||||
return Ok(false);
|
||||
}
|
||||
if !is_bytes_eq_32(&do_xor_32(&verify_bytes[32..64], &dht_key.bytes), 0xFFu8) {
|
||||
return Err(VeilidAPIError::parse_error(
|
||||
"Verification failed",
|
||||
"signature 32..64 is invalid",
|
||||
));
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
// AEAD Encrypt/Decrypt
|
||||
|
@ -129,9 +129,12 @@ impl Receipt {
|
||||
);
|
||||
|
||||
// Validate signature
|
||||
vcrypto
|
||||
if !vcrypto
|
||||
.verify(&sender_id, &data[0..(data.len() - 64)], &signature)
|
||||
.map_err(VeilidAPIError::generic)?;
|
||||
.map_err(VeilidAPIError::generic)?
|
||||
{
|
||||
apibail_parse_error!("signature failure in receipt", signature);
|
||||
}
|
||||
|
||||
// Get nonce
|
||||
let nonce: Nonce = Nonce::new(
|
||||
|
@ -64,49 +64,55 @@ pub async fn test_sign_and_verify(vcrypto: CryptoSystemVersion) {
|
||||
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key, LOREM_IPSUM.as_bytes(), &a1),
|
||||
Ok(())
|
||||
Ok(true)
|
||||
);
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key2, LOREM_IPSUM.as_bytes(), &a2),
|
||||
Ok(())
|
||||
Ok(true)
|
||||
);
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key, LOREM_IPSUM.as_bytes(), &a2),
|
||||
Ok(false)
|
||||
);
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key2, LOREM_IPSUM.as_bytes(), &a1),
|
||||
Ok(false)
|
||||
);
|
||||
assert!(vcrypto
|
||||
.verify(&dht_key, LOREM_IPSUM.as_bytes(), &a2)
|
||||
.is_err());
|
||||
assert!(vcrypto
|
||||
.verify(&dht_key2, LOREM_IPSUM.as_bytes(), &a1)
|
||||
.is_err());
|
||||
|
||||
// Try verifications that should work
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key, LOREM_IPSUM.as_bytes(), &dht_sig),
|
||||
Ok(())
|
||||
Ok(true)
|
||||
);
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key, LOREM_IPSUM.as_bytes(), &dht_sig_b),
|
||||
Ok(())
|
||||
Ok(true)
|
||||
);
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key2, LOREM_IPSUM.as_bytes(), &dht_sig2),
|
||||
Ok(())
|
||||
Ok(true)
|
||||
);
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key, CHEEZBURGER.as_bytes(), &dht_sig_c),
|
||||
Ok(())
|
||||
Ok(true)
|
||||
);
|
||||
// Try verifications that shouldn't work
|
||||
assert!(vcrypto
|
||||
.verify(&dht_key2, LOREM_IPSUM.as_bytes(), &dht_sig)
|
||||
.is_err());
|
||||
assert!(vcrypto
|
||||
.verify(&dht_key, LOREM_IPSUM.as_bytes(), &dht_sig2)
|
||||
.is_err());
|
||||
assert!(vcrypto
|
||||
.verify(&dht_key2, CHEEZBURGER.as_bytes(), &dht_sig_c)
|
||||
.is_err());
|
||||
assert!(vcrypto
|
||||
.verify(&dht_key, CHEEZBURGER.as_bytes(), &dht_sig)
|
||||
.is_err());
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key2, LOREM_IPSUM.as_bytes(), &dht_sig),
|
||||
Ok(false)
|
||||
);
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key, LOREM_IPSUM.as_bytes(), &dht_sig2),
|
||||
Ok(false)
|
||||
);
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key2, CHEEZBURGER.as_bytes(), &dht_sig_c),
|
||||
Ok(false)
|
||||
);
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key, CHEEZBURGER.as_bytes(), &dht_sig),
|
||||
Ok(false)
|
||||
);
|
||||
}
|
||||
|
||||
pub async fn test_key_conversions(vcrypto: CryptoSystemVersion) {
|
||||
|
@ -161,13 +161,13 @@ impl CryptoSystem for CryptoSystemVLD0 {
|
||||
// Validation
|
||||
fn validate_keypair(&self, dht_key: &PublicKey, dht_key_secret: &SecretKey) -> bool {
|
||||
let data = vec![0u8; 512];
|
||||
let sig = match self.sign(dht_key, dht_key_secret, &data) {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
return false;
|
||||
}
|
||||
let Ok(sig) = self.sign(dht_key, dht_key_secret, &data) else {
|
||||
return false;
|
||||
};
|
||||
self.verify(dht_key, &data, &sig).is_ok()
|
||||
let Ok(v) = self.verify(dht_key, &data, &sig) else {
|
||||
return false;
|
||||
};
|
||||
v
|
||||
}
|
||||
fn validate_hash(&self, data: &[u8], dht_key: &PublicKey) -> bool {
|
||||
let bytes = *blake3::hash(data).as_bytes();
|
||||
@ -219,7 +219,9 @@ impl CryptoSystem for CryptoSystemVLD0 {
|
||||
|
||||
let sig = Signature::new(sig_bytes.to_bytes());
|
||||
|
||||
self.verify(dht_key, data, &sig)?;
|
||||
if !self.verify(dht_key, data, &sig)? {
|
||||
apibail_internal!("newly created signature does not verify");
|
||||
}
|
||||
|
||||
Ok(sig)
|
||||
}
|
||||
@ -228,7 +230,7 @@ impl CryptoSystem for CryptoSystemVLD0 {
|
||||
dht_key: &PublicKey,
|
||||
data: &[u8],
|
||||
signature: &Signature,
|
||||
) -> VeilidAPIResult<()> {
|
||||
) -> VeilidAPIResult<bool> {
|
||||
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);
|
||||
@ -236,9 +238,13 @@ impl CryptoSystem for CryptoSystemVLD0 {
|
||||
let mut dig: ed::Sha512 = ed::Sha512::default();
|
||||
dig.update(data);
|
||||
|
||||
pk.verify_prehashed_strict(dig, Some(VEILID_DOMAIN_SIGN), &sig)
|
||||
.map_err(|e| VeilidAPIError::parse_error("Verification failed", e))?;
|
||||
Ok(())
|
||||
if pk
|
||||
.verify_prehashed_strict(dig, Some(VEILID_DOMAIN_SIGN), &sig)
|
||||
.is_err()
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
// AEAD Encrypt/Decrypt
|
||||
|
@ -694,9 +694,16 @@ impl RouteSpecStore {
|
||||
}
|
||||
} else {
|
||||
// Verify a signature for a hop node along the route
|
||||
if let Err(e) = vcrypto.verify(hop_public_key, data, &signatures[hop_n]) {
|
||||
log_rpc!(debug "failed to verify signature for hop {} at {} on private route {}: {}", hop_n, hop_public_key, public_key, e);
|
||||
return None;
|
||||
match vcrypto.verify(hop_public_key, data, &signatures[hop_n]) {
|
||||
Ok(true) => {}
|
||||
Ok(false) => {
|
||||
log_rpc!(debug "invalid signature for hop {} at {} on private route {}", hop_n, hop_public_key, public_key);
|
||||
return None;
|
||||
}
|
||||
Err(e) => {
|
||||
log_rpc!(debug "errir verifying signature for hop {} at {} on private route {}: {}", hop_n, hop_public_key, public_key, e);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,8 +27,11 @@ impl SignedDirectNodeInfo {
|
||||
let node_info_bytes = Self::make_signature_bytes(&self.node_info, self.timestamp)?;
|
||||
|
||||
// Verify the signatures that we can
|
||||
let validated_node_ids =
|
||||
let opt_validated_node_ids =
|
||||
crypto.verify_signatures(node_ids, &node_info_bytes, &self.signatures)?;
|
||||
let Some(validated_node_ids) = opt_validated_node_ids else {
|
||||
apibail_generic!("verification error in direct node info");
|
||||
};
|
||||
if validated_node_ids.is_empty() {
|
||||
apibail_generic!("no valid node ids in direct node info");
|
||||
}
|
||||
|
@ -53,8 +53,11 @@ impl SignedRelayedNodeInfo {
|
||||
&self.relay_info,
|
||||
self.timestamp,
|
||||
)?;
|
||||
let validated_node_ids =
|
||||
let opt_validated_node_ids =
|
||||
crypto.verify_signatures(node_ids, &node_info_bytes, &self.signatures)?;
|
||||
let Some(validated_node_ids) = opt_validated_node_ids else {
|
||||
apibail_generic!("verification error in relayed node info");
|
||||
};
|
||||
if validated_node_ids.is_empty() {
|
||||
apibail_generic!("no valid node ids in relayed node info");
|
||||
}
|
||||
|
@ -142,13 +142,16 @@ impl RPCOperationGetValueA {
|
||||
};
|
||||
|
||||
// And the signed value data
|
||||
value
|
||||
if !value
|
||||
.validate(
|
||||
descriptor.owner(),
|
||||
get_value_context.subkey,
|
||||
get_value_context.vcrypto.clone(),
|
||||
)
|
||||
.map_err(RPCError::protocol)?;
|
||||
.map_err(RPCError::protocol)?
|
||||
{
|
||||
return Err(RPCError::protocol("signed value data did not validate"));
|
||||
}
|
||||
}
|
||||
|
||||
PeerInfo::validate_vec(&mut self.peers, validate_context.crypto.clone());
|
||||
|
@ -149,13 +149,16 @@ impl RPCOperationSetValueA {
|
||||
|
||||
if let Some(value) = &self.value {
|
||||
// And the signed value data
|
||||
value
|
||||
if !value
|
||||
.validate(
|
||||
set_value_context.descriptor.owner(),
|
||||
set_value_context.subkey,
|
||||
set_value_context.vcrypto.clone(),
|
||||
)
|
||||
.map_err(RPCError::protocol)?;
|
||||
.map_err(RPCError::protocol)?
|
||||
{
|
||||
return Err(RPCError::protocol("signed value data did not validate"));
|
||||
}
|
||||
}
|
||||
|
||||
PeerInfo::validate_vec(&mut self.peers, validate_context.crypto.clone());
|
||||
|
@ -88,9 +88,12 @@ impl RPCOperationWatchValueQ {
|
||||
self.count,
|
||||
self.watch_id,
|
||||
);
|
||||
vcrypto
|
||||
if !vcrypto
|
||||
.verify(&self.watcher, &sig_data, &self.signature)
|
||||
.map_err(RPCError::protocol)?;
|
||||
.map_err(RPCError::protocol)?
|
||||
{
|
||||
return Err(RPCError::protocol("failed to validate watcher signature"));
|
||||
}
|
||||
|
||||
// Count is zero means cancelling, so there should always be a watch id in this case
|
||||
if self.count == 0 && self.watch_id.is_none() {
|
||||
|
@ -332,7 +332,7 @@ impl StorageManager {
|
||||
if let Some(rpc_processor) = opt_rpc_processor {
|
||||
// Use the safety selection we opened the record with
|
||||
// Use the writer we opened with as the 'watcher' as well
|
||||
let opt_owvresult = self
|
||||
let opt_owvresult = match self
|
||||
.outbound_watch_value_cancel(
|
||||
rpc_processor,
|
||||
key,
|
||||
@ -342,7 +342,16 @@ impl StorageManager {
|
||||
active_watch.id,
|
||||
active_watch.watch_node,
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
{
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
log_stor!(debug
|
||||
"close record watch cancel failed: {}", e
|
||||
);
|
||||
None
|
||||
}
|
||||
};
|
||||
if let Some(owvresult) = opt_owvresult {
|
||||
if owvresult.expiration_ts.as_u64() != 0 {
|
||||
log_stor!(debug
|
||||
|
@ -20,7 +20,7 @@ impl SignedValueData {
|
||||
owner: &PublicKey,
|
||||
subkey: ValueSubkey,
|
||||
vcrypto: CryptoSystemVersion,
|
||||
) -> VeilidAPIResult<()> {
|
||||
) -> VeilidAPIResult<bool> {
|
||||
let node_info_bytes = Self::make_signature_bytes(&self.value_data, owner, subkey)?;
|
||||
// validate signature
|
||||
vcrypto.verify(self.value_data.writer(), &node_info_bytes, &self.signature)
|
||||
|
@ -19,7 +19,12 @@ impl SignedValueDescriptor {
|
||||
|
||||
pub fn validate(&self, vcrypto: CryptoSystemVersion) -> VeilidAPIResult<()> {
|
||||
// validate signature
|
||||
vcrypto.verify(&self.owner, &self.schema_data, &self.signature)?;
|
||||
if !vcrypto.verify(&self.owner, &self.schema_data, &self.signature)? {
|
||||
apibail_parse_error!(
|
||||
"failed to validate signature of signed value descriptor",
|
||||
self.signature
|
||||
);
|
||||
}
|
||||
// validate schema
|
||||
DHTSchema::try_from(self.schema_data.as_slice())?;
|
||||
Ok(())
|
||||
|
@ -18,6 +18,7 @@ pub struct CryptoSystemResponse {
|
||||
#[serde(tag = "cs_op")]
|
||||
pub enum CryptoSystemRequestOp {
|
||||
Release,
|
||||
Kind,
|
||||
CachedDh {
|
||||
#[schemars(with = "String")]
|
||||
key: PublicKey,
|
||||
@ -108,7 +109,7 @@ pub enum CryptoSystemRequestOp {
|
||||
#[schemars(with = "String")]
|
||||
data: Vec<u8>,
|
||||
#[schemars(with = "String")]
|
||||
secret: Signature,
|
||||
signature: Signature,
|
||||
},
|
||||
AeadOverhead,
|
||||
DecryptAead {
|
||||
@ -150,6 +151,10 @@ pub enum CryptoSystemRequestOp {
|
||||
pub enum CryptoSystemResponseOp {
|
||||
InvalidId,
|
||||
Release,
|
||||
Kind {
|
||||
#[schemars(with = "String")]
|
||||
value: CryptoKind,
|
||||
},
|
||||
CachedDh {
|
||||
#[serde(flatten)]
|
||||
#[schemars(with = "ApiResult<String>")]
|
||||
@ -219,7 +224,7 @@ pub enum CryptoSystemResponseOp {
|
||||
},
|
||||
Verify {
|
||||
#[serde(flatten)]
|
||||
result: ApiResult<()>,
|
||||
result: ApiResult<bool>,
|
||||
},
|
||||
AeadOverhead {
|
||||
value: u32,
|
||||
|
@ -201,8 +201,8 @@ pub enum ResponseOp {
|
||||
CryptoSystem(CryptoSystemResponse),
|
||||
VerifySignatures {
|
||||
#[serde(flatten)]
|
||||
#[schemars(with = "ApiResult<Vec<String>>")]
|
||||
result: ApiResultWithVecString<TypedKeyGroup>,
|
||||
#[schemars(with = "ApiResult<Option<Vec<String>>>")]
|
||||
result: ApiResultWithOptVecString<Option<TypedKeyGroup>>,
|
||||
},
|
||||
GenerateSignatures {
|
||||
#[serde(flatten)]
|
||||
@ -308,6 +308,21 @@ where
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(untagged)]
|
||||
pub enum ApiResultWithOptVecString<T>
|
||||
where
|
||||
T: Clone + fmt::Debug,
|
||||
{
|
||||
Ok {
|
||||
#[schemars(with = "Option<Vec<String>>")]
|
||||
value: T,
|
||||
},
|
||||
Err {
|
||||
error: VeilidAPIError,
|
||||
},
|
||||
}
|
||||
|
||||
pub fn emit_schemas(out: &mut HashMap<String, String>) {
|
||||
let schema_request = schema_for!(Request);
|
||||
let schema_recv_message = schema_for!(RecvMessage);
|
||||
|
@ -28,6 +28,15 @@ pub fn to_json_api_result_with_vec_string<T: Clone + fmt::Debug>(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_json_api_result_with_opt_vec_string<T: Clone + fmt::Debug>(
|
||||
r: VeilidAPIResult<T>,
|
||||
) -> json_api::ApiResultWithOptVecString<T> {
|
||||
match r {
|
||||
Err(e) => json_api::ApiResultWithOptVecString::Err { error: e },
|
||||
Ok(v) => json_api::ApiResultWithOptVecString::Ok { value: v },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_json_api_result_with_vec_u8(r: VeilidAPIResult<Vec<u8>>) -> json_api::ApiResultWithVecU8 {
|
||||
match r {
|
||||
Err(e) => json_api::ApiResultWithVecU8::Err { error: e },
|
||||
@ -462,6 +471,7 @@ impl JsonRequestProcessor {
|
||||
self.release_crypto_system(csr.cs_id);
|
||||
CryptoSystemResponseOp::Release {}
|
||||
}
|
||||
CryptoSystemRequestOp::Kind => CryptoSystemResponseOp::Kind { value: csv.kind() },
|
||||
CryptoSystemRequestOp::CachedDh { key, secret } => CryptoSystemResponseOp::CachedDh {
|
||||
result: to_json_api_result_with_string(csv.cached_dh(&key, &secret)),
|
||||
},
|
||||
@ -532,8 +542,12 @@ impl JsonRequestProcessor {
|
||||
CryptoSystemRequestOp::Sign { key, secret, data } => CryptoSystemResponseOp::Sign {
|
||||
result: to_json_api_result_with_string(csv.sign(&key, &secret, &data)),
|
||||
},
|
||||
CryptoSystemRequestOp::Verify { key, data, secret } => CryptoSystemResponseOp::Verify {
|
||||
result: to_json_api_result(csv.verify(&key, &data, &secret)),
|
||||
CryptoSystemRequestOp::Verify {
|
||||
key,
|
||||
data,
|
||||
signature,
|
||||
} => CryptoSystemResponseOp::Verify {
|
||||
result: to_json_api_result(csv.verify(&key, &data, &signature)),
|
||||
},
|
||||
CryptoSystemRequestOp::AeadOverhead => CryptoSystemResponseOp::AeadOverhead {
|
||||
value: csv.aead_overhead() as u32,
|
||||
@ -766,7 +780,7 @@ impl JsonRequestProcessor {
|
||||
}
|
||||
};
|
||||
ResponseOp::VerifySignatures {
|
||||
result: to_json_api_result_with_vec_string(crypto.verify_signatures(
|
||||
result: to_json_api_result_with_opt_vec_string(crypto.verify_signatures(
|
||||
&node_ids,
|
||||
&data,
|
||||
&signatures,
|
||||
|
@ -30,6 +30,8 @@ void main() {
|
||||
test('get cryptosystem', testGetCryptoSystem);
|
||||
test('get cryptosystem invalid', testGetCryptoSystemInvalid);
|
||||
test('hash and verify password', testHashAndVerifyPassword);
|
||||
test('sign and verify signature', testSignAndVerifySignature);
|
||||
test('sign and verify signatures', testSignAndVerifySignatures);
|
||||
});
|
||||
|
||||
group('Table DB Tests', () {
|
||||
|
@ -32,6 +32,41 @@ Future<void> testHashAndVerifyPassword() async {
|
||||
expect(await cs.verifyPassword(utf8.encode('abc1235'), phash), isFalse);
|
||||
}
|
||||
|
||||
Future<void> testSignAndVerifySignature() async {
|
||||
final cs = await Veilid.instance.bestCryptoSystem();
|
||||
final kp1 = await cs.generateKeyPair();
|
||||
final kp2 = await cs.generateKeyPair();
|
||||
|
||||
// Signature match
|
||||
final sig = await cs.sign(kp1.key, kp1.secret, utf8.encode('abc123'));
|
||||
expect(await cs.verify(kp1.key, utf8.encode('abc123'), sig), isTrue);
|
||||
|
||||
// Signature mismatch
|
||||
final sig2 = await cs.sign(kp1.key, kp1.secret, utf8.encode('abc1234'));
|
||||
expect(await cs.verify(kp1.key, utf8.encode('abc1234'), sig2), isTrue);
|
||||
expect(await cs.verify(kp1.key, utf8.encode('abc12345'), sig2), isFalse);
|
||||
expect(await cs.verify(kp2.key, utf8.encode('abc1234'), sig2), isFalse);
|
||||
}
|
||||
|
||||
Future<void> testSignAndVerifySignatures() async {
|
||||
final cs = await Veilid.instance.bestCryptoSystem();
|
||||
final kind = cs.kind();
|
||||
final kp1 = await cs.generateKeyPair();
|
||||
|
||||
// Signature match
|
||||
final sigs = await Veilid.instance.generateSignatures(
|
||||
utf8.encode('abc123'), [TypedKeyPair.fromKeyPair(kind, kp1)]);
|
||||
expect(
|
||||
await Veilid.instance.verifySignatures(
|
||||
[TypedKey(kind: kind, value: kp1.key)], utf8.encode('abc123'), sigs),
|
||||
equals([TypedKey(kind: kind, value: kp1.key)]));
|
||||
// Signature mismatch
|
||||
expect(
|
||||
await Veilid.instance.verifySignatures(
|
||||
[TypedKey(kind: kind, value: kp1.key)], utf8.encode('abc1234'), sigs),
|
||||
isNull);
|
||||
}
|
||||
|
||||
Future<void> testGenerateSharedSecret() async {
|
||||
final cs = await Veilid.instance.bestCryptoSystem();
|
||||
|
||||
|
@ -27,7 +27,7 @@ EXTERNAL SOURCES:
|
||||
SPEC CHECKSUMS:
|
||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||
macos_window_utils: 933f91f64805e2eb91a5bd057cf97cd097276663
|
||||
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
|
||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||
veilid: a54f57b7bcf0e4e072fe99272d76ca126b2026d0
|
||||
|
||||
PODFILE CHECKSUM: 73d2f470b1d889e27fcfda1d6e6efec66f98af3f
|
||||
|
@ -115,6 +115,13 @@ extension DHTRecordDescriptorExt on DHTRecordDescriptor {
|
||||
return KeyPair(key: owner, secret: ownerSecret!);
|
||||
}
|
||||
|
||||
TypedKey? ownerTypedSecret() {
|
||||
if (ownerSecret == null) {
|
||||
return null;
|
||||
}
|
||||
return TypedKey(kind: key.kind, value: ownerSecret!);
|
||||
}
|
||||
|
||||
TypedKeyPair? ownerTypedKeyPair() {
|
||||
if (ownerSecret == null) {
|
||||
return null;
|
||||
|
@ -44,6 +44,10 @@ Object? veilidApiToEncodable(Object? value) {
|
||||
List<T> Function(dynamic) jsonListConstructor<T>(
|
||||
T Function(dynamic) jsonConstructor) =>
|
||||
(dynamic j) => (j as List<dynamic>).map(jsonConstructor).toList();
|
||||
List<T>? Function(dynamic) optJsonListConstructor<T>(
|
||||
T Function(dynamic) jsonConstructor) =>
|
||||
(dynamic j) =>
|
||||
j == null ? null : (j as List<dynamic>).map(jsonConstructor).toList();
|
||||
|
||||
//////////////////////////////////////
|
||||
/// VeilidVersion
|
||||
@ -152,8 +156,8 @@ abstract class Veilid {
|
||||
List<CryptoKind> validCryptoKinds();
|
||||
Future<VeilidCryptoSystem> getCryptoSystem(CryptoKind kind);
|
||||
Future<VeilidCryptoSystem> bestCryptoSystem();
|
||||
Future<List<TypedKey>> verifySignatures(
|
||||
List<TypedKey> nodeIds, Uint8List data, List<TypedSignature> signatures);
|
||||
Future<List<TypedKey>?> verifySignatures(List<TypedKey> publicKeys,
|
||||
Uint8List data, List<TypedSignature> signatures);
|
||||
Future<List<TypedSignature>> generateSignatures(
|
||||
Uint8List data, List<TypedKeyPair> keyPairs);
|
||||
Future<TypedKeyPair> generateKeyPair(CryptoKind kind);
|
||||
|
@ -214,7 +214,7 @@ abstract class VeilidCryptoSystem {
|
||||
Future<Signature> signWithKeyPair(KeyPair keyPair, Uint8List data) =>
|
||||
sign(keyPair.key, keyPair.secret, data);
|
||||
|
||||
Future<void> verify(PublicKey key, Uint8List data, Signature signature);
|
||||
Future<bool> verify(PublicKey key, Uint8List data, Signature signature);
|
||||
Future<int> aeadOverhead();
|
||||
Future<Uint8List> decryptAead(Uint8List body, Nonce nonce,
|
||||
SharedSecret sharedSecret, Uint8List? associatedData);
|
||||
|
@ -1154,7 +1154,7 @@ class VeilidCryptoSystemFFI extends VeilidCryptoSystem {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> verify(
|
||||
Future<bool> verify(
|
||||
PublicKey key, Uint8List data, Signature signature) async {
|
||||
final nativeKey = jsonEncode(key).toNativeUtf8();
|
||||
final nativeEncodedData = base64UrlNoPadEncode(data).toNativeUtf8();
|
||||
@ -1164,7 +1164,7 @@ class VeilidCryptoSystemFFI extends VeilidCryptoSystem {
|
||||
final sendPort = recvPort.sendPort;
|
||||
_ffi._cryptoVerify(sendPort.nativePort, _kind, nativeKey, nativeEncodedData,
|
||||
nativeSignature);
|
||||
return processFutureVoid(recvPort.first);
|
||||
return processFuturePlain(recvPort.first);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -1742,7 +1742,7 @@ class VeilidFFI extends Veilid {
|
||||
VeilidCryptoSystemFFI._(this, _bestCryptoKind());
|
||||
|
||||
@override
|
||||
Future<List<TypedKey>> verifySignatures(List<TypedKey> nodeIds,
|
||||
Future<List<TypedKey>?> verifySignatures(List<TypedKey> nodeIds,
|
||||
Uint8List data, List<TypedSignature> signatures) async {
|
||||
final nativeNodeIds = jsonEncode(nodeIds).toNativeUtf8();
|
||||
final nativeData = base64UrlNoPadEncode(data).toNativeUtf8();
|
||||
@ -1752,7 +1752,7 @@ class VeilidFFI extends Veilid {
|
||||
final sendPort = recvPort.sendPort;
|
||||
_verifySignatures(
|
||||
sendPort.nativePort, nativeNodeIds, nativeData, nativeSignatures);
|
||||
return processFutureJson(
|
||||
return processFutureOptJson(
|
||||
jsonListConstructor<TypedKey>(TypedKey.fromJson), recvPort.first);
|
||||
}
|
||||
|
||||
|
@ -359,7 +359,7 @@ class VeilidCryptoSystemJS extends VeilidCryptoSystem {
|
||||
]))));
|
||||
|
||||
@override
|
||||
Future<void> verify(PublicKey key, Uint8List data, Signature signature) =>
|
||||
Future<bool> verify(PublicKey key, Uint8List data, Signature signature) =>
|
||||
_wrapApiPromise(js_util.callMethod(wasm, 'crypto_verify', [
|
||||
_kind,
|
||||
jsonEncode(key),
|
||||
@ -655,10 +655,10 @@ class VeilidJS extends Veilid {
|
||||
this, js_util.callMethod(wasm, 'best_crypto_kind', []));
|
||||
|
||||
@override
|
||||
Future<List<TypedKey>> verifySignatures(List<TypedKey> nodeIds,
|
||||
Future<List<TypedKey>?> verifySignatures(List<TypedKey> nodeIds,
|
||||
Uint8List data, List<TypedSignature> signatures) async =>
|
||||
jsonListConstructor(TypedKey.fromJson)(jsonDecode(await _wrapApiPromise(
|
||||
js_util.callMethod(wasm, 'verify_signatures', [
|
||||
optJsonListConstructor(TypedKey.fromJson)(jsonDecode(
|
||||
await _wrapApiPromise(js_util.callMethod(wasm, 'verify_signatures', [
|
||||
jsonEncode(nodeIds),
|
||||
base64UrlNoPadEncode(data),
|
||||
jsonEncode(signatures)
|
||||
|
@ -1525,8 +1525,8 @@ pub extern "C" fn crypto_verify(
|
||||
let csv = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument("crypto_verify", "kind", kind.to_string())
|
||||
})?;
|
||||
csv.verify(&key, &data, &signature)?;
|
||||
APIRESULT_VOID
|
||||
let out = csv.verify(&key, &data, &signature)?;
|
||||
APIResult::Ok(out)
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,39 @@ async def test_hash_and_verify_password(api_connection: veilid.VeilidAPI):
|
||||
phash2 = await cs.hash_password(b"abc1234", salt)
|
||||
assert not await cs.verify_password(b"abc12345", phash)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sign_and_verify_signature(api_connection: veilid.VeilidAPI):
|
||||
cs = await api_connection.best_crypto_system()
|
||||
async with cs:
|
||||
kp1 = await cs.generate_key_pair()
|
||||
kp2 = await cs.generate_key_pair()
|
||||
|
||||
# Signature match
|
||||
sig = await cs.sign(kp1.key(), kp1.secret(), b"abc123")
|
||||
assert await cs.verify(kp1.key(), b"abc123", sig)
|
||||
|
||||
# Signature mismatch
|
||||
sig2 = await cs.sign(kp1.key(), kp1.secret(), b"abc1234")
|
||||
assert await cs.verify(kp1.key(), b"abc1234", sig2)
|
||||
assert not await cs.verify(kp1.key(), b"abc12345", sig2)
|
||||
assert not await cs.verify(kp2.key(), b"abc1234", sig2)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sign_and_verify_signatures(api_connection: veilid.VeilidAPI):
|
||||
cs = await api_connection.best_crypto_system()
|
||||
async with cs:
|
||||
kind = await cs.kind()
|
||||
kp1 = await cs.generate_key_pair()
|
||||
|
||||
# Signature match
|
||||
sigs = await api_connection.generate_signatures(b"abc123", [veilid.TypedKeyPair.from_value(kind, kp1)])
|
||||
keys = [veilid.TypedKey.from_value(kind,kp1.key())]
|
||||
assert (await api_connection.verify_signatures(keys, b"abc123", sigs)) == keys
|
||||
|
||||
# Signature mismatch
|
||||
assert (await api_connection.verify_signatures([veilid.TypedKey.from_value(kind,kp1.key())], b"abc1234", sigs)) is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_generate_shared_secret(api_connection: veilid.VeilidAPI):
|
||||
|
@ -115,7 +115,7 @@ class TableDbTransaction(ABC):
|
||||
async def __aexit__(self, *excinfo):
|
||||
if not self.is_done():
|
||||
await self.rollback()
|
||||
|
||||
|
||||
@abstractmethod
|
||||
def is_done(self) -> bool:
|
||||
pass
|
||||
@ -186,6 +186,10 @@ class CryptoSystem(ABC):
|
||||
if not self.is_done():
|
||||
await self.release()
|
||||
|
||||
@abstractmethod
|
||||
def kind(self) -> types.CryptoKind:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def is_done(self) -> bool:
|
||||
pass
|
||||
@ -267,7 +271,7 @@ class CryptoSystem(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def verify(self, key: types.PublicKey, data: bytes, signature: types.Signature):
|
||||
async def verify(self, key: types.PublicKey, data: bytes, signature: types.Signature) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
@ -384,7 +388,7 @@ class VeilidAPI(ABC):
|
||||
node_ids: list[types.TypedKey],
|
||||
data: bytes,
|
||||
signatures: list[types.TypedSignature],
|
||||
) -> list[types.TypedKey]:
|
||||
) -> Optional[list[types.TypedKey]]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
|
@ -386,18 +386,21 @@ class _JsonVeilidAPI(VeilidAPI):
|
||||
|
||||
async def verify_signatures(
|
||||
self, node_ids: list[TypedKey], data: bytes, signatures: list[TypedSignature]
|
||||
) -> list[TypedKey]:
|
||||
) -> Optional[list[TypedKey]]:
|
||||
out = raise_api_result(
|
||||
await self.send_ndjson_request(
|
||||
Operation.VERIFY_SIGNATURES,
|
||||
node_ids=node_ids,
|
||||
data=data,
|
||||
signatures=signatures,
|
||||
)
|
||||
)
|
||||
if out is None:
|
||||
return out
|
||||
return list(
|
||||
map(
|
||||
lambda x: TypedKey(x),
|
||||
raise_api_result(
|
||||
await self.send_ndjson_request(
|
||||
Operation.VERIFY_SIGNATURES,
|
||||
node_ids=node_ids,
|
||||
data=data,
|
||||
signatures=signatures,
|
||||
)
|
||||
),
|
||||
out
|
||||
)
|
||||
)
|
||||
|
||||
@ -938,6 +941,18 @@ class _JsonCryptoSystem(CryptoSystem):
|
||||
|
||||
# complain
|
||||
raise AssertionError("Should have released crypto system before dropping object")
|
||||
|
||||
async def kind(self) -> CryptoKind:
|
||||
return CryptoKind(
|
||||
raise_api_result(
|
||||
await self.api.send_ndjson_request(
|
||||
Operation.CRYPTO_SYSTEM,
|
||||
validate=validate_cs_op,
|
||||
cs_id=self.cs_id,
|
||||
cs_op=CryptoSystemOperation.KIND,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def is_done(self) -> bool:
|
||||
return self.done
|
||||
@ -1160,7 +1175,7 @@ class _JsonCryptoSystem(CryptoSystem):
|
||||
)
|
||||
|
||||
async def verify(self, key: PublicKey, data: bytes, signature: Signature):
|
||||
raise_api_result(
|
||||
return raise_api_result(
|
||||
await self.api.send_ndjson_request(
|
||||
Operation.CRYPTO_SYSTEM,
|
||||
validate=validate_cs_op,
|
||||
|
@ -73,6 +73,7 @@ class TableDbTransactionOperation(StrEnum):
|
||||
class CryptoSystemOperation(StrEnum):
|
||||
INVALID_ID = "InvalidId"
|
||||
RELEASE = "Release"
|
||||
KIND = "Kind"
|
||||
CACHED_DH = "CachedDh"
|
||||
COMPUTE_DH = "ComputeDh"
|
||||
GENERATE_SHARED_SECRET = "GenerateSharedSecret"
|
||||
|
@ -1599,6 +1599,24 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"cs_op",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"cs_op": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Kind"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"anyOf": [
|
||||
@ -2039,7 +2057,7 @@
|
||||
],
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": "null"
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -2205,12 +2223,12 @@
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": "array",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
@ -2450,7 +2468,7 @@
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "Operation Id (pairs with Request, or empty if unidirectional)",
|
||||
"description": "Operation Id (pairs with Request, or empty if unidirectional).",
|
||||
"default": 0,
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
@ -2465,10 +2483,11 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "An update from the veilid-core to the host application describing a change to the internal state of the Veilid node.",
|
||||
"type": "object",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "A VeilidCore log message with optional backtrace",
|
||||
"description": "A VeilidCore log message with optional backtrace.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
@ -2497,7 +2516,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Direct statement blob passed to hosting application for processing",
|
||||
"description": "Direct statement blob passed to hosting application for processing.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
@ -2528,7 +2547,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Direct question blob passed to hosting application for processing to send an eventual AppReply",
|
||||
"description": "Direct question blob passed to hosting application for processing to send an eventual AppReply.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"call_id",
|
||||
@ -2563,6 +2582,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Describe the attachment state of the Veilid node",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
@ -2578,17 +2598,25 @@
|
||||
]
|
||||
},
|
||||
"local_network_ready": {
|
||||
"description": "If attached and there are enough eachable nodes in the routing table to perform all the actions of the LocalNetwork RoutingDomain.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"public_internet_ready": {
|
||||
"description": "If attached and there are enough eachable nodes in the routing table to perform all the actions of the PublicInternet RoutingDomain, including things like private/safety route allocation and DHT operations.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"state": {
|
||||
"$ref": "#/definitions/AttachmentState"
|
||||
"description": "The overall quality of the routing table if attached, or the current state the attachment state machine.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AttachmentState"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Describe the current network state of the Veilid node",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"bps_down",
|
||||
@ -2599,9 +2627,11 @@
|
||||
],
|
||||
"properties": {
|
||||
"bps_down": {
|
||||
"description": "The total number of bytes per second used by Veilid currently in the download direction.",
|
||||
"type": "string"
|
||||
},
|
||||
"bps_up": {
|
||||
"description": "The total number of bytes per second used by Veilid currently in the upload direction.",
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
@ -2611,17 +2641,20 @@
|
||||
]
|
||||
},
|
||||
"peers": {
|
||||
"description": "The list of most recently accessed peers. This is not an active connection table, nor is representative of the entire routing table.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/PeerTableData"
|
||||
}
|
||||
},
|
||||
"started": {
|
||||
"description": "If the network has been started or not.",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Describe changes to the Veilid node configuration Currently this is only ever emitted once, however we reserve the right to add the ability to change the configuration or have it changed by the Veilid node itself during runtime.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"config",
|
||||
@ -2629,7 +2662,12 @@
|
||||
],
|
||||
"properties": {
|
||||
"config": {
|
||||
"$ref": "#/definitions/VeilidConfigInner"
|
||||
"description": "If the Veilid node configuration has changed the full new config will be here.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/VeilidConfigInner"
|
||||
}
|
||||
]
|
||||
},
|
||||
"kind": {
|
||||
"type": "string",
|
||||
@ -2640,6 +2678,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Describe a private route change that has happened",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dead_remote_routes",
|
||||
@ -2648,12 +2687,14 @@
|
||||
],
|
||||
"properties": {
|
||||
"dead_remote_routes": {
|
||||
"description": "If a private route that was imported has died, it is listed here.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"dead_routes": {
|
||||
"description": "If a private route that was allocated has died, it is listed here.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
@ -2668,6 +2709,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Describe when DHT records have subkey values changed",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"count",
|
||||
@ -2677,11 +2719,13 @@
|
||||
],
|
||||
"properties": {
|
||||
"count": {
|
||||
"description": "The count remaining on the watch that triggered this value change If there is no watch and this is received, it will be set to u32::MAX If this value is zero, any watch present on the value has died.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"key": {
|
||||
"description": "The DHT Record key that changed",
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
@ -2691,6 +2735,7 @@
|
||||
]
|
||||
},
|
||||
"subkeys": {
|
||||
"description": "The portion of the DHT Record's subkeys that have changed If the subkey range is empty, any watch present on the value has died.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "array",
|
||||
@ -2711,6 +2756,7 @@
|
||||
}
|
||||
},
|
||||
"value": {
|
||||
"description": "The (optional) value data for the first subkey in the subkeys range If 'subkeys' is not a single value, other values than the first value must be retrieved with RoutingContext::get_dht_value().",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ValueData"
|
||||
@ -2752,7 +2798,7 @@
|
||||
],
|
||||
"definitions": {
|
||||
"AttachmentState": {
|
||||
"description": "Attachment abstraction for network 'signal strength'",
|
||||
"description": "Attachment abstraction for network 'signal strength'.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Detached",
|
||||
@ -2949,7 +2995,7 @@
|
||||
}
|
||||
},
|
||||
"FourCC": {
|
||||
"description": "FOURCC code",
|
||||
"description": "FOURCC code.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer",
|
||||
@ -3023,6 +3069,7 @@
|
||||
}
|
||||
},
|
||||
"PeerTableData": {
|
||||
"description": "Describe a recently accessed peer",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"node_ids",
|
||||
@ -3031,16 +3078,23 @@
|
||||
],
|
||||
"properties": {
|
||||
"node_ids": {
|
||||
"description": "The node ids used by this peer",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"peer_address": {
|
||||
"description": "The peer's human readable address.",
|
||||
"type": "string"
|
||||
},
|
||||
"peer_stats": {
|
||||
"$ref": "#/definitions/PeerStats"
|
||||
"description": "Statistics we have collected on this peer.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/PeerStats"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -3100,10 +3154,10 @@
|
||||
}
|
||||
},
|
||||
"SafetySelection": {
|
||||
"description": "The choice of safety route to include in compiled routes",
|
||||
"description": "The choice of safety route to include in compiled routes.",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Don't use a safety route, only specify the sequencing preference",
|
||||
"description": "Don't use a safety route, only specify the sequencing preference.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Unsafe"
|
||||
@ -3116,7 +3170,7 @@
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Use a safety route and parameters specified by a SafetySpec",
|
||||
"description": "Use a safety route and parameters specified by a SafetySpec.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Safe"
|
||||
@ -3131,7 +3185,7 @@
|
||||
]
|
||||
},
|
||||
"SafetySpec": {
|
||||
"description": "Options for safety routes (sender privacy)",
|
||||
"description": "Options for safety routes (sender privacy).",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"hop_count",
|
||||
@ -3140,20 +3194,20 @@
|
||||
],
|
||||
"properties": {
|
||||
"hop_count": {
|
||||
"description": "must be greater than 0",
|
||||
"description": "Must be greater than 0.",
|
||||
"type": "integer",
|
||||
"format": "uint",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"preferred_route": {
|
||||
"description": "preferred safety route set id if it still exists",
|
||||
"description": "Preferred safety route set id if it still exists.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"sequencing": {
|
||||
"description": "prefer connection-oriented sequenced protocols",
|
||||
"description": "Prefer connection-oriented sequenced protocols.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Sequencing"
|
||||
@ -3161,7 +3215,7 @@
|
||||
]
|
||||
},
|
||||
"stability": {
|
||||
"description": "prefer reliability over speed",
|
||||
"description": "Prefer reliability over speed.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Stability"
|
||||
@ -3504,7 +3558,7 @@
|
||||
]
|
||||
},
|
||||
"VeilidConfigApplication": {
|
||||
"description": "Application configuration\n\nConfigure web access to the Progressive Web App (PWA)\n\nTo be implemented...",
|
||||
"description": "Application configuration.\n\nConfigure web access to the Progressive Web App (PWA).\n\nTo be implemented...",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"http",
|
||||
@ -3549,7 +3603,7 @@
|
||||
}
|
||||
},
|
||||
"VeilidConfigDHT": {
|
||||
"description": "Configure the Distributed Hash Table (DHT)",
|
||||
"description": "Configure the Distributed Hash Table (DHT).",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_value_count",
|
||||
@ -3689,7 +3743,7 @@
|
||||
}
|
||||
},
|
||||
"VeilidConfigHTTP": {
|
||||
"description": "Enable and configure HTTP access to the Veilid node\n\n```yaml http: enabled: false listen_address: ':5150' path: 'app\" url: 'https://localhost:5150' ```",
|
||||
"description": "Enable and configure HTTP access to the Veilid node.\n\n```yaml http: enabled: false listen_address: ':5150' path: 'app\" url: 'https://localhost:5150' ```",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enabled",
|
||||
@ -3715,7 +3769,7 @@
|
||||
}
|
||||
},
|
||||
"VeilidConfigHTTPS": {
|
||||
"description": "Enable and configure HTTPS access to the Veilid node\n\n```yaml https: enabled: false listen_address: ':5150' path: 'app' url: 'https://localhost:5150' ```",
|
||||
"description": "Enable and configure HTTPS access to the Veilid node.\n\n```yaml https: enabled: false listen_address: ':5150' path: 'app' url: 'https://localhost:5150' ```",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enabled",
|
||||
@ -3914,7 +3968,7 @@
|
||||
}
|
||||
},
|
||||
"VeilidConfigProtocol": {
|
||||
"description": "Configure Network Protocols\n\nVeilid can communicate over UDP, TCP, and Web Sockets.\n\nAll protocols are available by default, and the Veilid node will sort out which protocol is used for each peer connection.",
|
||||
"description": "Configure Network Protocols.\n\nVeilid can communicate over UDP, TCP, and Web Sockets.\n\nAll protocols are available by default, and the Veilid node will sort out which protocol is used for each peer connection.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"tcp",
|
||||
@ -3938,7 +3992,7 @@
|
||||
}
|
||||
},
|
||||
"VeilidConfigRPC": {
|
||||
"description": "Configure RPC",
|
||||
"description": "Configure RPC.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"concurrency",
|
||||
@ -3992,7 +4046,7 @@
|
||||
}
|
||||
},
|
||||
"VeilidConfigRoutingTable": {
|
||||
"description": "Configure the network routing table",
|
||||
"description": "Configure the network routing table.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"bootstrap",
|
||||
@ -4051,7 +4105,7 @@
|
||||
}
|
||||
},
|
||||
"VeilidConfigTCP": {
|
||||
"description": "Enable and configure TCP\n\n```yaml tcp: connect: true listen: true max_connections: 32 listen_address: ':5150' public_address: ''",
|
||||
"description": "Enable and configure TCP.\n\n```yaml tcp: connect: true listen: true max_connections: 32 listen_address: ':5150' public_address: ''",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"connect",
|
||||
@ -4083,7 +4137,7 @@
|
||||
}
|
||||
},
|
||||
"VeilidConfigTLS": {
|
||||
"description": "Configure TLS\n\n```yaml tls: certificate_path: /path/to/cert private_key_path: /path/to/private/key connection_initial_timeout_ms: 2000",
|
||||
"description": "Configure TLS.\n\n```yaml tls: certificate_path: /path/to/cert private_key_path: /path/to/private/key connection_initial_timeout_ms: 2000",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"certificate_path",
|
||||
@ -4120,7 +4174,7 @@
|
||||
}
|
||||
},
|
||||
"VeilidConfigUDP": {
|
||||
"description": "Enable and configure UDP\n\n```yaml udp: enabled: true socket_pool_size: 0 listen_address: ':5150' public_address: '' ```",
|
||||
"description": "Enable and configure UDP.\n\n```yaml udp: enabled: true socket_pool_size: 0 listen_address: ':5150' public_address: '' ```",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enabled",
|
||||
@ -4148,7 +4202,7 @@
|
||||
}
|
||||
},
|
||||
"VeilidConfigWS": {
|
||||
"description": "Enable and configure Web Sockets\n\n```yaml ws: connect: true listen: true max_connections: 32 listen_address: ':5150' path: 'ws' url: 'ws://localhost:5150/ws'",
|
||||
"description": "Enable and configure Web Sockets.\n\n```yaml ws: connect: true listen: true max_connections: 32 listen_address: ':5150' path: 'ws' url: 'ws://localhost:5150/ws'",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"connect",
|
||||
@ -4184,7 +4238,7 @@
|
||||
}
|
||||
},
|
||||
"VeilidConfigWSS": {
|
||||
"description": "Enable and configure Secure Web Sockets\n\n```yaml wss: connect: true listen: false max_connections: 32 listen_address: ':5150' path: 'ws' url: ''",
|
||||
"description": "Enable and configure Secure Web Sockets.\n\n```yaml wss: connect: true listen: false max_connections: 32 listen_address: ':5150' path: 'ws' url: ''",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"connect",
|
||||
@ -4220,7 +4274,7 @@
|
||||
}
|
||||
},
|
||||
"VeilidLogLevel": {
|
||||
"description": "Log level for VeilidCore",
|
||||
"description": "Log level for VeilidCore.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Error",
|
||||
@ -4231,6 +4285,7 @@
|
||||
]
|
||||
},
|
||||
"VeilidState": {
|
||||
"description": "A queriable state of the internals of veilid-core.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"attachment",
|
||||
@ -4250,6 +4305,7 @@
|
||||
}
|
||||
},
|
||||
"VeilidStateAttachment": {
|
||||
"description": "Describe the attachment state of the Veilid node",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"local_network_ready",
|
||||
@ -4258,28 +4314,42 @@
|
||||
],
|
||||
"properties": {
|
||||
"local_network_ready": {
|
||||
"description": "If attached and there are enough eachable nodes in the routing table to perform all the actions of the LocalNetwork RoutingDomain.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"public_internet_ready": {
|
||||
"description": "If attached and there are enough eachable nodes in the routing table to perform all the actions of the PublicInternet RoutingDomain, including things like private/safety route allocation and DHT operations.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"state": {
|
||||
"$ref": "#/definitions/AttachmentState"
|
||||
"description": "The overall quality of the routing table if attached, or the current state the attachment state machine.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AttachmentState"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"VeilidStateConfig": {
|
||||
"description": "Describe changes to the Veilid node configuration Currently this is only ever emitted once, however we reserve the right to add the ability to change the configuration or have it changed by the Veilid node itself during runtime.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"config"
|
||||
],
|
||||
"properties": {
|
||||
"config": {
|
||||
"$ref": "#/definitions/VeilidConfigInner"
|
||||
"description": "If the Veilid node configuration has changed the full new config will be here.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/VeilidConfigInner"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"VeilidStateNetwork": {
|
||||
"description": "Describe the current network state of the Veilid node",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"bps_down",
|
||||
@ -4289,18 +4359,22 @@
|
||||
],
|
||||
"properties": {
|
||||
"bps_down": {
|
||||
"description": "The total number of bytes per second used by Veilid currently in the download direction.",
|
||||
"type": "string"
|
||||
},
|
||||
"bps_up": {
|
||||
"description": "The total number of bytes per second used by Veilid currently in the upload direction.",
|
||||
"type": "string"
|
||||
},
|
||||
"peers": {
|
||||
"description": "The list of most recently accessed peers. This is not an active connection table, nor is representative of the entire routing table.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/PeerTableData"
|
||||
}
|
||||
},
|
||||
"started": {
|
||||
"description": "If the network has been started or not.",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
|
@ -963,6 +963,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"cs_op"
|
||||
],
|
||||
"properties": {
|
||||
"cs_op": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Kind"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -1291,7 +1305,7 @@
|
||||
"cs_op",
|
||||
"data",
|
||||
"key",
|
||||
"secret"
|
||||
"signature"
|
||||
],
|
||||
"properties": {
|
||||
"cs_op": {
|
||||
@ -1306,7 +1320,7 @@
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"secret": {
|
||||
"signature": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@ -1586,7 +1600,7 @@
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "Operation Id (pairs with Response, or empty if unidirectional)",
|
||||
"description": "Operation Id (pairs with Response, or empty if unidirectional).",
|
||||
"default": 0,
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
@ -1712,10 +1726,10 @@
|
||||
}
|
||||
},
|
||||
"SafetySelection": {
|
||||
"description": "The choice of safety route to include in compiled routes",
|
||||
"description": "The choice of safety route to include in compiled routes.",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Don't use a safety route, only specify the sequencing preference",
|
||||
"description": "Don't use a safety route, only specify the sequencing preference.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Unsafe"
|
||||
@ -1728,7 +1742,7 @@
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Use a safety route and parameters specified by a SafetySpec",
|
||||
"description": "Use a safety route and parameters specified by a SafetySpec.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Safe"
|
||||
@ -1743,7 +1757,7 @@
|
||||
]
|
||||
},
|
||||
"SafetySpec": {
|
||||
"description": "Options for safety routes (sender privacy)",
|
||||
"description": "Options for safety routes (sender privacy).",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"hop_count",
|
||||
@ -1752,20 +1766,20 @@
|
||||
],
|
||||
"properties": {
|
||||
"hop_count": {
|
||||
"description": "must be greater than 0",
|
||||
"description": "Must be greater than 0.",
|
||||
"type": "integer",
|
||||
"format": "uint",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"preferred_route": {
|
||||
"description": "preferred safety route set id if it still exists",
|
||||
"description": "Preferred safety route set id if it still exists.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"sequencing": {
|
||||
"description": "prefer connection-oriented sequenced protocols",
|
||||
"description": "Prefer connection-oriented sequenced protocols.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Sequencing"
|
||||
@ -1773,7 +1787,7 @@
|
||||
]
|
||||
},
|
||||
"stability": {
|
||||
"description": "prefer reliability over speed",
|
||||
"description": "Prefer reliability over speed.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Stability"
|
||||
|
@ -1353,14 +1353,14 @@ pub fn crypto_verify(kind: u32, key: String, data: String, signature: String) ->
|
||||
.unwrap();
|
||||
let signature: veilid_core::Signature = veilid_core::deserialize_json(&signature).unwrap();
|
||||
|
||||
wrap_api_future_void(async move {
|
||||
wrap_api_future_plain(async move {
|
||||
let veilid_api = get_veilid_api()?;
|
||||
let crypto = veilid_api.crypto()?;
|
||||
let csv = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument("crypto_verify", "kind", kind.to_string())
|
||||
})?;
|
||||
csv.verify(&key, &data, &signature)?;
|
||||
APIRESULT_UNDEFINED
|
||||
let out = csv.verify(&key, &data, &signature)?;
|
||||
APIResult::Ok(out)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -207,7 +207,7 @@ impl VeilidCrypto {
|
||||
node_ids: StringArray,
|
||||
data: Box<[u8]>,
|
||||
signatures: StringArray,
|
||||
) -> VeilidAPIResult<StringArray> {
|
||||
) -> VeilidAPIResult<Option<StringArray>> {
|
||||
let node_ids = into_unchecked_string_vec(node_ids);
|
||||
let node_ids: Vec<TypedKey> = node_ids
|
||||
.iter()
|
||||
@ -238,12 +238,15 @@ impl VeilidCrypto {
|
||||
|
||||
let veilid_api = get_veilid_api()?;
|
||||
let crypto = veilid_api.crypto()?;
|
||||
let out = crypto.verify_signatures(&node_ids, &data, &typed_signatures)?;
|
||||
let out = out
|
||||
.iter()
|
||||
.map(|item| item.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
let out = into_unchecked_string_array(out);
|
||||
let out = crypto
|
||||
.verify_signatures(&node_ids, &data, &typed_signatures)?
|
||||
.map(|sigs| {
|
||||
let out = sigs
|
||||
.iter()
|
||||
.map(|item| item.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
into_unchecked_string_array(out)
|
||||
});
|
||||
APIResult::Ok(out)
|
||||
}
|
||||
|
||||
@ -375,7 +378,12 @@ impl VeilidCrypto {
|
||||
APIResult::Ok(out.to_string())
|
||||
}
|
||||
|
||||
pub fn verify(kind: String, key: String, data: Box<[u8]>, signature: String) -> APIResult<()> {
|
||||
pub fn verify(
|
||||
kind: String,
|
||||
key: String,
|
||||
data: Box<[u8]>,
|
||||
signature: String,
|
||||
) -> APIResult<bool> {
|
||||
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
||||
|
||||
let key: veilid_core::PublicKey = veilid_core::PublicKey::from_str(&key)?;
|
||||
@ -386,8 +394,8 @@ impl VeilidCrypto {
|
||||
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||
veilid_core::VeilidAPIError::invalid_argument("crypto_verify", "kind", kind.to_string())
|
||||
})?;
|
||||
crypto_system.verify(&key, &data, &signature)?;
|
||||
APIRESULT_UNDEFINED
|
||||
let out = crypto_system.verify(&key, &data, &signature)?;
|
||||
APIResult::Ok(out)
|
||||
}
|
||||
|
||||
pub fn aeadOverhead(kind: String) -> APIResult<usize> {
|
||||
|
@ -138,7 +138,7 @@ describe('veilidCrypto', () => {
|
||||
|
||||
expect(() => {
|
||||
const res = veilidCrypto.verify(bestKind, publicKey, data, sig);
|
||||
expect(res).toBeUndefined();
|
||||
expect(res).toBe(true);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user