lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

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 }