oxedyne/fe2o3/fe2o3_crypto/src/kem.rs
6.2 KiB, 24 runs
created by r1870400018:230, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | use crate::{ |
| 2 | pqc::saber::{ |
| 3 | self, |
| 4 | SaberAlgorithm, |
| 5 | }, |
| 6 | keys::Keys, |
| 7 | }; |
| 8 | |
| 9 | use oxedyne_fe2o3_core::{ |
| 10 | prelude::*, |
| 11 | rand::RanDef, |
| 12 | }; |
| 13 | use oxedyne_fe2o3_iop_crypto::{ |
| 14 | kem::KeyExchanger, |
| 15 | }; |
| 16 | use oxedyne_fe2o3_namex::{ |
| 17 | id::{ |
| 18 | LocalId, |
| 19 | InNamex, |
| 20 | NamexId, |
| 21 | }, |
| 22 | }; |
| 23 | |
| 24 | use std::{ |
| 25 | convert::TryFrom, |
| 26 | fmt::{ |
| 27 | self, |
| 28 | Debug, |
| 29 | }, |
| 30 | str, |
| 31 | }; |
| 32 | |
| 33 | use secrecy::{ |
| 34 | ExposeSecret, |
| 35 | }; |
| 36 | |
| 37 | |
| 38 | #[derive(Clone)] |
| 39 | pub enum KeyExchangeScheme { |
| 40 | FireSaber(Keys< |
| 41 | {Self::FIRESABER_PK_LEN}, |
| 42 | {Self::FIRESABER_SK_LEN}, |
| 43 | >), |
| 44 | } |
| 45 | |
| 46 | impl Debug for KeyExchangeScheme { |
| 47 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 48 | match self { |
| 49 | Self::FireSaber(..) => write!(f, "FireSaber-KEM"), |
| 50 | } |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | impl InNamex for KeyExchangeScheme { |
| 55 | |
| 56 | fn name_id(&self) -> Outcome<NamexId> { |
| 57 | Ok(match self { |
| 58 | Self::FireSaber(..) => |
| 59 | res!(NamexId::try_from("nb68Os+ihmMixsBrsV5K+OzgCJrtxQ1L3e1FJ7KOvPk=")), |
| 60 | }) |
| 61 | } |
| 62 | |
| 63 | /// Version-dependent identifier for the encryption scheme. The type of the identifier can |
| 64 | /// change with verisons. This offers a much more compact alternative to the 256 bit Namex |
| 65 | /// id. |
| 66 | fn local_id(&self) -> LocalId { |
| 67 | match self { |
| 68 | Self::FireSaber(..) => LocalId(1), |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | fn assoc_names_base64( |
| 73 | gname: &'static str, |
| 74 | ) |
| 75 | -> Outcome<Option<Vec<( |
| 76 | &'static str, |
| 77 | &'static str, |
| 78 | )>>> |
| 79 | { |
| 80 | let ids = match gname { |
| 81 | "schemes" => [ |
| 82 | ("FireSaber", "nb68Os+ihmMixsBrsV5K+OzgCJrtxQ1L3e1FJ7KOvPk="), |
| 83 | ], |
| 84 | _ => return Err(err!( |
| 85 | "The Namex group name '{}' is not recognised for KeyExchangeScheme.", gname; |
| 86 | Invalid, Input)), |
| 87 | }; |
| 88 | Ok(if ids.len() == 0 { |
| 89 | None |
| 90 | } else { |
| 91 | Some(ids.to_vec()) |
| 92 | }) |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | impl KeyExchanger for KeyExchangeScheme { |
| 97 | |
| 98 | fn encap< |
| 99 | const PK_LEN: usize, |
| 100 | const SESSION_KEY_LEN: usize, |
| 101 | const CIPHERTEXT_LEN: usize, |
| 102 | >( |
| 103 | &self, |
| 104 | _pk: [u8; PK_LEN], // TODO does the trait need this? |
| 105 | ) |
| 106 | -> Outcome<( |
| 107 | [u8; SESSION_KEY_LEN], |
| 108 | [u8; CIPHERTEXT_LEN], |
| 109 | )> |
| 110 | { |
| 111 | match self { |
| 112 | Self::FireSaber(keys) => match keys { |
| 113 | Keys { pk: Some(pk_byts), .. } => { |
| 114 | let scheme = saber::FireSaber; |
| 115 | let pk = res!(saber::PublicKey::from_bytes(&pk_byts[..])); |
| 116 | let (sess_key1, ct1) = scheme.kem_encap(&pk); |
| 117 | let ct2 = ct1.to_bytes::<{CIPHERTEXT_LEN}>(); |
| 118 | let ct3 = res!(<[u8; CIPHERTEXT_LEN]>::try_from(&ct2[..])); |
| 119 | let sess_key2 = res!(<[u8; SESSION_KEY_LEN]>::try_from(&sess_key1[..])); |
| 120 | Ok((sess_key2, ct3)) |
| 121 | }, |
| 122 | _ => Err(err!( |
| 123 | "Require public key to encapsulate."; |
| 124 | Missing, Configuration)), |
| 125 | }, |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | fn decap< |
| 130 | const SESSION_KEY_LEN: usize, |
| 131 | const CIPHERTEXT_LEN: usize, |
| 132 | >( |
| 133 | &self, |
| 134 | ciphertext: [u8; CIPHERTEXT_LEN], |
| 135 | ) |
| 136 | -> Outcome<[u8; SESSION_KEY_LEN]> |
| 137 | { |
| 138 | match self { |
| 139 | Self::FireSaber(keys) => match keys { |
| 140 | Keys { sks: Some(sks), .. } => { |
| 141 | let sk_byts = sks.expose_secret(); |
| 142 | let scheme = saber::FireSaber; |
| 143 | let sk_cca = res!(saber::SecretKeyCCA::from_bytes(&sk_byts[..])); |
| 144 | let sess_key1 = res!(scheme.kem_decap(&ciphertext, &sk_cca)); |
| 145 | let sess_key2 = res!(<[u8; SESSION_KEY_LEN]>::try_from(&sess_key1[..])); |
| 146 | Ok(sess_key2) |
| 147 | }, |
| 148 | _ => Err(err!( |
| 149 | "Require secret key to de-encapsulate."; |
| 150 | Missing, Configuration)), |
| 151 | }, |
| 152 | } |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | impl str::FromStr for KeyExchangeScheme { |
| 157 | type Err = Error<ErrTag>; |
| 158 | |
| 159 | fn from_str(name: &str) -> std::result::Result<Self, Self::Err> { |
| 160 | match name { |
| 161 | "FireSaber" => Ok(Self::new_firesaber()), |
| 162 | _ => Err(err!( |
| 163 | "The key exchange scheme '{}' is not recognised.", name; |
| 164 | Invalid, Input)), |
| 165 | } |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | impl TryFrom<LocalId> for KeyExchangeScheme { |
| 170 | type Error = Error<ErrTag>; |
| 171 | |
| 172 | fn try_from(n: LocalId) -> std::result::Result<Self, Self::Error> { |
| 173 | match n { |
| 174 | LocalId(1) => Ok(Self::new_firesaber()), |
| 175 | _ => Err(err!( |
| 176 | "The key exchange scheme with local id {} is not recognised.", n; |
| 177 | Invalid, Input)), |
| 178 | } |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | impl KeyExchangeScheme { |
| 183 | |
| 184 | pub const FIRESABER_PK_LEN: usize = saber::FireSaber::PK_LEN; |
| 185 | pub const FIRESABER_SK_LEN: usize = saber::FireSaber::SK_LEN; |
| 186 | pub const FIRESABER_SESSION_KEY_LEN: usize = saber::FireSaber::SK_LEN; |
| 187 | pub const FIRESABER_CIPHERTEXT_LEN: usize = saber::FireSaber::CIPHERTEXT_BYTES; |
| 188 | |
| 189 | pub fn new_firesaber() -> Self { |
| 190 | Self::FireSaber(Keys::randef()) |
| 191 | } |
| 192 | } |
| 193 | |
| 194 | #[cfg(test)] |
| 195 | mod tests { |
| 196 | use super::*; |
| 197 | |
| 198 | #[test] |
| 199 | fn test_enc_scheme_firesaber_00() -> Outcome<()> { |
| 200 | let scheme = saber::FireSaber; |
| 201 | // Bob generates keys at the server. |
| 202 | let (bob_pk, bob_sk) = scheme.kem_keygen(); |
| 203 | // Bob sends the public key to Alice. |
| 204 | // Alice generates her symmetric key using Bob's public key. |
| 205 | let (alice_session_key, ciphertext) = scheme.kem_encap(&bob_pk); |
| 206 | // Alice sends the scrambled symmetric key to Bob. |
| 207 | // Bob recovers the symmetric key from Alice's scrambled transmissions. |
| 208 | let bob_session_key = res!(scheme.kem_decap( |
| 209 | &ciphertext.to_bytes::<{saber::FireSaber::CIPHERTEXT_BYTES}>(), |
| 210 | &bob_sk, |
| 211 | )); |
| 212 | // Alice and Bob now encrypt/decrypt their session using the symmetric key. |
| 213 | assert_eq!(alice_session_key, bob_session_key); |
| 214 | msg!("Lengths:"); |
| 215 | msg!(" bob_pk: {}", bob_pk.byte_len()); |
| 216 | msg!(" bob_sk: {}", bob_sk.byte_len()); |
| 217 | msg!(" session_key: {}", alice_session_key.len()); |
| 218 | msg!(" ciphertext: {}", ciphertext.byte_len()); |
| 219 | Ok(()) |
| 220 | } |
| 221 | } |