Skip to content

Commit

Permalink
Convert Poly1305 to Rust
Browse files Browse the repository at this point in the history
  • Loading branch information
alex committed May 17, 2023
1 parent 736df2d commit f860f9a
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 135 deletions.
11 changes: 0 additions & 11 deletions src/cryptography/hazmat/backends/openssl/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@
_EllipticCurvePrivateKey,
_EllipticCurvePublicKey,
)
from cryptography.hazmat.backends.openssl.poly1305 import (
_POLY1305_KEY_SIZE,
_Poly1305Context,
)
from cryptography.hazmat.backends.openssl.rsa import (
_RSAPrivateKey,
_RSAPublicKey,
Expand Down Expand Up @@ -1949,13 +1945,6 @@ def poly1305_supported(self) -> bool:
return False
return self._lib.Cryptography_HAS_POLY1305 == 1

def create_poly1305_ctx(self, key: bytes) -> _Poly1305Context:
utils._check_byteslike("key", key)
if len(key) != _POLY1305_KEY_SIZE:
raise ValueError("A poly1305 key is 32 bytes long")

return _Poly1305Context(self, key)

def pkcs7_supported(self) -> bool:
return not self._lib.CRYPTOGRAPHY_IS_BORINGSSL

Expand Down
69 changes: 0 additions & 69 deletions src/cryptography/hazmat/backends/openssl/poly1305.py

This file was deleted.

2 changes: 2 additions & 0 deletions src/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ from cryptography.hazmat.bindings._rust.openssl import (
hashes,
hmac,
kdf,
poly1305,
x448,
x25519,
)
Expand All @@ -24,6 +25,7 @@ __all__ = [
"kdf",
"ed448",
"ed25519",
"poly1305",
"x448",
"x25519",
]
Expand Down
13 changes: 13 additions & 0 deletions src/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

class Poly1305:
def __init__(self, key: bytes) -> None: ...
@staticmethod
def generate_tag(key: bytes, data: bytes) -> bytes: ...
@staticmethod
def verify_tag(key: bytes, data: bytes, tag: bytes) -> None: ...
def update(self, data: bytes) -> None: ...
def finalize(self) -> bytes: ...
def verify(self, tag: bytes) -> None: ...
57 changes: 3 additions & 54 deletions src/cryptography/hazmat/primitives/poly1305.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,59 +4,8 @@

from __future__ import annotations

import typing
from cryptography.hazmat.bindings._rust import openssl as rust_openssl

from cryptography import utils
from cryptography.exceptions import (
AlreadyFinalized,
UnsupportedAlgorithm,
_Reasons,
)
from cryptography.hazmat.backends.openssl.poly1305 import _Poly1305Context
__all__ = ["Poly1305"]


class Poly1305:
_ctx: typing.Optional[_Poly1305Context]

def __init__(self, key: bytes):
from cryptography.hazmat.backends.openssl.backend import backend

if not backend.poly1305_supported():
raise UnsupportedAlgorithm(
"poly1305 is not supported by this version of OpenSSL.",
_Reasons.UNSUPPORTED_MAC,
)
self._ctx = backend.create_poly1305_ctx(key)

def update(self, data: bytes) -> None:
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized.")
utils._check_byteslike("data", data)
self._ctx.update(data)

def finalize(self) -> bytes:
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized.")
mac = self._ctx.finalize()
self._ctx = None
return mac

def verify(self, tag: bytes) -> None:
utils._check_bytes("tag", tag)
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized.")

ctx, self._ctx = self._ctx, None
ctx.verify(tag)

@classmethod
def generate_tag(cls, key: bytes, data: bytes) -> bytes:
p = Poly1305(key)
p.update(data)
return p.finalize()

@classmethod
def verify_tag(cls, key: bytes, data: bytes, tag: bytes) -> None:
p = Poly1305(key)
p.update(data)
p.verify(tag)
Poly1305 = rust_openssl.poly1305.Poly1305
2 changes: 1 addition & 1 deletion src/rust/src/backend/hmac.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ impl Hmac {
let actual = self.finalize(py)?.as_bytes();
if actual.len() != signature.len() || !openssl::memcmp::eq(actual, signature) {
return Err(CryptographyError::from(
exceptions::InvalidSignature::new_err(("Signature did not match digest.",)),
exceptions::InvalidSignature::new_err("Signature did not match digest."),
));
}

Expand Down
3 changes: 3 additions & 0 deletions src/rust/src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub(crate) mod ed448;
pub(crate) mod hashes;
pub(crate) mod hmac;
pub(crate) mod kdf;
pub(crate) mod poly1305;
pub(crate) mod utils;
#[cfg(any(not(CRYPTOGRAPHY_IS_LIBRESSL), CRYPTOGRAPHY_LIBRESSL_370_OR_GREATER))]
pub(crate) mod x25519;
Expand All @@ -29,6 +30,8 @@ pub(crate) fn add_to_module(module: &pyo3::prelude::PyModule) -> pyo3::PyResult<
#[cfg(all(not(CRYPTOGRAPHY_IS_LIBRESSL), not(CRYPTOGRAPHY_IS_BORINGSSL)))]
module.add_submodule(x448::create_module(module.py())?)?;

module.add_submodule(poly1305::create_module(module.py())?)?;

module.add_submodule(hashes::create_module(module.py())?)?;
module.add_submodule(hmac::create_module(module.py())?)?;
module.add_submodule(kdf::create_module(module.py())?)?;
Expand Down
127 changes: 127 additions & 0 deletions src/rust/src/backend/poly1305.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// This file is dual licensed under the terms of the Apache License, Version
// 2.0, and the BSD License. See the LICENSE file in the root of this repository
// for complete details.

use crate::backend::hashes::already_finalized_error;
use crate::buf::CffiBuf;
use crate::error::{CryptographyError, CryptographyResult};
use crate::exceptions;

#[pyo3::prelude::pyclass(module = "cryptography.hazmat.bindings._rust.openssl.poly1305")]
struct Poly1305 {
signer: Option<openssl::sign::Signer<'static>>,
}

impl Poly1305 {
fn get_mut_signer(&mut self) -> CryptographyResult<&mut openssl::sign::Signer<'static>> {
if let Some(signer) = self.signer.as_mut() {
return Ok(signer);
};
Err(already_finalized_error())
}
}

#[pyo3::pymethods]
impl Poly1305 {
#[new]
fn new(key: CffiBuf<'_>) -> CryptographyResult<Poly1305> {
#[cfg(any(CRYPTOGRAPHY_IS_LIBRESSL, CRYPTOGRAPHY_IS_BORINGSSL))]
{
return Err(CryptographyError::from(
exceptions::UnsupportedAlgorithm::new_err((
"poly1305 is not supported by this version of OpenSSL.",
exceptions::Reasons::UNSUPPORTED_MAC,
)),
));
}

#[cfg(all(not(CRYPTOGRAPHY_IS_LIBRESSL), not(CRYPTOGRAPHY_IS_BORINGSSL)))]
{
if cryptography_openssl::fips::is_enabled() {
return Err(CryptographyError::from(
exceptions::UnsupportedAlgorithm::new_err((
"poly1305 is not supported by this version of OpenSSL.",
exceptions::Reasons::UNSUPPORTED_MAC,
)),
));
}

let pkey = openssl::pkey::PKey::private_key_from_raw_bytes(
key.as_bytes(),
openssl::pkey::Id::POLY1305,
)
.map_err(|_| {
pyo3::exceptions::PyValueError::new_err("A poly1305 key is 32 bytes long")
})?;

Ok(Poly1305 {
signer: Some(
openssl::sign::Signer::new_without_digest(&pkey).map_err(|_| {
pyo3::exceptions::PyValueError::new_err("A poly1305 key is 32 bytes long")
})?,
),
})
}
}

#[staticmethod]
fn generate_tag<'p>(
py: pyo3::Python<'p>,
key: CffiBuf<'_>,
data: CffiBuf<'_>,
) -> CryptographyResult<&'p pyo3::types::PyBytes> {
let mut p = Poly1305::new(key)?;
p.update(data)?;
p.finalize(py)
}

#[staticmethod]
fn verify_tag(
py: pyo3::Python<'_>,
key: CffiBuf<'_>,
data: CffiBuf<'_>,
tag: &[u8],
) -> CryptographyResult<()> {
let mut p = Poly1305::new(key)?;
p.update(data)?;
p.verify(py, tag)
}

fn update(&mut self, data: CffiBuf<'_>) -> CryptographyResult<()> {
self.get_mut_signer()?.update(data.as_bytes())?;
Ok(())
}

fn finalize<'p>(
&mut self,
py: pyo3::Python<'p>,
) -> CryptographyResult<&'p pyo3::types::PyBytes> {
let signer = self.get_mut_signer()?;
let result = pyo3::types::PyBytes::new_with(py, signer.len()?, |b| {
let n = signer.sign(b).unwrap();
assert_eq!(n, b.len());
Ok(())
})?;
self.signer = None;
Ok(result)
}

fn verify(&mut self, py: pyo3::Python<'_>, signature: &[u8]) -> CryptographyResult<()> {
let actual = self.finalize(py)?.as_bytes();
if actual.len() != signature.len() || !openssl::memcmp::eq(actual, signature) {
return Err(CryptographyError::from(
exceptions::InvalidSignature::new_err("Value did not match computed tag."),
));
}

Ok(())
}
}

pub(crate) fn create_module(py: pyo3::Python<'_>) -> pyo3::PyResult<&pyo3::prelude::PyModule> {
let m = pyo3::prelude::PyModule::new(py, "poly1305")?;

m.add_class::<Poly1305>()?;

Ok(m)
}

0 comments on commit f860f9a

Please sign in to comment.