refactor: update dependencies and remove unused code
- Added new dependencies: `adler2`, `crc32fast`, `flate2`, `miniz_oxide`, and `libredox`. - Updated existing dependencies: `tokio-rustls` to version 0.26.4 and `filetime` to version 0.2.27. - Removed the `backup.rs` file as it is no longer needed. - Introduced tests for configuration and credential management. - Enhanced the `identity` module to generate W3C compliant DID documents. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -105,6 +105,11 @@ impl NodeIdentity {
|
||||
pub fn did_key(&self) -> String {
|
||||
did_key_from_pubkey_hex(&self.pubkey_hex()).expect("pubkey_hex is valid")
|
||||
}
|
||||
|
||||
/// Generate a W3C DID Core v1.0 compliant DID Document.
|
||||
pub fn did_document(&self) -> serde_json::Value {
|
||||
did_document_from_pubkey_hex(&self.pubkey_hex()).expect("pubkey_hex is valid")
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert Ed25519 pubkey (hex) to did:key format.
|
||||
@@ -121,6 +126,77 @@ pub fn did_key_from_pubkey_hex(pubkey_hex: &str) -> Result<String> {
|
||||
Ok(format!("did:key:z{}", bs58::encode(multicodec_pubkey).into_string()))
|
||||
}
|
||||
|
||||
/// Generate a W3C DID Core v1.0 compliant DID Document from an Ed25519 public key.
|
||||
/// Follows: https://www.w3.org/TR/did-core/
|
||||
/// Includes: verificationMethod, authentication, assertionMethod, keyAgreement contexts.
|
||||
pub fn did_document_from_pubkey_hex(pubkey_hex: &str) -> Result<serde_json::Value> {
|
||||
let did = did_key_from_pubkey_hex(pubkey_hex)?;
|
||||
let pubkey_bytes = hex::decode(pubkey_hex).context("Invalid pubkey hex")?;
|
||||
let pubkey_multibase = format!("z{}", bs58::encode(&pubkey_bytes).into_string());
|
||||
let key_id = format!("{}#key-1", did);
|
||||
|
||||
// Build X25519 key agreement key from Ed25519 public key
|
||||
// Ed25519 -> X25519 conversion (Montgomery form)
|
||||
let ed_point = curve25519_dalek::edwards::CompressedEdwardsY(
|
||||
pubkey_bytes
|
||||
.as_slice()
|
||||
.try_into()
|
||||
.map_err(|_| anyhow::anyhow!("Invalid pubkey length"))?,
|
||||
);
|
||||
let x25519_key = if let Some(point) = ed_point.decompress() {
|
||||
let montgomery = point.to_montgomery();
|
||||
format!("z{}", bs58::encode(montgomery.as_bytes()).into_string())
|
||||
} else {
|
||||
// Fallback: use Ed25519 key if conversion fails
|
||||
pubkey_multibase.clone()
|
||||
};
|
||||
let x25519_key_id = format!("{}#key-x25519-1", did);
|
||||
|
||||
Ok(serde_json::json!({
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/did/v1",
|
||||
"https://w3id.org/security/suites/ed25519-2020/v1",
|
||||
"https://w3id.org/security/suites/x25519-2020/v1"
|
||||
],
|
||||
"id": did,
|
||||
"verificationMethod": [
|
||||
{
|
||||
"id": key_id,
|
||||
"type": "Ed25519VerificationKey2020",
|
||||
"controller": did,
|
||||
"publicKeyMultibase": pubkey_multibase
|
||||
},
|
||||
{
|
||||
"id": x25519_key_id,
|
||||
"type": "X25519KeyAgreementKey2020",
|
||||
"controller": did,
|
||||
"publicKeyMultibase": x25519_key
|
||||
}
|
||||
],
|
||||
"authentication": [key_id],
|
||||
"assertionMethod": [key_id],
|
||||
"capabilityInvocation": [key_id],
|
||||
"capabilityDelegation": [key_id],
|
||||
"keyAgreement": [x25519_key_id]
|
||||
}))
|
||||
}
|
||||
|
||||
/// Extract the raw 32-byte Ed25519 public key from a did:key string.
|
||||
pub fn pubkey_bytes_from_did_key(did: &str) -> Result<[u8; 32]> {
|
||||
let multibase_str = did
|
||||
.strip_prefix("did:key:z")
|
||||
.ok_or_else(|| anyhow::anyhow!("Invalid did:key format"))?;
|
||||
let decoded = bs58::decode(multibase_str)
|
||||
.into_vec()
|
||||
.context("Invalid base58 in did:key")?;
|
||||
if decoded.len() != 34 || decoded[0] != 0xed || decoded[1] != 0x01 {
|
||||
return Err(anyhow::anyhow!("Invalid Ed25519 multicodec prefix"));
|
||||
}
|
||||
let mut pubkey = [0u8; 32];
|
||||
pubkey.copy_from_slice(&decoded[2..34]);
|
||||
Ok(pubkey)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -210,4 +286,60 @@ mod tests {
|
||||
assert!(addr.starts_with("archipelago://abc123.onion#"));
|
||||
assert!(addr.contains(&identity.pubkey_hex()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_did_document_w3c_structure() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let identity = NodeIdentity::load_or_create(&dir.path().join("id")).await.unwrap();
|
||||
|
||||
let doc = identity.did_document();
|
||||
let did = identity.did_key();
|
||||
|
||||
// Verify @context
|
||||
let context = doc["@context"].as_array().unwrap();
|
||||
assert_eq!(context[0], "https://www.w3.org/ns/did/v1");
|
||||
|
||||
// Verify id matches did:key
|
||||
assert_eq!(doc["id"], did);
|
||||
|
||||
// Verify verificationMethod has Ed25519 and X25519 keys
|
||||
let vms = doc["verificationMethod"].as_array().unwrap();
|
||||
assert_eq!(vms.len(), 2);
|
||||
assert_eq!(vms[0]["type"], "Ed25519VerificationKey2020");
|
||||
assert_eq!(vms[1]["type"], "X25519KeyAgreementKey2020");
|
||||
assert_eq!(vms[0]["controller"], did);
|
||||
|
||||
// Verify authentication references key-1
|
||||
let auth = doc["authentication"].as_array().unwrap();
|
||||
assert_eq!(auth[0], format!("{}#key-1", did));
|
||||
|
||||
// Verify assertionMethod
|
||||
assert!(doc["assertionMethod"].as_array().is_some());
|
||||
|
||||
// Verify keyAgreement references x25519 key
|
||||
let ka = doc["keyAgreement"].as_array().unwrap();
|
||||
assert_eq!(ka[0], format!("{}#key-x25519-1", did));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_did_document_from_pubkey_hex() {
|
||||
let hex = "d75a980182b10ab7d54bfed3c964073a0ee172f3daa3f4a18446b7e21e7e2c33";
|
||||
let doc = did_document_from_pubkey_hex(hex).unwrap();
|
||||
assert_eq!(doc["@context"].as_array().unwrap().len(), 3);
|
||||
assert!(doc["id"].as_str().unwrap().starts_with("did:key:z"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pubkey_bytes_from_did_key_roundtrip() {
|
||||
let hex = "d75a980182b10ab7d54bfed3c964073a0ee172f3daa3f4a18446b7e21e7e2c33";
|
||||
let did = did_key_from_pubkey_hex(hex).unwrap();
|
||||
let recovered = pubkey_bytes_from_did_key(&did).unwrap();
|
||||
assert_eq!(hex::encode(recovered), hex);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pubkey_bytes_from_invalid_did() {
|
||||
assert!(pubkey_bytes_from_did_key("did:web:example.com").is_err());
|
||||
assert!(pubkey_bytes_from_did_key("did:key:invalid").is_err());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user