Skip to content

Commit

Permalink
Migrate x25519 to use rust-openssl
Browse files Browse the repository at this point in the history
  • Loading branch information
alex committed Dec 26, 2022
1 parent 6a67e27 commit 7e151c9
Show file tree
Hide file tree
Showing 12 changed files with 343 additions and 195 deletions.
63 changes: 10 additions & 53 deletions src/cryptography/hazmat/backends/openssl/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,11 @@
_RSAPrivateKey,
_RSAPublicKey,
)
from cryptography.hazmat.backends.openssl.x25519 import (
_X25519PrivateKey,
_X25519PublicKey,
)
from cryptography.hazmat.backends.openssl.x448 import (
_X448PrivateKey,
_X448PublicKey,
)
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
from cryptography.hazmat.bindings._rust import x509 as rust_x509
from cryptography.hazmat.bindings.openssl import binding
from cryptography.hazmat.primitives import hashes, serialization
Expand Down Expand Up @@ -718,7 +715,9 @@ def _evp_pkey_to_private_key(
# EVP_PKEY_X448 is not present in CRYPTOGRAPHY_IS_LIBRESSL
return _X448PrivateKey(self, evp_pkey)
elif key_type == self._lib.EVP_PKEY_X25519:
return _X25519PrivateKey(self, evp_pkey)
return rust_openssl.x25519.private_key_from_ptr(
int(self._ffi.cast("intptr_t", evp_pkey))
)
elif key_type == getattr(self._lib, "EVP_PKEY_ED448", None):
# EVP_PKEY_ED448 is not present in CRYPTOGRAPHY_IS_LIBRESSL
return _Ed448PrivateKey(self, evp_pkey)
Expand Down Expand Up @@ -775,7 +774,9 @@ def _evp_pkey_to_public_key(self, evp_pkey) -> PUBLIC_KEY_TYPES:
# EVP_PKEY_X448 is not present in CRYPTOGRAPHY_IS_LIBRESSL
return _X448PublicKey(self, evp_pkey)
elif key_type == self._lib.EVP_PKEY_X25519:
return _X25519PublicKey(self, evp_pkey)
return rust_openssl.x25519.public_key_from_ptr(
int(self._ffi.cast("intptr_t", evp_pkey))
)
elif key_type == getattr(self._lib, "EVP_PKEY_ED448", None):
# EVP_PKEY_ED448 is not present in CRYPTOGRAPHY_IS_LIBRESSL
return _Ed448PublicKey(self, evp_pkey)
Expand Down Expand Up @@ -1888,55 +1889,12 @@ def dh_x942_serialization_supported(self) -> bool:
return self._lib.Cryptography_HAS_EVP_PKEY_DHX == 1

def x25519_load_public_bytes(self, data: bytes) -> x25519.X25519PublicKey:
# If/when LibreSSL adds support for EVP_PKEY_new_raw_public_key we
# can switch to it (Cryptography_HAS_RAW_KEY)
if len(data) != 32:
raise ValueError("An X25519 public key is 32 bytes long")

evp_pkey = self._create_evp_pkey_gc()
res = self._lib.EVP_PKEY_set_type(evp_pkey, self._lib.NID_X25519)
self.openssl_assert(res == 1)
res = self._lib.EVP_PKEY_set1_tls_encodedpoint(
evp_pkey, data, len(data)
)
self.openssl_assert(res == 1)
return _X25519PublicKey(self, evp_pkey)
return rust_openssl.x25519.from_public_bytes(data)

def x25519_load_private_bytes(
self, data: bytes
) -> x25519.X25519PrivateKey:
# If/when LibreSSL adds support for EVP_PKEY_new_raw_private_key we
# can switch to it (Cryptography_HAS_RAW_KEY) drop the
# zeroed_bytearray garbage.
# OpenSSL only has facilities for loading PKCS8 formatted private
# keys using the algorithm identifiers specified in
# https://tools.ietf.org/html/draft-ietf-curdle-pkix-09.
# This is the standard PKCS8 prefix for a 32 byte X25519 key.
# The form is:
# 0:d=0 hl=2 l= 46 cons: SEQUENCE
# 2:d=1 hl=2 l= 1 prim: INTEGER :00
# 5:d=1 hl=2 l= 5 cons: SEQUENCE
# 7:d=2 hl=2 l= 3 prim: OBJECT :1.3.101.110
# 12:d=1 hl=2 l= 34 prim: OCTET STRING (the key)
# Of course there's a bit more complexity. In reality OCTET STRING
# contains an OCTET STRING of length 32! So the last two bytes here
# are \x04\x20, which is an OCTET STRING of length 32.
if len(data) != 32:
raise ValueError("An X25519 private key is 32 bytes long")

pkcs8_prefix = b'0.\x02\x01\x000\x05\x06\x03+en\x04"\x04 '
with self._zeroed_bytearray(48) as ba:
ba[0:16] = pkcs8_prefix
ba[16:] = data
bio = self._bytes_to_bio(ba)
evp_pkey = self._lib.d2i_PrivateKey_bio(bio.bio, self._ffi.NULL)

self.openssl_assert(evp_pkey != self._ffi.NULL)
evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
self.openssl_assert(
self._lib.EVP_PKEY_id(evp_pkey) == self._lib.EVP_PKEY_X25519
)
return _X25519PrivateKey(self, evp_pkey)
return rust_openssl.x25519.from_private_bytes(bytes(memoryview(data)))

def _evp_pkey_keygen_gc(self, nid):
evp_pkey_ctx = self._lib.EVP_PKEY_CTX_new_id(nid, self._ffi.NULL)
Expand All @@ -1952,8 +1910,7 @@ def _evp_pkey_keygen_gc(self, nid):
return evp_pkey

def x25519_generate_key(self) -> x25519.X25519PrivateKey:
evp_pkey = self._evp_pkey_keygen_gc(self._lib.NID_X25519)
return _X25519PrivateKey(self, evp_pkey)
return rust_openssl.x25519.generate_key()

def x25519_supported(self) -> bool:
if self._fips_enabled:
Expand Down
132 changes: 0 additions & 132 deletions src/cryptography/hazmat/backends/openssl/x25519.py

This file was deleted.

14 changes: 14 additions & 0 deletions src/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# 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.

from cryptography.hazmat.primitives.asymmetric import x25519

class X25519PrivateKey: ...
class X25519PublicKey: ...

def generate_key() -> x25519.X25519PrivateKey: ...
def private_key_from_ptr(ptr: int) -> x25519.X25519PrivateKey: ...
def public_key_from_ptr(ptr: int) -> x25519.X25519PublicKey: ...
def from_private_bytes(data: bytes) -> x25519.X25519PrivateKey: ...
def from_public_bytes(data: bytes) -> x25519.X25519PublicKey: ...
11 changes: 11 additions & 0 deletions src/cryptography/hazmat/primitives/asymmetric/x25519.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import abc

from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
from cryptography.hazmat.primitives import _serialization


Expand Down Expand Up @@ -33,6 +34,11 @@ def public_bytes(
"""


# For LibreSSL
if hasattr(rust_openssl, "x25519"):
X25519PublicKey.register(rust_openssl.x25519.X25519PublicKey)


class X25519PrivateKey(metaclass=abc.ABCMeta):
@classmethod
def generate(cls) -> "X25519PrivateKey":
Expand Down Expand Up @@ -79,3 +85,8 @@ def exchange(self, peer_public_key: X25519PublicKey) -> bytes:
"""
Performs a key exchange operation using the provided peer's public key.
"""


# For LibreSSL
if hasattr(rust_openssl, "x25519"):
X25519PrivateKey.register(rust_openssl.x25519.X25519PrivateKey)
12 changes: 5 additions & 7 deletions src/rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions src/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ asn1 = { version = "0.13.0", default-features = false }
pem = "1.1"
chrono = { version = "0.4.22", default-features = false, features = ["alloc", "clock"] }
ouroboros = "0.15"
openssl = "0.10.38"
openssl-sys = "0.9.72"
# openssl = "0.10.38"
openssl = { git = "https://github.com/sfackler/rust-openssl" }
# openssl-sys = "0.9.72"
openssl-sys = { git = "https://github.com/sfackler/rust-openssl" }
foreign-types-shared = "0.1"

[build-dependencies]
cc = "1.0.72"
Expand Down
4 changes: 4 additions & 0 deletions src/rust/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ fn main() {
}

build.compile("_openssl.a");

if env::var("DEP_OPENSSL_LIBRESSL_VERSION_NUMBER").is_ok() {
println!("cargo:rustc-cfg=CRYPTOGRAPHY_IS_LIBRESSL")
}
}

/// Run a python script using the specified interpreter binary.
Expand Down
13 changes: 13 additions & 0 deletions src/rust/src/backend/mod.rs
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.

#[cfg(not(CRYPTOGRAPHY_IS_LIBRESSL))]
pub(crate) mod x25519;

pub(crate) fn add_to_module(module: &pyo3::prelude::PyModule) -> pyo3::PyResult<()> {
#[cfg(not(CRYPTOGRAPHY_IS_LIBRESSL))]
module.add_submodule(x25519::create_module(module.py())?)?;

Ok(())
}

0 comments on commit 7e151c9

Please sign in to comment.