m10_signing/
ed25519.rs

1use crate::{internal_error, Signer, SigningError};
2use core::convert::TryFrom;
3use core::str::FromStr;
4use m10_protos::sdk::signature::Algorithm;
5use ring::{
6    rand,
7    signature::{Ed25519KeyPair, KeyPair},
8};
9use std::fmt;
10use std::fs::File;
11use std::io::{Read, Write};
12
13/// An Ed25519 key-pair
14#[derive(serde::Deserialize)]
15#[serde(try_from = "String", into = "String")]
16pub struct Ed25519 {
17    #[serde(flatten)]
18    key_pair: Ed25519KeyPair,
19}
20
21impl Ed25519 {
22    /// Generates an ED25519 key-pair, and if the path is passed writes it to disk as a PKCS8 document
23    pub fn new_key_pair(path: Option<&str>) -> Result<Self, SigningError> {
24        let rng = rand::SystemRandom::new();
25        let pkcs8_bytes = Ed25519KeyPair::generate_pkcs8(&rng)
26            .map_err(|e| internal_error(e, "Ed25519::new_key_pair: generate_pkcs8"))?;
27        if let Some(p) = path {
28            let mut key_file = File::create(p)?;
29            key_file.write_all(pkcs8_bytes.as_ref())?;
30        }
31        Ok(Self {
32            key_pair: Ed25519KeyPair::from_pkcs8(pkcs8_bytes.as_ref())
33                .map_err(|e| internal_error(e, "Ed25519::new_key_pair: from_pkcs8"))?,
34        })
35    }
36
37    /// Loads an Ed25519 key-pair from a PKCS8 formatted file
38    pub fn load_key_pair(path: &str) -> Result<Self, SigningError> {
39        let mut key_file = File::open(path)?;
40        let mut pkcs8_bytes: Vec<u8> = Vec::new();
41        key_file.read_to_end(&mut pkcs8_bytes)?;
42        Ed25519::from_pkcs8(&pkcs8_bytes)
43    }
44
45    /// Generates a new key-pair, and returns both the key-pair and a PKCS8 document containing the key-pair
46    pub fn new_key_pair_exportable() -> Result<(Vec<u8>, Self), SigningError> {
47        let rng = rand::SystemRandom::new();
48        let pkcs8_bytes = Ed25519KeyPair::generate_pkcs8(&rng)
49            .map_err(|e| internal_error(e, "Ed25519::new_key_pair_exportable: generate_pkcs8"))?;
50        let key_pair = Ed25519KeyPair::from_pkcs8(pkcs8_bytes.as_ref())
51            .map_err(|e| internal_error(e, "Ed25519::new_key_pair_exportable: from_pkcs8"))?;
52        Ok((pkcs8_bytes.as_ref().to_vec(), Self { key_pair }))
53    }
54
55    /// Returns a new [`Ed25519`] key-pair from a PKCS8 document
56    pub fn from_pkcs8(bytes: &[u8]) -> Result<Self, SigningError> {
57        let key_pair = Ed25519KeyPair::from_pkcs8(bytes)
58            .map_err(|e| internal_error(e, "Ed25519::from_pkcs8"))?;
59        Ok(Self { key_pair })
60    }
61}
62
63#[async_trait::async_trait]
64impl Signer for Ed25519 {
65    async fn sign(&self, msg: &[u8]) -> Result<Vec<u8>, super::SigningError> {
66        Ok(self.key_pair.sign(msg).as_ref().to_vec())
67    }
68
69    fn public_key(&self) -> &[u8] {
70        self.key_pair.public_key().as_ref()
71    }
72
73    fn algorithm(&self) -> Algorithm {
74        Algorithm::Ed25519
75    }
76}
77
78impl fmt::Debug for Ed25519 {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        write!(f, "Ed25519({:?})", self.public_key())
81    }
82}
83
84impl FromStr for Ed25519 {
85    type Err = SigningError;
86    fn from_str(key_pair_enc: &str) -> Result<Self, Self::Err> {
87        let pkcs8_bytes = base64::decode(key_pair_enc).unwrap_or_default();
88        Ed25519::from_pkcs8(&pkcs8_bytes)
89    }
90}
91
92impl TryFrom<String> for Ed25519 {
93    type Error = SigningError;
94    fn try_from(key_pair: String) -> Result<Self, Self::Error> {
95        key_pair.parse()
96    }
97}
98
99impl Ed25519 {
100    /// Parses Ed25519 key-pair from base64-encoded 64-byte raw key (32-byte private + 32-byte public)
101    pub fn from_keypair(b64: &str) -> Result<Self, SigningError> {
102        let bytes = base64::decode(b64).map_err(|_| {
103            SigningError::KeyInvalid("Failed to decode base64-encoded key".to_string())
104        })?;
105        if bytes.len() != 64 {
106            return Err(SigningError::KeyInvalid(
107                "Invalid key length, expected 64 bytes".to_string(),
108            ));
109        }
110        let secret = &bytes[..32];
111        let public = &bytes[32..];
112
113        let key_pair = Ed25519KeyPair::from_seed_and_public_key(secret, public)
114            .map_err(|_| SigningError::KeyInvalid("Invalid key".to_string()))?;
115        Ok(Self { key_pair })
116    }
117}