use super::Refusal;
use crate::node_key::carried_key_well_formed;
use crate::profile::Profile;
const CODEC_MLDSA87: u64 = 0x1212;
const CODEC_MLDSA87_RSA4096: u64 = 0x30_0087;
const PREFIX: &str = "did:key:z";
const MAX_ENCODED_CHARS: usize = 4_400;
const ALPHABET: &[u8; 58] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
pub fn did_key(carried: &[u8], profile: Profile) -> String {
let mut bytes = varint(codec(profile));
bytes.extend_from_slice(carried);
format!("{PREFIX}{}", base58btc_encode(&bytes))
}
pub fn carried_key(did: &str, profile: Profile) -> Result<Vec<u8>, Refusal> {
let encoded = did.strip_prefix(PREFIX).ok_or(Refusal::Malformed)?;
if encoded.len() > MAX_ENCODED_CHARS {
return Err(Refusal::Malformed);
}
let prefix = varint(codec(profile));
let decoded = base58btc_decode(encoded).ok_or(Refusal::Malformed)?;
match decoded.strip_prefix(prefix.as_slice()) {
Some(carried) if !carried.is_empty() && carried_key_well_formed(carried, profile) => {
Ok(carried.to_vec())
}
_ => Err(Refusal::Malformed),
}
}
fn codec(profile: Profile) -> u64 {
match profile {
Profile::PqPure => CODEC_MLDSA87,
Profile::PqHybrid => CODEC_MLDSA87_RSA4096,
}
}
fn varint(mut n: u64) -> Vec<u8> {
let mut out = Vec::new();
while n >= 0x80 {
out.push((n & 0x7f) as u8 | 0x80);
n >>= 7;
}
out.push(n as u8);
out
}
fn base58btc_encode(bytes: &[u8]) -> String {
let zeros = bytes.iter().take_while(|&&b| b == 0).count();
let mut digits: Vec<u8> = Vec::new();
for &byte in &bytes[zeros..] {
let mut carry = byte as u32;
for digit in digits.iter_mut() {
carry += (*digit as u32) << 8;
*digit = (carry % 58) as u8;
carry /= 58;
}
while carry > 0 {
digits.push((carry % 58) as u8);
carry /= 58;
}
}
let mut out = String::with_capacity(zeros + digits.len());
out.extend(std::iter::repeat_n('1', zeros));
out.extend(digits.iter().rev().map(|&d| ALPHABET[d as usize] as char));
out
}
fn base58btc_decode(text: &str) -> Option<Vec<u8>> {
let ones = text.bytes().take_while(|&b| b == b'1').count();
let mut value: Vec<u8> = Vec::new();
for c in text[ones..].bytes() {
let mut carry = ALPHABET.iter().position(|&a| a == c)? as u32;
for byte in value.iter_mut() {
carry += (*byte as u32) * 58;
*byte = (carry & 0xff) as u8;
carry >>= 8;
}
while carry > 0 {
value.push((carry & 0xff) as u8);
carry >>= 8;
}
}
let mut out = vec![0u8; ones];
out.extend(value.iter().rev());
Some(out)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn base58btc_round_trips_and_keeps_leading_zeros() {
for bytes in [
vec![],
vec![0],
vec![0, 0, 1],
vec![0xff; 40],
(0u8..=255).collect::<Vec<_>>(),
] {
let text = base58btc_encode(&bytes);
assert_eq!(base58btc_decode(&text), Some(bytes.clone()), "{text}");
}
assert_eq!(base58btc_encode(&[0, 0, 0x3a]), "1121");
assert_eq!(base58btc_decode("0OIl"), None);
}
#[test]
fn the_key_types_are_leb128_varints() {
assert_eq!(varint(CODEC_MLDSA87), vec![0x92, 0x24]);
assert_eq!(varint(CODEC_MLDSA87_RSA4096), vec![0x87, 0x81, 0xc0, 0x01]);
}
#[test]
fn a_did_key_of_another_shape_carries_no_key() {
assert_eq!(
carried_key("did:key:x", Profile::PqPure),
Err(Refusal::Malformed)
);
assert_eq!(
carried_key("did:key:z", Profile::PqPure),
Err(Refusal::Malformed)
);
let short = did_key(&[1, 2, 3], Profile::PqPure);
assert_eq!(
carried_key(&short, Profile::PqPure),
Err(Refusal::Malformed)
);
}
}