Skip to main content

agent_pay/
keys.rs

1use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey, SECRET_KEY_LENGTH};
2use rand::rngs::OsRng;
3
4use crate::error::Error;
5
6const ED25519_PUB_MULTICODEC: [u8; 2] = [0xED, 0x01];
7
8#[derive(Debug, Clone)]
9pub struct KeyPair {
10    pub public_key: [u8; 32],
11    pub private_key: [u8; SECRET_KEY_LENGTH],
12}
13
14pub fn generate_key_pair() -> KeyPair {
15    let signing = SigningKey::generate(&mut OsRng);
16    let verifying = signing.verifying_key();
17    KeyPair {
18        public_key: verifying.to_bytes(),
19        private_key: signing.to_bytes(),
20    }
21}
22
23pub fn did_key_from_public_key(public_key: &[u8]) -> Result<String, Error> {
24    if public_key.len() != 32 {
25        return Err(Error::InvalidDidKey(format!(
26            "Ed25519 public key must be 32 bytes, got {}",
27            public_key.len()
28        )));
29    }
30    let mut bytes = Vec::with_capacity(34);
31    bytes.extend_from_slice(&ED25519_PUB_MULTICODEC);
32    bytes.extend_from_slice(public_key);
33    let encoded = bs58::encode(&bytes).into_string();
34    Ok(format!("did:key:z{encoded}"))
35}
36
37pub fn public_key_from_did_key(did: &str) -> Result<[u8; 32], Error> {
38    if !did.starts_with("did:key:z") {
39        return Err(Error::InvalidDidKey(format!("not a did:key: {did}")));
40    }
41    let multibase = &did["did:key:z".len()..];
42    let decoded = bs58::decode(multibase)
43        .into_vec()
44        .map_err(|e| Error::Multibase(e.to_string()))?;
45    if decoded.len() < 34 {
46        return Err(Error::InvalidDidKey(format!(
47            "did:key multibase too short: decoded {} bytes (need 2-byte multicodec + 32-byte Ed25519 key)",
48            decoded.len()
49        )));
50    }
51    if decoded[0] != 0xED || decoded[1] != 0x01 {
52        let got = format!("0x{:02x}{:02x}", decoded[0], decoded[1]);
53        return Err(Error::InvalidDidKey(format!(
54            "unsupported did:key multicodec (expected 0xed01, got {got})"
55        )));
56    }
57    let mut out = [0u8; 32];
58    out.copy_from_slice(&decoded[2..34]);
59    Ok(out)
60}
61
62pub fn verification_method_id(did: &str) -> Result<String, Error> {
63    if let Some(fragment) = did.strip_prefix("did:key:") {
64        return Ok(format!("{did}#{fragment}"));
65    }
66    if !did.contains('#') {
67        return Err(Error::Other(format!("cannot derive fragment from {did}")));
68    }
69    Ok(did.to_string())
70}
71
72pub fn ed25519_sign(private_key: &[u8; SECRET_KEY_LENGTH], message: &[u8]) -> [u8; 64] {
73    let signing = SigningKey::from_bytes(private_key);
74    signing.sign(message).to_bytes()
75}
76
77pub fn ed25519_verify(public_key: &[u8; 32], message: &[u8], signature: &[u8]) -> bool {
78    let Ok(verifying) = VerifyingKey::from_bytes(public_key) else {
79        return false;
80    };
81    let Ok(sig) = <[u8; 64]>::try_from(signature) else {
82        return false;
83    };
84    let sig = Signature::from_bytes(&sig);
85    verifying.verify(message, &sig).is_ok()
86}