From ab332f5869997bb6442ae4c07811add7bcb0c347 Mon Sep 17 00:00:00 2001 From: Hayden Roche Date: Wed, 15 Dec 2021 16:01:10 -0800 Subject: [PATCH] Add a pem_to_der function and support for PEM RSA keys. --- src/wolfcrypt/_build_ffi.py | 26 ++++++++++++++++ src/wolfcrypt/asn.py | 54 ++++++++++++++++++++++++++++++++++ src/wolfcrypt/ciphers.py | 13 ++++++++ src/wolfcrypt/utils.py | 2 +- tests/certs/server-cert.der | Bin 0 -> 1249 bytes tests/certs/server-cert.pem | 29 ++++++++++++++++++ tests/certs/server-key.der | Bin 0 -> 1193 bytes tests/certs/server-key.pem | 27 +++++++++++++++++ tests/certs/server-keyPub.pem | 9 ++++++ tests/test_asn.py | 47 +++++++++++++++++++++++++++++ tests/test_ciphers.py | 40 +++++++++++++++++++++++-- 11 files changed, 243 insertions(+), 4 deletions(-) create mode 100644 src/wolfcrypt/asn.py create mode 100644 tests/certs/server-cert.der create mode 100644 tests/certs/server-cert.pem create mode 100644 tests/certs/server-key.der create mode 100644 tests/certs/server-key.pem create mode 100644 tests/certs/server-keyPub.pem create mode 100644 tests/test_asn.py diff --git a/src/wolfcrypt/_build_ffi.py b/src/wolfcrypt/_build_ffi.py index e270ef4..728373c 100644 --- a/src/wolfcrypt/_build_ffi.py +++ b/src/wolfcrypt/_build_ffi.py @@ -69,6 +69,7 @@ PWDBASED_ENABLED = 0 FIPS_ENABLED = 0 FIPS_VERSION = 0 ERROR_STRINGS_ENABLED = 1 +ASN_ENABLED = 1 # detect native features based on options.h defines if featureDetection: @@ -91,6 +92,7 @@ if featureDetection: KEYGEN_ENABLED = 1 if '#define WOLFSSL_KEY_GEN' in optionsHeaderStr else 0 PWDBASED_ENABLED = 0 if '#define NO_PWDBASED' in optionsHeaderStr else 1 ERROR_STRINGS_ENABLED = 0 if '#define NO_ERROR_STRINGS' in optionsHeaderStr else 1 + ASN_ENABLED = 0 if '#define NO_ASN' in optionsHeaderStr else 1 if '#define HAVE_FIPS' in optionsHeaderStr: FIPS_ENABLED = 1 @@ -153,6 +155,7 @@ ffibuilder.set_source( int PWDBASED_ENABLED = """ + str(PWDBASED_ENABLED) + """; int FIPS_ENABLED = """ + str(FIPS_ENABLED) + """; int FIPS_VERSION = """ + str(FIPS_VERSION) + """; + int ASN_ENABLED = """ + str(ASN_ENABLED) + """; """, include_dirs=[wolfssl_inc_path()], library_dirs=[wolfssl_lib_path()], @@ -180,6 +183,7 @@ _cdef = """ extern int PWDBASED_ENABLED; extern int FIPS_ENABLED; extern int FIPS_VERSION; + extern int ASN_ENABLED; typedef unsigned char byte; typedef unsigned int word32; @@ -452,6 +456,28 @@ if PWDBASED_ENABLED: int typeH); """ +if ASN_ENABLED: + _cdef += """ + static const long PRIVATEKEY_TYPE; + static const long PUBLICKEY_TYPE; + static const long CERT_TYPE; + + typedef struct DerBuffer { + byte* buffer; + void* heap; + word32 length; + int type; + int dynType; + } DerBuffer; + typedef struct { ...; } EncryptedInfo; + + int wc_PemToDer(const unsigned char* buff, long longSz, int type, + DerBuffer** pDer, void* heap, EncryptedInfo* info, + int* keyFormat); + int wc_DerToPemEx(const byte* der, word32 derSz, byte* output, word32 outSz, + byte *cipher_info, int type); + """ + ffibuilder.cdef(_cdef) if __name__ == "__main__": diff --git a/src/wolfcrypt/asn.py b/src/wolfcrypt/asn.py new file mode 100644 index 0000000..5ad3218 --- /dev/null +++ b/src/wolfcrypt/asn.py @@ -0,0 +1,54 @@ +# asn.py +# +# Copyright (C) 2006-2020 wolfSSL Inc. +# +# This file is part of wolfSSL. (formerly known as CyaSSL) +# +# wolfSSL is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# wolfSSL is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +# pylint: disable=no-member,no-name-in-module + +from wolfcrypt._ffi import ffi as _ffi +from wolfcrypt._ffi import lib as _lib + +from wolfcrypt.exceptions import WolfCryptError + + +if _lib.ASN_ENABLED: + def pem_to_der(pem, pem_type): + der = _ffi.new("DerBuffer**") + ret = _lib.wc_PemToDer(pem, len(pem), pem_type, der, _ffi.NULL, + _ffi.NULL, _ffi.NULL) + if ret != 0: + err = "Error converting from PEM to DER. ({})".format(ret) + raise WolfCryptError(err) + + return _ffi.buffer(der[0][0].buffer, der[0][0].length)[:] + + def der_to_pem(der, pem_type): + pem_length = _lib.wc_DerToPemEx(der, len(der), _ffi.NULL, 0, _ffi.NULL, + pem_type) + if pem_length <= 0: + err = "Error getting required PEM buffer length. ({})".format(pem_length) + raise WolfCryptError(err) + + pem = _ffi.new("byte[%d]" % pem_length) + pem_length = _lib.wc_DerToPemEx(der, len(der), pem, pem_length, + _ffi.NULL, pem_type) + if pem_length <= 0: + err = "Error converting from DER to PEM. ({})".format(pem_length) + raise WolfCryptError(err) + + return _ffi.buffer(pem, pem_length)[:] diff --git a/src/wolfcrypt/ciphers.py b/src/wolfcrypt/ciphers.py index 6d4a387..2bd3f6e 100644 --- a/src/wolfcrypt/ciphers.py +++ b/src/wolfcrypt/ciphers.py @@ -24,6 +24,7 @@ from wolfcrypt._ffi import ffi as _ffi from wolfcrypt._ffi import lib as _lib from wolfcrypt.utils import t2b from wolfcrypt.random import Random +from wolfcrypt.asn import pem_to_der from wolfcrypt.exceptions import WolfCryptError @@ -359,6 +360,12 @@ if _lib.RSA_ENABLED: raise WolfCryptError("Invalid key error (%d)" % self.output_size) + if _lib.ASN_ENABLED: + @classmethod + def from_pem(cls, file): + der = pem_to_der(file, _lib.PUBLICKEY_TYPE) + return cls(der) + def encrypt(self, plaintext): """ Encrypts **plaintext**, using the public key data in the @@ -455,6 +462,12 @@ if _lib.RSA_ENABLED: raise WolfCryptError("Invalid key size error (%d)" % self.output_size) + if _lib.ASN_ENABLED: + @classmethod + def from_pem(cls, file): + der = pem_to_der(file, _lib.PRIVATEKEY_TYPE) + return cls(der) + if _lib.KEYGEN_ENABLED: def encode_key(self): """ diff --git a/src/wolfcrypt/utils.py b/src/wolfcrypt/utils.py index dd9b511..c29c7cc 100644 --- a/src/wolfcrypt/utils.py +++ b/src/wolfcrypt/utils.py @@ -31,7 +31,7 @@ _BINARY_TYPE = bytes if _PY3 else str def t2b(string): """ - Converts text to bynary. + Converts text to binary. """ if isinstance(string, _BINARY_TYPE): return string diff --git a/tests/certs/server-cert.der b/tests/certs/server-cert.der new file mode 100644 index 0000000000000000000000000000000000000000..041eba29199388428e2f4ae51190faaf4d0ceaf6 GIT binary patch literal 1249 zcmXqLV!3P3#C&uCGZP~d6CG|Vea-zu8!)AsmEN`ecGd9uw&{u zeZOnpWIFcL%>TV2z5ZX!oWRoWU8mTeo9g#J+*^G>zh-sH3h_UuVykyOP<&dXm#^1$ zV`}lOHPXR5%3#BOpM9qbqouVkAAD?j?AbiUe+VAk|}rn zVM#{49RiP!opIQbx!BD(vpw&~Zt-QGU))_=yr)`UaqHi-K%TuJn;+)J%j{7uzR1bk zDRN0*XUbu%3k#=t?Rj`bD*E_>0|%2U_+88mjHWdhDNkKkzDxMh^x{XcswcB}4_hWI z*|H(i$%*sj4URp=3Y#O8malq!zz{J5#y@kY zEFX&)i^yg{qmz?nv^+ZbJvoW%=vNJQnSxm_33z&I<>5Gv;ns=GY zWL`cVQw=o-!%MH#eB7fYf3N*#_0_;i3e~@Ti@ng(hYB# zYO$v_E{52ZbK0yMQ@LM1 z*BK2ZPvK)@QHu*?KdnQ!r5s!|v#tQ3W5xD=3t4D*bEb?KsRC`U!x;fCxDeyU%s{kh zqee4nhi=5X7pC**-K}%HdoLli{bo@Ny;QT|ZC@O`BXiLS1Bn#U5V>T-D$t>pO1$CJ z8e7Mpz`%`&aVS~lemfzXXPp#Ji0|5X50)hbn0GiM=4^maR8}MYxnp*`6TdK?D<3DaPA_eo%`tNj7f#P3u)4l&OgGF`vV|te^*0veOk|yLR z22fBAcrK}U4HTEO64)_7tDylw!Z7#;Ul!zHchX?;pN;7By6a=!Dwl!pOl;;=G*K%k zH1ty(#8?VHyRWtPN`*r$Jn>jqfdYYn0P-wUb~z(s$q+KPldZ458FiL{WahQFi9$iq zZO|MW_#3Qg%Qie@ecbdJxWizvS4dw{Pgy!ue=@)sLr_1i&Tkj<4xd2EPOTL?$$9C( zSvYfWoCmSK~EnCLK7oQ%lh;bq}BJHHhf#3s64@iUv$zr zE&tGj-}|#5dM=w6uI9Q6u3uB6QtcbWkph8%0PL9ByVd^rQc8USRY`AEZc;CVqbsw9 zVzX4zV^Z4Vh<2dwixs8Q7!-1MUdN*ZAD$UvwHq6r!}7H<`Aos8>kKA+)L%+bT%<(? za73q8z|GP!;=h5Fk}*MtIT}L?7^PDmIT~LdL%fG--)_?)0RAGFa84Q- zDFT6k0E$6YC{dnY)Se4Fn4?tkY*y5&>1i{CBn;Zr3@yaJPXcK;eAMRLOzqbe5yL(J z=fftzQ3Bq(b)0WjoO<()@6U?GE7IBqYDCF%{%)Cv_*=g5eCB1fPi*kX#s3z{iyIf> zSUsXQs{y>fK+aQ)j_vR6S)Jfx=6qiDj!W@7!2*GRO-1=YjKy2@DjJHk@*J#d=?qpj zw@z}F)#ZxP;{WmS7!y5)3AosHr^y^~Qj{=5;PH5L{b3tS3C!kbDsinG-$T4s+ohzw zHE^U}&rkdSRhc?HBhP1A^&oqP$zWt6EvR|mb88V^u+ymx(njZ{&sjv