|
2 | 2 | // 2.0, and the BSD License. See the LICENSE file in the root of this repository |
3 | 3 | // for complete details. |
4 | 4 |
|
| 5 | +use pyo3::types::{PyAnyMethods, PyBytesMethods}; |
| 6 | + |
5 | 7 | use crate::backend::aead::AesGcm; |
6 | 8 | use crate::backend::kdf::{hkdf_extract, HkdfExpand}; |
7 | 9 | use crate::backend::x25519; |
8 | 10 | use crate::buf::CffiBuf; |
9 | 11 | use crate::error::{CryptographyError, CryptographyResult}; |
10 | | -use crate::exceptions; |
11 | | -use crate::types; |
12 | | -use pyo3::types::{PyAnyMethods, PyBytesMethods}; |
| 12 | +use crate::{exceptions, types}; |
13 | 13 |
|
14 | 14 | const HPKE_VERSION: &[u8] = b"HPKE-v1"; |
15 | 15 | const HPKE_MODE_BASE: u8 = 0x00; |
@@ -235,6 +235,66 @@ impl Suite { |
235 | 235 | self.hkdf_expand(py, prk, &labeled_info, length) |
236 | 236 | } |
237 | 237 |
|
| 238 | + fn encrypt_inner<'p>( |
| 239 | + &self, |
| 240 | + py: pyo3::Python<'p>, |
| 241 | + plaintext: CffiBuf<'_>, |
| 242 | + public_key: &pyo3::Bound<'_, pyo3::PyAny>, |
| 243 | + info: Option<CffiBuf<'_>>, |
| 244 | + aad: Option<CffiBuf<'_>>, |
| 245 | + ) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> { |
| 246 | + let info_bytes: &[u8] = info.as_ref().map(|b| b.as_bytes()).unwrap_or(b""); |
| 247 | + |
| 248 | + let (shared_secret, enc) = self.encap(py, public_key)?; |
| 249 | + let (key, base_nonce) = self.key_schedule(py, shared_secret.as_bytes(), info_bytes)?; |
| 250 | + |
| 251 | + let aesgcm = AesGcm::new(py, pyo3::types::PyBytes::new(py, &key).unbind().into_any())?; |
| 252 | + let ct = aesgcm.encrypt(py, CffiBuf::from_bytes(py, &base_nonce), plaintext, aad)?; |
| 253 | + |
| 254 | + let enc_bytes = enc.as_bytes(); |
| 255 | + let ct_bytes = ct.as_bytes(); |
| 256 | + Ok(pyo3::types::PyBytes::new_with( |
| 257 | + py, |
| 258 | + enc_bytes.len() + ct_bytes.len(), |
| 259 | + |buf| { |
| 260 | + buf[..enc_bytes.len()].copy_from_slice(enc_bytes); |
| 261 | + buf[enc_bytes.len()..].copy_from_slice(ct_bytes); |
| 262 | + Ok(()) |
| 263 | + }, |
| 264 | + )?) |
| 265 | + } |
| 266 | + |
| 267 | + fn decrypt_inner<'p>( |
| 268 | + &self, |
| 269 | + py: pyo3::Python<'p>, |
| 270 | + ciphertext: CffiBuf<'_>, |
| 271 | + private_key: &pyo3::Bound<'_, pyo3::PyAny>, |
| 272 | + info: Option<CffiBuf<'_>>, |
| 273 | + aad: Option<CffiBuf<'_>>, |
| 274 | + ) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> { |
| 275 | + let ct_bytes = ciphertext.as_bytes(); |
| 276 | + if ct_bytes.len() < kem_params::X25519_NENC + aead_params::AES_128_GCM_NT { |
| 277 | + return Err(CryptographyError::from(exceptions::InvalidTag::new_err(()))); |
| 278 | + } |
| 279 | + |
| 280 | + let info_bytes: &[u8] = info.as_ref().map(|b| b.as_bytes()).unwrap_or(b""); |
| 281 | + |
| 282 | + let (enc, ct) = ct_bytes.split_at(kem_params::X25519_NENC); |
| 283 | + |
| 284 | + let shared_secret = self |
| 285 | + .decap(py, enc, private_key) |
| 286 | + .map_err(|_| CryptographyError::from(exceptions::InvalidTag::new_err(())))?; |
| 287 | + let (key, base_nonce) = self.key_schedule(py, shared_secret.as_bytes(), info_bytes)?; |
| 288 | + |
| 289 | + let aesgcm = AesGcm::new(py, pyo3::types::PyBytes::new(py, &key).unbind().into_any())?; |
| 290 | + aesgcm.decrypt( |
| 291 | + py, |
| 292 | + CffiBuf::from_bytes(py, &base_nonce), |
| 293 | + CffiBuf::from_bytes(py, ct), |
| 294 | + aad, |
| 295 | + ) |
| 296 | + } |
| 297 | + |
238 | 298 | fn key_schedule( |
239 | 299 | &self, |
240 | 300 | py: pyo3::Python<'_>, |
@@ -297,71 +357,59 @@ impl Suite { |
297 | 357 | }) |
298 | 358 | } |
299 | 359 |
|
300 | | - #[pyo3(signature = (plaintext, public_key, info=None, aad=None))] |
| 360 | + #[pyo3(signature = (plaintext, public_key, info=None))] |
301 | 361 | fn encrypt<'p>( |
302 | 362 | &self, |
303 | 363 | py: pyo3::Python<'p>, |
304 | 364 | plaintext: CffiBuf<'_>, |
305 | 365 | public_key: &pyo3::Bound<'_, pyo3::PyAny>, |
306 | 366 | info: Option<CffiBuf<'_>>, |
307 | | - aad: Option<CffiBuf<'_>>, |
308 | 367 | ) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> { |
309 | | - let info_bytes: &[u8] = info.as_ref().map(|b| b.as_bytes()).unwrap_or(b""); |
310 | | - |
311 | | - let (shared_secret, enc) = self.encap(py, public_key)?; |
312 | | - let (key, base_nonce) = self.key_schedule(py, shared_secret.as_bytes(), info_bytes)?; |
313 | | - |
314 | | - let aesgcm = AesGcm::new(py, pyo3::types::PyBytes::new(py, &key).unbind().into_any())?; |
315 | | - let ct = aesgcm.encrypt(py, CffiBuf::from_bytes(py, &base_nonce), plaintext, aad)?; |
316 | | - |
317 | | - let enc_bytes = enc.as_bytes(); |
318 | | - let ct_bytes = ct.as_bytes(); |
319 | | - Ok(pyo3::types::PyBytes::new_with( |
320 | | - py, |
321 | | - enc_bytes.len() + ct_bytes.len(), |
322 | | - |buf| { |
323 | | - buf[..enc_bytes.len()].copy_from_slice(enc_bytes); |
324 | | - buf[enc_bytes.len()..].copy_from_slice(ct_bytes); |
325 | | - Ok(()) |
326 | | - }, |
327 | | - )?) |
| 368 | + self.encrypt_inner(py, plaintext, public_key, info, None) |
328 | 369 | } |
329 | 370 |
|
330 | | - #[pyo3(signature = (ciphertext, private_key, info=None, aad=None))] |
| 371 | + #[pyo3(signature = (ciphertext, private_key, info=None))] |
331 | 372 | fn decrypt<'p>( |
332 | 373 | &self, |
333 | 374 | py: pyo3::Python<'p>, |
334 | 375 | ciphertext: CffiBuf<'_>, |
335 | 376 | private_key: &pyo3::Bound<'_, pyo3::PyAny>, |
336 | 377 | info: Option<CffiBuf<'_>>, |
337 | | - aad: Option<CffiBuf<'_>>, |
338 | 378 | ) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> { |
339 | | - let ct_bytes = ciphertext.as_bytes(); |
340 | | - if ct_bytes.len() < kem_params::X25519_NENC + aead_params::AES_128_GCM_NT { |
341 | | - return Err(CryptographyError::from(exceptions::InvalidTag::new_err(()))); |
342 | | - } |
343 | | - |
344 | | - let info_bytes: &[u8] = info.as_ref().map(|b| b.as_bytes()).unwrap_or(b""); |
345 | | - |
346 | | - let (enc, ct) = ct_bytes.split_at(kem_params::X25519_NENC); |
| 379 | + self.decrypt_inner(py, ciphertext, private_key, info, None) |
| 380 | + } |
| 381 | +} |
347 | 382 |
|
348 | | - let shared_secret = self |
349 | | - .decap(py, enc, private_key) |
350 | | - .map_err(|_| CryptographyError::from(exceptions::InvalidTag::new_err(())))?; |
351 | | - let (key, base_nonce) = self.key_schedule(py, shared_secret.as_bytes(), info_bytes)?; |
| 383 | +#[pyo3::pyfunction] |
| 384 | +#[pyo3(signature = (suite, plaintext, public_key, info=None, aad=None))] |
| 385 | +fn _encrypt_with_aad<'p>( |
| 386 | + py: pyo3::Python<'p>, |
| 387 | + suite: &Suite, |
| 388 | + plaintext: CffiBuf<'_>, |
| 389 | + public_key: &pyo3::Bound<'_, pyo3::PyAny>, |
| 390 | + info: Option<CffiBuf<'_>>, |
| 391 | + aad: Option<CffiBuf<'_>>, |
| 392 | +) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> { |
| 393 | + suite.encrypt_inner(py, plaintext, public_key, info, aad) |
| 394 | +} |
352 | 395 |
|
353 | | - let aesgcm = AesGcm::new(py, pyo3::types::PyBytes::new(py, &key).unbind().into_any())?; |
354 | | - aesgcm.decrypt( |
355 | | - py, |
356 | | - CffiBuf::from_bytes(py, &base_nonce), |
357 | | - CffiBuf::from_bytes(py, ct), |
358 | | - aad, |
359 | | - ) |
360 | | - } |
| 396 | +#[pyo3::pyfunction] |
| 397 | +#[pyo3(signature = (suite, ciphertext, private_key, info=None, aad=None))] |
| 398 | +fn _decrypt_with_aad<'p>( |
| 399 | + py: pyo3::Python<'p>, |
| 400 | + suite: &Suite, |
| 401 | + ciphertext: CffiBuf<'_>, |
| 402 | + private_key: &pyo3::Bound<'_, pyo3::PyAny>, |
| 403 | + info: Option<CffiBuf<'_>>, |
| 404 | + aad: Option<CffiBuf<'_>>, |
| 405 | +) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> { |
| 406 | + suite.decrypt_inner(py, ciphertext, private_key, info, aad) |
361 | 407 | } |
362 | 408 |
|
363 | 409 | #[pyo3::pymodule(gil_used = false)] |
364 | 410 | pub(crate) mod hpke { |
| 411 | + // stable and nightly rustfmt disagree on import ordering |
| 412 | + #[rustfmt::skip] |
365 | 413 | #[pymodule_export] |
366 | | - use super::{Suite, AEAD, KDF, KEM}; |
| 414 | + use super::{_decrypt_with_aad, _encrypt_with_aad, Suite, AEAD, KDF, KEM}; |
367 | 415 | } |
0 commit comments