attenuable_jwt/ed25519/
mod.rs

1//! Module containing [crate::protocol::PrivateKey] and [crate::protocol::PublicKey] implementations for the
2//! ed25519 algorithm.
3
4use std::convert::TryInto;
5
6use base64::URL_SAFE_NO_PAD;
7use ed25519_dalek::{
8    Keypair as Ed25519DalekKeyPair, PublicKey as Ed25519DalekPublicKey,
9    SecretKey as Ed25519DalekSecretKey, Signer, Verifier,
10};
11use serde::{Deserialize, Serialize};
12
13use crate::protocol::{KeyUse, PrivateKey, PublicKey};
14
15mod ed25519_sign;
16
17pub use ed25519_sign::EddsaKeyGen;
18
19/// Algorithm identifier for the EdDSA (ED25519) algorithm.
20pub const EDDSA_ALGORITHM: &str = "EdDSA";
21
22/// Private key for the ed25519 algorithm.
23#[derive(Serialize, Deserialize)]
24#[serde(into = "JWK", try_from = "JWK")]
25pub struct Ed25519PrivateKey {
26    key_id: String,
27    private_key: Ed25519DalekKeyPair,
28}
29
30impl Clone for Ed25519PrivateKey {
31    fn clone(&self) -> Self {
32        Self {
33            key_id: self.key_id.clone(),
34            private_key: Ed25519DalekKeyPair::from_bytes(&self.private_key.to_bytes()).unwrap(),
35        }
36    }
37}
38
39impl PartialEq for Ed25519PrivateKey {
40    fn eq(&self, other: &Self) -> bool {
41        self.key_id == other.key_id
42            && self.private_key.secret.as_bytes() == other.private_key.secret.as_bytes()
43            // public keys are uniquely derived from secret keys so it's ok to just compare the secret keys
44    }
45}
46
47impl Eq for Ed25519PrivateKey {}
48
49impl std::fmt::Debug for Ed25519PrivateKey {
50    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51        f.debug_struct("Ed25519PrivateKey")
52            .field("key_id", &self.key_id)
53            .field("private_key", &"***")
54            .finish()
55    }
56}
57
58impl Ed25519PrivateKey {
59    /// Create an ed25519 private key.
60    fn new(key_id: String, private_key: Ed25519DalekKeyPair) -> Self {
61        Self {
62            key_id,
63            private_key,
64        }
65    }
66}
67
68impl PrivateKey for Ed25519PrivateKey {
69    fn key_id(&self) -> &str {
70        &self.key_id
71    }
72
73    fn algorithm(&self) -> &str {
74        EDDSA_ALGORITHM
75    }
76
77    fn sign(&self, message: &[u8]) -> crate::sign::Result<Vec<u8>> {
78        Ok(self
79            .private_key
80            .try_sign(message)
81            .map_err(|_| crate::sign::Error::CryptoError)?
82            .to_bytes()
83            .to_vec())
84    }
85}
86
87/// Public key for the ed25519 algorithm.
88#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
89#[serde(into = "JWK", try_from = "JWK")]
90pub struct Ed25519PublicKey {
91    key_id: String,
92    public_key: Ed25519DalekPublicKey,
93}
94
95impl PublicKey for Ed25519PublicKey {
96    fn key_id(&self) -> &str {
97        &self.key_id
98    }
99
100    fn algorithm(&self) -> &str {
101        EDDSA_ALGORITHM
102    }
103
104    fn key_use(&self) -> KeyUse {
105        KeyUse::Signing
106    }
107
108    fn verify(&self, message: &[u8], signature: &[u8]) -> bool {
109        let res = signature
110            .try_into()
111            .and_then(|signature| self.public_key.verify(message, &signature));
112        res.is_ok()
113    }
114}
115
116impl Ed25519PublicKey {
117    /// Create a public key for the ed25519 algorithm.
118    fn new(public_key: Ed25519DalekPublicKey) -> Self {
119        Self {
120            key_id: "aky".to_owned(),
121            public_key,
122        }
123    }
124}
125
126/// JWK for [Ed25519PublicKey]s and [Ed25519PrivateKey]s.
127#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
128pub struct JWK {
129    /// Key ID.
130    pub kid: String,
131    /// Key use.
132    #[serde(rename = "use")]
133    pub key_use: KeyUse,
134    /// Key operations.
135    pub key_ops: Vec<KeyOp>,
136    /// Algorithm.
137    pub alg: String,
138    /// Key type.
139    pub kty: String,
140    /// Curve.
141    pub crv: String,
142    /// Public key component.
143    pub x: String,
144    /// Private key component.
145    #[serde(skip_serializing_if = "Option::is_none")]
146    pub d: Option<String>,
147}
148
149impl std::fmt::Debug for JWK {
150    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151        f.debug_struct("JWK")
152            .field("kid", &self.kid)
153            .field("key_use", &self.key_use)
154            .field("key_ops", &self.key_ops)
155            .field("alg", &self.alg)
156            .field("kty", &self.kty)
157            .field("crv", &self.crv)
158            .field("x", &self.x)
159            .field("d", &self.d.as_ref().map(|_| "***"))
160            .finish()
161    }
162}
163
164/// Key operation.
165#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug)]
166pub enum KeyOp {
167    /// Sign.
168    #[serde(rename = "sign")]
169    Sign,
170    /// Verify.
171    #[serde(rename = "verify")]
172    Verify,
173}
174
175impl From<Ed25519PublicKey> for JWK {
176    fn from(k: Ed25519PublicKey) -> Self {
177        Self::from(&k)
178    }
179}
180
181impl From<&Ed25519PublicKey> for JWK {
182    fn from(k: &Ed25519PublicKey) -> Self {
183        JWK {
184            kid: k.key_id.clone(),
185            key_use: KeyUse::Signing,
186            key_ops: vec![KeyOp::Verify],
187            kty: "OKP".to_owned(),
188            crv: "Ed25519".to_owned(),
189            alg: "EdDSA".to_owned(),
190            x: base64::encode_config(k.public_key.as_bytes(), URL_SAFE_NO_PAD),
191            d: None,
192        }
193    }
194}
195
196impl From<Ed25519PrivateKey> for JWK {
197    fn from(k: Ed25519PrivateKey) -> Self {
198        Self::from(&k)
199    }
200}
201
202impl From<&Ed25519PrivateKey> for JWK {
203    fn from(k: &Ed25519PrivateKey) -> Self {
204        JWK {
205            kid: k.key_id.to_owned(),
206            key_use: KeyUse::Signing,
207            key_ops: vec![KeyOp::Sign],
208            kty: "OKP".to_owned(),
209            crv: "Ed25519".to_owned(),
210            alg: "EdDSA".to_owned(),
211            x: base64::encode_config(k.private_key.public.as_bytes(), URL_SAFE_NO_PAD),
212            d: Some(base64::encode_config(
213                k.private_key.secret.as_bytes(),
214                URL_SAFE_NO_PAD,
215            )),
216        }
217    }
218}
219
220impl TryFrom<JWK> for Ed25519PublicKey {
221    type Error = crate::verify::Error;
222
223    fn try_from(value: JWK) -> Result<Self, Self::Error> {
224        Ed25519PublicKey::try_from(&value)
225    }
226}
227
228impl TryFrom<&JWK> for Ed25519PublicKey {
229    type Error = crate::verify::Error;
230
231    fn try_from(jwk: &JWK) -> std::result::Result<Self, Self::Error> {
232        let x_bytes = base64::decode_config(&jwk.x, URL_SAFE_NO_PAD)
233            .map_err(|_| crate::verify::Error::MalformedAttenuationKeyJWK)?;
234        let public_key = Ed25519DalekPublicKey::from_bytes(&x_bytes)
235            .map_err(|_| crate::verify::Error::MalformedAttenuationKeyJWK)?;
236        Ok(Ed25519PublicKey {
237            key_id: jwk.kid.clone(),
238            public_key,
239        })
240    }
241}
242
243impl TryFrom<JWK> for Ed25519PrivateKey {
244    type Error = crate::verify::Error;
245
246    fn try_from(jwk: JWK) -> std::result::Result<Self, Self::Error> {
247        Ed25519PrivateKey::try_from(&jwk)
248    }
249}
250
251impl TryFrom<&JWK> for Ed25519PrivateKey {
252    type Error = crate::verify::Error;
253
254    fn try_from(jwk: &JWK) -> std::result::Result<Self, Self::Error> {
255        if let Some(d) = &jwk.d {
256            let x_bytes = base64::decode_config(&jwk.x, URL_SAFE_NO_PAD)
257                .map_err(|_| crate::verify::Error::MalformedAttenuationKeyJWK)?;
258            let public = Ed25519DalekPublicKey::from_bytes(&x_bytes)
259                .map_err(|_| crate::verify::Error::MalformedAttenuationKeyJWK)?;
260            let d_bytes = base64::decode_config(d, URL_SAFE_NO_PAD)
261                .map_err(|_| crate::verify::Error::MalformedAttenuationKeyJWK)?;
262            let secret = Ed25519DalekSecretKey::from_bytes(&d_bytes)
263                .map_err(|_| crate::verify::Error::MalformedAttenuationKeyJWK)?;
264            let keypair = Ed25519DalekKeyPair { public, secret };
265            Ok(Ed25519PrivateKey {
266                key_id: jwk.kid.clone(),
267                private_key: keypair,
268            })
269        } else {
270            Err(crate::verify::Error::InvalidKey)
271        }
272    }
273}
274
275#[cfg(test)]
276mod test {
277    use crate::{ed25519::Ed25519PrivateKey, protocol::AttenuationKeyGenerator};
278
279    use super::{Ed25519PublicKey, EddsaKeyGen, JWK};
280
281    #[test]
282    fn test_private_key_jwk() -> Result<(), Box<dyn std::error::Error>> {
283        let kg = EddsaKeyGen::new_with_std_rng();
284        let (_, priv_key) = kg.generate_attenuation_key()?;
285        let jwk = JWK::from(&priv_key);
286        let round_trip = Ed25519PrivateKey::try_from(&jwk)?;
287        assert_eq!(
288            priv_key.private_key.to_bytes(),
289            round_trip.private_key.to_bytes()
290        );
291        assert_eq!(&priv_key.key_id, &round_trip.key_id);
292        Ok(())
293    }
294
295    #[test]
296    fn test_public_key_jwk() -> Result<(), Box<dyn std::error::Error>> {
297        let kg = EddsaKeyGen::new_with_std_rng();
298        let (pub_key, _) = kg.generate_attenuation_key()?;
299        let jwk = JWK::from(&pub_key);
300        let round_trip = Ed25519PublicKey::try_from(&jwk)?;
301        assert_eq!(
302            pub_key.public_key.as_bytes(),
303            round_trip.public_key.as_bytes()
304        );
305        assert_eq!(&pub_key.key_id, &round_trip.key_id);
306        Ok(())
307    }
308}