auth.rs (11989B)
1 use crate::error::RadrootsSimplexSmpCryptoError; 2 use alloc::vec::Vec; 3 use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; 4 use radroots_simplex_smp_proto::prelude::{ 5 RadrootsSimplexSmpBrokerMessage, RadrootsSimplexSmpCommand, RadrootsSimplexSmpCorrelationId, 6 }; 7 use x25519_dalek::PublicKey as X25519PublicKey; 8 9 const ED25519_SPKI_DER_PREFIX: &[u8] = &[ 10 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00, 11 ]; 12 const X25519_SPKI_DER_PREFIX: &[u8] = &[ 13 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, 0x03, 0x21, 0x00, 14 ]; 15 const X25519_SPKI_DER_PREFIX_WRAPPED: &[u8] = &[ 16 0x30, 0x2c, 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, 0x03, 0x21, 0x00, 17 ]; 18 19 #[derive(Debug, Clone, PartialEq, Eq)] 20 pub struct RadrootsSimplexSmpEd25519Keypair { 21 pub public_key: Vec<u8>, 22 pub private_key: Vec<u8>, 23 } 24 25 impl RadrootsSimplexSmpEd25519Keypair { 26 pub fn generate() -> Result<Self, RadrootsSimplexSmpCryptoError> { 27 let mut secret = [0_u8; 32]; 28 getrandom::getrandom(&mut secret) 29 .map_err(|_| RadrootsSimplexSmpCryptoError::EntropyUnavailable)?; 30 let signing_key = SigningKey::from_bytes(&secret); 31 Ok(Self { 32 public_key: signing_key.verifying_key().to_bytes().to_vec(), 33 private_key: secret.to_vec(), 34 }) 35 } 36 37 pub fn signing_key(&self) -> Result<SigningKey, RadrootsSimplexSmpCryptoError> { 38 let bytes: [u8; 32] = self.private_key.as_slice().try_into().map_err(|_| { 39 RadrootsSimplexSmpCryptoError::InvalidPrivateKeyLength(self.private_key.len()) 40 })?; 41 Ok(SigningKey::from_bytes(&bytes)) 42 } 43 44 pub fn verifying_key(&self) -> Result<VerifyingKey, RadrootsSimplexSmpCryptoError> { 45 verifying_key_from_bytes(&self.public_key) 46 } 47 48 pub fn verify( 49 &self, 50 payload: &[u8], 51 signature: &[u8], 52 ) -> Result<(), RadrootsSimplexSmpCryptoError> { 53 verify_signature(payload, &self.public_key, signature) 54 } 55 } 56 57 #[derive(Debug, Clone, PartialEq, Eq)] 58 pub enum RadrootsSimplexSmpCommandAuthorization { 59 None, 60 Ed25519(RadrootsSimplexSmpEd25519Keypair), 61 } 62 63 #[derive(Debug, Clone, PartialEq, Eq)] 64 pub struct RadrootsSimplexSmpQueueAuthorizationScope { 65 pub session_identifier: Vec<u8>, 66 pub correlation_id: RadrootsSimplexSmpCorrelationId, 67 pub entity_id: Vec<u8>, 68 } 69 70 impl RadrootsSimplexSmpQueueAuthorizationScope { 71 pub fn new( 72 session_identifier: Vec<u8>, 73 correlation_id: RadrootsSimplexSmpCorrelationId, 74 entity_id: Vec<u8>, 75 ) -> Result<Self, RadrootsSimplexSmpCryptoError> { 76 validate_short_field(&session_identifier)?; 77 validate_short_field(&entity_id)?; 78 Ok(Self { 79 session_identifier, 80 correlation_id, 81 entity_id, 82 }) 83 } 84 85 pub fn encode_authorized_frame( 86 &self, 87 frame: &[u8], 88 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 89 let mut buffer = Vec::new(); 90 push_short_bytes(&mut buffer, &self.session_identifier)?; 91 push_short_bytes(&mut buffer, self.correlation_id.as_bytes())?; 92 push_short_bytes(&mut buffer, &self.entity_id)?; 93 buffer.extend_from_slice(frame); 94 Ok(buffer) 95 } 96 97 pub fn authorized_command_body( 98 &self, 99 command: &RadrootsSimplexSmpCommand, 100 transport_version: u16, 101 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 102 let frame = command.encode_for_version(transport_version)?; 103 self.encode_authorized_frame(&frame) 104 } 105 106 pub fn authorized_broker_body( 107 &self, 108 message: &RadrootsSimplexSmpBrokerMessage, 109 transport_version: u16, 110 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 111 let frame = message.encode_for_version(transport_version)?; 112 self.encode_authorized_frame(&frame) 113 } 114 } 115 116 #[derive(Debug, Clone, PartialEq, Eq)] 117 pub struct RadrootsSimplexSmpQueueAuthorizationMaterial { 118 pub authorized_body: Vec<u8>, 119 pub authorization: Vec<u8>, 120 } 121 122 impl RadrootsSimplexSmpQueueAuthorizationMaterial { 123 pub fn for_command( 124 scope: &RadrootsSimplexSmpQueueAuthorizationScope, 125 command: &RadrootsSimplexSmpCommand, 126 transport_version: u16, 127 authorization: &RadrootsSimplexSmpCommandAuthorization, 128 ) -> Result<Self, RadrootsSimplexSmpCryptoError> { 129 let authorized_body = scope.authorized_command_body(command, transport_version)?; 130 Self::new(authorized_body, authorization) 131 } 132 133 pub fn for_broker_message( 134 scope: &RadrootsSimplexSmpQueueAuthorizationScope, 135 message: &RadrootsSimplexSmpBrokerMessage, 136 transport_version: u16, 137 authorization: &RadrootsSimplexSmpCommandAuthorization, 138 ) -> Result<Self, RadrootsSimplexSmpCryptoError> { 139 let authorized_body = scope.authorized_broker_body(message, transport_version)?; 140 Self::new(authorized_body, authorization) 141 } 142 143 fn new( 144 authorized_body: Vec<u8>, 145 authorization: &RadrootsSimplexSmpCommandAuthorization, 146 ) -> Result<Self, RadrootsSimplexSmpCryptoError> { 147 let authorization = match authorization { 148 RadrootsSimplexSmpCommandAuthorization::None => Vec::new(), 149 RadrootsSimplexSmpCommandAuthorization::Ed25519(keypair) => { 150 let signing_key = keypair.signing_key()?; 151 let signature = signing_key.sign(&authorized_body); 152 signature.to_bytes().to_vec() 153 } 154 }; 155 Ok(Self { 156 authorized_body, 157 authorization, 158 }) 159 } 160 } 161 162 pub fn encode_ed25519_public_key_x509( 163 public_key: &[u8], 164 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 165 let _: [u8; 32] = public_key 166 .try_into() 167 .map_err(|_| RadrootsSimplexSmpCryptoError::InvalidPublicKeyLength(public_key.len()))?; 168 let mut encoded = Vec::with_capacity(ED25519_SPKI_DER_PREFIX.len() + public_key.len()); 169 encoded.extend_from_slice(ED25519_SPKI_DER_PREFIX); 170 encoded.extend_from_slice(public_key); 171 Ok(encoded) 172 } 173 174 pub fn decode_ed25519_public_key_x509( 175 encoded: &[u8], 176 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 177 let raw = encoded.strip_prefix(ED25519_SPKI_DER_PREFIX).ok_or( 178 RadrootsSimplexSmpCryptoError::InvalidPublicKeyLength(encoded.len()), 179 )?; 180 let key: [u8; 32] = raw 181 .try_into() 182 .map_err(|_| RadrootsSimplexSmpCryptoError::InvalidPublicKeyLength(encoded.len()))?; 183 Ok(key.to_vec()) 184 } 185 186 pub fn encode_x25519_public_key_x509( 187 public_key: &[u8], 188 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 189 let _: [u8; 32] = public_key 190 .try_into() 191 .map_err(|_| RadrootsSimplexSmpCryptoError::InvalidPublicKeyLength(public_key.len()))?; 192 let mut encoded = Vec::with_capacity(X25519_SPKI_DER_PREFIX.len() + public_key.len()); 193 encoded.extend_from_slice(X25519_SPKI_DER_PREFIX); 194 encoded.extend_from_slice(public_key); 195 Ok(encoded) 196 } 197 198 pub fn decode_x25519_public_key_x509( 199 encoded: &[u8], 200 ) -> Result<Vec<u8>, RadrootsSimplexSmpCryptoError> { 201 if encoded.len() == 32 { 202 let key: [u8; 32] = encoded 203 .try_into() 204 .map_err(|_| RadrootsSimplexSmpCryptoError::InvalidPublicKeyLength(encoded.len()))?; 205 return Ok(X25519PublicKey::from(key).as_bytes().to_vec()); 206 } 207 let raw = encoded 208 .strip_prefix(X25519_SPKI_DER_PREFIX) 209 .or_else(|| encoded.strip_prefix(X25519_SPKI_DER_PREFIX_WRAPPED)) 210 .ok_or(RadrootsSimplexSmpCryptoError::InvalidPublicKeyLength( 211 encoded.len(), 212 ))?; 213 let key: [u8; 32] = raw 214 .try_into() 215 .map_err(|_| RadrootsSimplexSmpCryptoError::InvalidPublicKeyLength(encoded.len()))?; 216 Ok(X25519PublicKey::from(key).as_bytes().to_vec()) 217 } 218 219 pub fn verify_signature( 220 payload: &[u8], 221 public_key: &[u8], 222 signature: &[u8], 223 ) -> Result<(), RadrootsSimplexSmpCryptoError> { 224 let verifying_key = verifying_key_from_bytes(public_key)?; 225 let signature: [u8; 64] = signature 226 .try_into() 227 .map_err(|_| RadrootsSimplexSmpCryptoError::InvalidSignatureLength(signature.len()))?; 228 verifying_key 229 .verify(payload, &Signature::from_bytes(&signature)) 230 .map_err(|_| RadrootsSimplexSmpCryptoError::SignatureVerificationFailed) 231 } 232 233 fn verifying_key_from_bytes( 234 public_key: &[u8], 235 ) -> Result<VerifyingKey, RadrootsSimplexSmpCryptoError> { 236 let bytes: [u8; 32] = public_key 237 .try_into() 238 .map_err(|_| RadrootsSimplexSmpCryptoError::InvalidPublicKeyLength(public_key.len()))?; 239 VerifyingKey::from_bytes(&bytes) 240 .map_err(|_| RadrootsSimplexSmpCryptoError::InvalidPublicKeyLength(public_key.len())) 241 } 242 243 fn validate_short_field(value: &[u8]) -> Result<(), RadrootsSimplexSmpCryptoError> { 244 if value.len() > u8::MAX as usize { 245 return Err(RadrootsSimplexSmpCryptoError::InvalidShortFieldLength( 246 value.len(), 247 )); 248 } 249 Ok(()) 250 } 251 252 fn push_short_bytes( 253 buffer: &mut Vec<u8>, 254 value: &[u8], 255 ) -> Result<(), RadrootsSimplexSmpCryptoError> { 256 validate_short_field(value)?; 257 buffer.push(value.len() as u8); 258 buffer.extend_from_slice(value); 259 Ok(()) 260 } 261 262 #[cfg(test)] 263 mod tests { 264 use super::*; 265 use radroots_simplex_smp_proto::prelude::{ 266 RADROOTS_SIMPLEX_SMP_CURRENT_TRANSPORT_VERSION, RadrootsSimplexSmpCommand, 267 }; 268 269 #[test] 270 fn builds_ed25519_authorization_for_command_scope() { 271 let scope = RadrootsSimplexSmpQueueAuthorizationScope::new( 272 b"tls-session".to_vec(), 273 RadrootsSimplexSmpCorrelationId::new([5_u8; 24]), 274 b"queue-id".to_vec(), 275 ) 276 .unwrap(); 277 let keypair = RadrootsSimplexSmpEd25519Keypair::generate().unwrap(); 278 279 let material = RadrootsSimplexSmpQueueAuthorizationMaterial::for_command( 280 &scope, 281 &RadrootsSimplexSmpCommand::Ping, 282 RADROOTS_SIMPLEX_SMP_CURRENT_TRANSPORT_VERSION, 283 &RadrootsSimplexSmpCommandAuthorization::Ed25519(keypair.clone()), 284 ) 285 .unwrap(); 286 287 assert_eq!(material.authorized_body[0], b"tls-session".len() as u8); 288 assert_eq!(material.authorization.len(), 64); 289 keypair 290 .verify(&material.authorized_body, &material.authorization) 291 .unwrap(); 292 } 293 294 #[test] 295 fn leaves_unsigned_authorization_empty() { 296 let scope = RadrootsSimplexSmpQueueAuthorizationScope::new( 297 b"tls-session".to_vec(), 298 RadrootsSimplexSmpCorrelationId::new([3_u8; 24]), 299 Vec::new(), 300 ) 301 .unwrap(); 302 303 let material = RadrootsSimplexSmpQueueAuthorizationMaterial::for_command( 304 &scope, 305 &RadrootsSimplexSmpCommand::Ping, 306 RADROOTS_SIMPLEX_SMP_CURRENT_TRANSPORT_VERSION, 307 &RadrootsSimplexSmpCommandAuthorization::None, 308 ) 309 .unwrap(); 310 311 assert!(material.authorization.is_empty()); 312 } 313 314 #[test] 315 fn ed25519_public_key_x509_roundtrips_shape() { 316 let keypair = RadrootsSimplexSmpEd25519Keypair::generate().unwrap(); 317 let encoded = encode_ed25519_public_key_x509(&keypair.public_key).unwrap(); 318 319 assert_eq!( 320 &encoded[..ED25519_SPKI_DER_PREFIX.len()], 321 ED25519_SPKI_DER_PREFIX 322 ); 323 assert_eq!( 324 &encoded[ED25519_SPKI_DER_PREFIX.len()..], 325 keypair.public_key 326 ); 327 } 328 329 #[test] 330 fn x25519_public_key_x509_roundtrips_and_accepts_raw() { 331 let keypair = crate::message::RadrootsSimplexSmpX25519Keypair::generate().unwrap(); 332 let encoded = encode_x25519_public_key_x509(&keypair.public_key).unwrap(); 333 334 assert_eq!( 335 decode_x25519_public_key_x509(&encoded).unwrap(), 336 keypair.public_key 337 ); 338 assert_eq!( 339 decode_x25519_public_key_x509(&keypair.public_key).unwrap(), 340 keypair.public_key 341 ); 342 } 343 }