bitrouter_core/jwt/
keys.rs1use base64::Engine;
8use base64::engine::general_purpose::URL_SAFE_NO_PAD;
9use ed25519_dalek::{SigningKey, VerifyingKey};
10use rand::rngs::OsRng;
11use serde::{Deserialize, Serialize};
12
13use crate::jwt::JwtError;
14
15#[derive(Clone)]
20pub struct MasterKeypair {
21 signing_key: SigningKey,
22}
23
24impl MasterKeypair {
25 pub fn generate() -> Self {
27 Self {
28 signing_key: SigningKey::generate(&mut OsRng),
29 }
30 }
31
32 pub fn from_keypair_bytes(bytes: &[u8; 64]) -> Result<Self, JwtError> {
34 let signing_key =
35 SigningKey::from_keypair_bytes(bytes).map_err(|_| JwtError::InvalidKeypair)?;
36 Ok(Self { signing_key })
37 }
38
39 pub fn to_keypair_bytes(&self) -> [u8; 64] {
41 self.signing_key.to_keypair_bytes()
42 }
43
44 pub fn signing_key(&self) -> &SigningKey {
46 &self.signing_key
47 }
48
49 pub fn verifying_key(&self) -> VerifyingKey {
51 self.signing_key.verifying_key()
52 }
53
54 pub fn public_key_b64(&self) -> String {
57 URL_SAFE_NO_PAD.encode(self.verifying_key().as_bytes())
58 }
59
60 pub fn public_key_prefix(&self) -> String {
63 let b64 = self.public_key_b64();
64 b64[..16.min(b64.len())].to_string()
65 }
66
67 pub fn to_json(&self) -> MasterKeyJson {
69 MasterKeyJson {
70 algorithm: "eddsa".to_string(),
71 secret_key: URL_SAFE_NO_PAD.encode(self.to_keypair_bytes()),
72 }
73 }
74
75 pub fn from_json(json: &MasterKeyJson) -> Result<Self, JwtError> {
77 if json.algorithm != "eddsa" {
78 return Err(JwtError::InvalidKeypair);
79 }
80 let bytes = URL_SAFE_NO_PAD
81 .decode(&json.secret_key)
82 .map_err(|_| JwtError::InvalidKeypair)?;
83 let bytes: [u8; 64] = bytes.try_into().map_err(|_| JwtError::InvalidKeypair)?;
84 Self::from_keypair_bytes(&bytes)
85 }
86}
87
88pub fn decode_public_key(b64: &str) -> Result<VerifyingKey, JwtError> {
90 let bytes = URL_SAFE_NO_PAD
91 .decode(b64)
92 .map_err(|_| JwtError::InvalidPublicKey)?;
93 let bytes: [u8; 32] = bytes.try_into().map_err(|_| JwtError::InvalidPublicKey)?;
94 VerifyingKey::from_bytes(&bytes).map_err(|_| JwtError::InvalidPublicKey)
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct MasterKeyJson {
102 pub algorithm: String,
104 pub secret_key: String,
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn roundtrip_keypair() {
114 let kp = MasterKeypair::generate();
115 let bytes = kp.to_keypair_bytes();
116 let kp2 = MasterKeypair::from_keypair_bytes(&bytes).expect("valid keypair");
117 assert_eq!(kp.public_key_b64(), kp2.public_key_b64());
118 }
119
120 #[test]
121 fn roundtrip_json() {
122 let kp = MasterKeypair::generate();
123 let json = kp.to_json();
124 let kp2 = MasterKeypair::from_json(&json).expect("valid json");
125 assert_eq!(kp.public_key_b64(), kp2.public_key_b64());
126 }
127
128 #[test]
129 fn public_key_prefix_length() {
130 let kp = MasterKeypair::generate();
131 assert_eq!(kp.public_key_prefix().len(), 16);
132 }
133
134 #[test]
135 fn decode_public_key_roundtrip() {
136 let kp = MasterKeypair::generate();
137 let b64 = kp.public_key_b64();
138 let vk = decode_public_key(&b64).expect("valid public key");
139 assert_eq!(vk, kp.verifying_key());
140 }
141
142 #[test]
143 fn from_json_rejects_wrong_algorithm() {
144 let kp = MasterKeypair::generate();
145 let mut json = kp.to_json();
146 json.algorithm = "rsa".to_string();
147 assert!(MasterKeypair::from_json(&json).is_err());
148 }
149}