Oregami
Repositories/oxedyne/fe2o3

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

1use crate::{
2 pqc::saber::{
3 self,
4 SaberAlgorithm,
5 },
6 keys::Keys,
7};
8
9use oxedyne_fe2o3_core::{
10 prelude::*,
11 rand::RanDef,
12};
13use oxedyne_fe2o3_iop_crypto::{
14 kem::KeyExchanger,
15};
16use oxedyne_fe2o3_namex::{
17 id::{
18 LocalId,
19 InNamex,
20 NamexId,
21 },
22};
23
24use std::{
25 convert::TryFrom,
26 fmt::{
27 self,
28 Debug,
29 },
30 str,
31};
32
33use secrecy::{
34 ExposeSecret,
35};
36
37
38#[derive(Clone)]
39pub enum KeyExchangeScheme {
40 FireSaber(Keys<
41 {Self::FIRESABER_PK_LEN},
42 {Self::FIRESABER_SK_LEN},
43 >),
44}
45
46impl 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
54impl 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
96impl 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
156impl 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
169impl 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
182impl 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)]
195mod 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}