use libp2p::PeerId;
const P256_MULTICODEC: &[u8] = &[0x80, 0x24];
pub fn peer_id_to_did(peer_id: &PeerId) -> String {
format!("did:peer:{}", peer_id.to_base58())
}
pub fn did_to_peer_id(did: &str) -> Option<PeerId> {
did.strip_prefix("did:peer:")
.and_then(|base58| base58.parse().ok())
}
pub fn did_matches_peer(did: &str, peer_id: &PeerId) -> bool {
did_to_peer_id(did).map(|p| p == *peer_id).unwrap_or(false)
}
pub fn verification_method(peer_id: &PeerId) -> String {
format!("{}#key-1", peer_id_to_did(peer_id))
}
pub fn extract_ed25519_bytes(peer_id: &PeerId) -> Option<[u8; 32]> {
let bytes = peer_id.to_bytes();
for i in 0..bytes.len().saturating_sub(35) {
if bytes[i] == 0x08 && bytes[i + 1] == 0x01 && bytes[i + 2] == 0x12 && bytes[i + 3] == 0x20
{
return bytes[i + 4..i + 36].try_into().ok();
}
}
None
}
pub fn p256_spki_to_did(spki_bytes: &[u8]) -> Result<String, anyhow::Error> {
if spki_bytes.len() < 65 {
return Err(anyhow::anyhow!(
"SPKI too short: expected ≥65 bytes, got {}",
spki_bytes.len()
));
}
let uncompressed = &spki_bytes[spki_bytes.len() - 65..];
if uncompressed[0] != 0x04 {
return Err(anyhow::anyhow!(
"Expected uncompressed point prefix 0x04, got 0x{:02x}",
uncompressed[0]
));
}
let x = &uncompressed[1..33];
let y_lsb = uncompressed[64];
let prefix = if y_lsb & 1 == 0 { 0x02u8 } else { 0x03u8 };
let mut compressed = Vec::with_capacity(33);
compressed.push(prefix);
compressed.extend_from_slice(x);
let mut multikey = Vec::with_capacity(2 + 33);
multikey.extend_from_slice(P256_MULTICODEC);
multikey.extend_from_slice(&compressed);
Ok(format!(
"did:key:z{}",
bs58::encode(&multikey).into_string()
))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn roundtrip_peer_id_did() {
let keypair = libp2p::identity::Keypair::generate_ed25519();
let peer_id = keypair.public().to_peer_id();
let did = peer_id_to_did(&peer_id);
assert!(did.starts_with("did:peer:"));
let recovered = did_to_peer_id(&did).expect("should round-trip");
assert_eq!(recovered, peer_id);
}
#[test]
fn did_matches_peer_positive() {
let keypair = libp2p::identity::Keypair::generate_ed25519();
let peer_id = keypair.public().to_peer_id();
let did = peer_id_to_did(&peer_id);
assert!(did_matches_peer(&did, &peer_id));
}
#[test]
fn extract_ed25519_roundtrip() {
let keypair = libp2p::identity::Keypair::generate_ed25519();
let peer_id = keypair.public().to_peer_id();
let key_bytes = extract_ed25519_bytes(&peer_id).expect("should extract Ed25519 bytes");
assert_eq!(key_bytes.len(), 32);
}
}