1use elliptic_curve::pkcs8::EncodePublicKey;
2use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Validation};
3use p384::ecdsa::signature::rand_core::OsRng;
4use p384::ecdsa::{SigningKey, VerifyingKey};
5use p384::pkcs8::{EncodePrivateKey, LineEnding};
6use serde::Serialize;
7use serde::de::DeserializeOwned;
8use std::str::FromStr;
9
10#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, Eq, PartialEq)]
11#[serde(tag = "alg")]
12pub enum JWTPublicKey {
13 ES384 {
15 #[serde(rename = "pub")]
16 public: String,
17 },
18}
19
20#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
21#[serde(tag = "alg")]
22pub enum JWTPrivateKey {
23 ES384 { r#priv: String },
24}
25
26impl JWTPrivateKey {
27 pub fn generate_ec384_signing_key() -> anyhow::Result<Self> {
29 let signing_key = SigningKey::random(&mut OsRng);
30 let priv_pem = signing_key
31 .to_pkcs8_der()?
32 .to_pem("PRIVATE KEY", LineEnding::LF)?
33 .to_string();
34
35 Ok(Self::ES384 { r#priv: priv_pem })
36 }
37
38 pub fn to_public_key(&self) -> anyhow::Result<JWTPublicKey> {
40 match self {
41 JWTPrivateKey::ES384 { r#priv } => {
42 let signing_key = SigningKey::from_str(r#priv)?;
43
44 let pub_key = VerifyingKey::from(signing_key);
45 let pub_pem = pub_key.to_public_key_pem(LineEnding::LF)?;
46
47 Ok(JWTPublicKey::ES384 { public: pub_pem })
48 }
49 }
50 }
51
52 pub fn sign_jwt<C: Serialize>(&self, claims: &C) -> anyhow::Result<String> {
54 match self {
55 JWTPrivateKey::ES384 { r#priv } => {
56 let encoding_key = EncodingKey::from_ec_pem(r#priv.as_bytes())?;
57
58 Ok(jsonwebtoken::encode(
59 &jsonwebtoken::Header::new(Algorithm::ES384),
60 &claims,
61 &encoding_key,
62 )?)
63 }
64 }
65 }
66}
67
68impl JWTPublicKey {
69 pub fn validate_jwt<E: DeserializeOwned + Clone>(&self, jwt: &str) -> anyhow::Result<E> {
71 match self {
72 JWTPublicKey::ES384 { public } => {
73 let decoding_key = DecodingKey::from_ec_pem(public.as_bytes())?;
74
75 let validation = Validation::new(Algorithm::ES384);
76 Ok(jsonwebtoken::decode::<E>(jwt, &decoding_key, &validation)?.claims)
77 }
78 }
79 }
80}
81
82#[cfg(test)]
83mod test {
84 use std::time::{SystemTime, UNIX_EPOCH};
85
86 use crate::JWTPrivateKey;
87 use serde::{Deserialize, Serialize};
88
89 fn time() -> u64 {
90 SystemTime::now()
91 .duration_since(UNIX_EPOCH)
92 .unwrap()
93 .as_secs()
94 }
95
96 #[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
97 pub struct Claims {
98 sub: String,
99 exp: u64,
100 }
101
102 impl Default for Claims {
103 fn default() -> Self {
104 Self {
105 sub: "my-sub".to_string(),
106 exp: time() + 100,
107 }
108 }
109 }
110
111 #[test]
112 fn jwt_encode_sign_verify_valid() {
113 let priv_key = JWTPrivateKey::generate_ec384_signing_key().unwrap();
114 let pub_key = priv_key.to_public_key().unwrap();
115
116 let claims = Claims::default();
117 let jwt = priv_key.sign_jwt(&claims).expect("Failed to sign JWT!");
118 let claims_out = pub_key
119 .validate_jwt::<Claims>(&jwt)
120 .expect("Failed to validate JWT!");
121
122 assert_eq!(claims, claims_out)
123 }
124
125 #[test]
126 fn jwt_encode_sign_verify_invalid_key() {
127 let priv_key = JWTPrivateKey::generate_ec384_signing_key().unwrap();
128 let pub_key_2 = JWTPrivateKey::generate_ec384_signing_key()
129 .unwrap()
130 .to_public_key()
131 .unwrap();
132
133 let claims = Claims::default();
134 let jwt = priv_key.sign_jwt(&claims).expect("Failed to sign JWT!");
135 pub_key_2
136 .validate_jwt::<Claims>(&jwt)
137 .expect_err("JWT should not have validated!");
138 }
139
140 #[test]
141 fn jwt_verify_random_string() {
142 let priv_key = JWTPrivateKey::generate_ec384_signing_key().unwrap();
143 let pub_key = priv_key.to_public_key().unwrap();
144
145 pub_key
146 .validate_jwt::<Claims>("random_string")
147 .expect_err("JWT should not have validated!");
148 }
149
150 #[test]
151 fn jwt_expired() {
152 let priv_key = JWTPrivateKey::generate_ec384_signing_key().unwrap();
153 let pub_key = priv_key.to_public_key().unwrap();
154
155 let claims = Claims {
156 exp: time() - 100,
157 ..Default::default()
158 };
159 let jwt = priv_key.sign_jwt(&claims).expect("Failed to sign JWT!");
160 pub_key
161 .validate_jwt::<Claims>(&jwt)
162 .expect_err("JWT should not have validated!");
163 }
164
165 #[test]
166 fn jwt_invalid_signature() {
167 let priv_key = JWTPrivateKey::generate_ec384_signing_key().unwrap();
168 let pub_key = priv_key.to_public_key().unwrap();
169
170 let claims = Claims::default();
171 let jwt = priv_key.sign_jwt(&claims).expect("Failed to sign JWT!");
172 pub_key
173 .validate_jwt::<Claims>(&format!("{jwt}bad"))
174 .expect_err("JWT should not have validated!");
175 }
176}