Skip to main content

substrate/crypto/
key.rs

1use anyhow::{anyhow, Result};
2
3use super::AlgorithmBytes;
4
5const KEY_SEPARATOR: char = '.';
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum KeyAlgorithm {
9    Ed25519,
10}
11
12pub fn parse_key(value: &str) -> Result<AlgorithmBytes<KeyAlgorithm>> {
13    let (algorithm_str, value_b58) = value
14        .split_once(KEY_SEPARATOR)
15        .ok_or_else(|| anyhow!("Key must use '{{algorithm}}.{{base58}}' format"))?;
16    let algorithm = match algorithm_str {
17        "ed25519" => KeyAlgorithm::Ed25519,
18        other => return Err(anyhow!("Unsupported key algorithm: '{}'", other)),
19    };
20    if value_b58.is_empty() {
21        return Err(anyhow!("Key payload must not be empty"));
22    }
23
24    let bytes = bs58::decode(value_b58)
25        .into_vec()
26        .map_err(|e| anyhow!("Invalid key base58 payload: {}", e))?;
27    if bytes.len() != 32 {
28        return Err(anyhow!(
29            "Invalid key length for {}: expected 32 bytes, got {}",
30            algorithm.prefix(),
31            bytes.len()
32        ));
33    }
34
35    Ok(AlgorithmBytes { algorithm, bytes })
36}
37
38pub fn format_key(algorithm: KeyAlgorithm, bytes: &[u8]) -> String {
39    format!(
40        "{}.{}",
41        algorithm.prefix(),
42        bs58::encode(bytes).into_string()
43    )
44}
45
46impl KeyAlgorithm {
47    fn prefix(self) -> &'static str {
48        match self {
49            Self::Ed25519 => "ed25519",
50        }
51    }
52}
53
54#[cfg(test)]
55#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
56mod tests {
57
58    use super::*;
59
60    #[test]
61    fn test_parse_key_roundtrip() {
62        let input = "ed25519.2p3NPZceQ6njbPg8aMFsEynX3Cmv6uCt1XMGHhPcL4AT";
63        let parsed = parse_key(input).unwrap();
64        assert_eq!(parsed.algorithm, KeyAlgorithm::Ed25519);
65        assert_eq!(format_key(parsed.algorithm, &parsed.bytes), input);
66    }
67
68    #[test]
69    fn test_parse_key_rejects_wrong_length() {
70        assert!(parse_key("ed25519.5Hue").is_err());
71    }
72}