Skip to content

Commit

Permalink
Use S/MIME API from cryptography instead of SSL bindings
Browse files Browse the repository at this point in the history
The SROS2 utilities use the Cryptography library, which doesn't
officially support S/MIME. As a result, we had to hit the SSL bindings
directly to get that functionality. These bindings were removed in v3.1
of Cryptography, which means the SROS2 utilities were stuck on v3.0 at
the latest. However, Cryptography has added an official S/MIME API which
is far easier to use than the raw SSL API. Update the SROS2 utilities to
use it.

Resolve ros2#239
Update ros2/ci#507

Signed-off-by: Kyle Fazzari <[email protected]>
  • Loading branch information
kyrofa committed Sep 9, 2020
1 parent 5d57584 commit 07cb361
Showing 1 changed file with 8 additions and 41 deletions.
49 changes: 8 additions & 41 deletions sros2/sros2/api/_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@

from cryptography import x509
from cryptography.hazmat.backends import default_backend as cryptography_backend
from cryptography.hazmat.bindings.openssl.binding import Binding as SSLBinding
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives import smime
from cryptography.hazmat.primitives.asymmetric import ec

_DOMAIN_ID_ENV = 'ROS_DOMAIN_ID'
Expand Down Expand Up @@ -63,7 +63,13 @@ def create_smime_signed_file(cert_path, key_path, unsigned_file_path, signed_fil

# Sign the contents, and write the result to the appropriate place
with open(signed_file_path, 'wb') as f:
f.write(_sign_bytes(cert, private_key, content))
builder = smime.SMIMESignatureBuilder(
content).add_signer(
cert, private_key, hashes.SHA256()
)
f.write(builder.sign(
smime.SMIMEEncoding.PEM,
(smime.SMIMEOptions.Text, smime.SMIMEOptions.DetachedSignature)))


def build_key_and_cert(subject_name, *, ca=False, ca_key=None, issuer_name=''):
Expand Down Expand Up @@ -130,42 +136,3 @@ def load_cert(cert_path):
with open(cert_path, 'rb') as cert_file:
return x509.load_pem_x509_certificate(
cert_file.read(), cryptography_backend())


def _sign_bytes(cert, key, byte_string):
# Using two flags here to get the output required:
# - PKCS7_DETACHED: Use cleartext signing
# - PKCS7_TEXT: Set the MIME headers for text/plain
flags = SSLBinding.lib.PKCS7_DETACHED
flags |= SSLBinding.lib.PKCS7_TEXT

# Convert the byte string into a buffer for SSL
bio_in = SSLBinding.lib.BIO_new_mem_buf(byte_string, len(byte_string))
try:
pkcs7 = SSLBinding.lib.PKCS7_sign(
cert._x509, key._evp_pkey, SSLBinding.ffi.NULL, bio_in, flags)
finally:
# Free the memory allocated for the buffer
SSLBinding.lib.BIO_free(bio_in)

# PKCS7_sign consumes the buffer; allocate a new one again to get it into the final document
bio_in = SSLBinding.lib.BIO_new_mem_buf(byte_string, len(byte_string))
try:
# Allocate a buffer for the output document
bio_out = SSLBinding.lib.BIO_new(SSLBinding.lib.BIO_s_mem())
try:
# Write the final document out to the buffer
SSLBinding.lib.SMIME_write_PKCS7(bio_out, pkcs7, bio_in, flags)

# Copy the output document back to python-managed memory
result_buffer = SSLBinding.ffi.new('char**')
buffer_length = SSLBinding.lib.BIO_get_mem_data(bio_out, result_buffer)
output = SSLBinding.ffi.buffer(result_buffer[0], buffer_length)[:]
finally:
# Free the memory required for the output buffer
SSLBinding.lib.BIO_free(bio_out)
finally:
# Free the memory allocated for the input buffer
SSLBinding.lib.BIO_free(bio_in)

return output

0 comments on commit 07cb361

Please sign in to comment.