Merge pull request #26 from haydenroche5/pem

Add a pem_to_der function and support for PEM RSA keys.
pull/27/head
Daniele Lacamera 2021-12-21 03:25:32 -08:00 committed by GitHub
commit 10ba23046b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 243 additions and 4 deletions

View File

@ -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__":

View File

@ -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)[:]

View File

@ -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):
"""

View File

@ -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

Binary file not shown.

View File

@ -0,0 +1,29 @@
-----BEGIN CERTIFICATE-----
MIIE3TCCA8WgAwIBAgIBATANBgkqhkiG9w0BAQsFADCBlDELMAkGA1UEBhMCVVMx
EDAOBgNVBAgMB01vbnRhbmExEDAOBgNVBAcMB0JvemVtYW4xETAPBgNVBAoMCFNh
d3Rvb3RoMRMwEQYDVQQLDApDb25zdWx0aW5nMRgwFgYDVQQDDA93d3cud29sZnNz
bC5jb20xHzAdBgkqhkiG9w0BCQEWEGluZm9Ad29sZnNzbC5jb20wHhcNMjEwMjEw
MTk0OTUzWhcNMjMxMTA3MTk0OTUzWjCBkDELMAkGA1UEBhMCVVMxEDAOBgNVBAgM
B01vbnRhbmExEDAOBgNVBAcMB0JvemVtYW4xEDAOBgNVBAoMB3dvbGZTU0wxEDAO
BgNVBAsMB1N1cHBvcnQxGDAWBgNVBAMMD3d3dy53b2xmc3NsLmNvbTEfMB0GCSqG
SIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAMCVCOFXQfJxbbfSRUEnAWXGRa7yvCQwuJXOL07W9hyIvHyf+6hn
f/5cnFF194rKB+c1L4/hvXvAL3yrZKgX/Mpde7rgIeVyLm8uhtiVc9qsG1O5Xz/X
GQ0lT+FjY1GLC2Q/rUO4pRxcNLOuAKBjxfZ/C1loeHOmjBipAm2vwxkBLrgQ48bM
QLRpo0YzaYduxLsXpvPo3a1zvHsvIbX9ZlEMvVSz4W1fHLwjc9EJA4kU0hC5ZMMq
0KGWSrzh1Bpbx6DAwWN4D0Q3MDKWgDIjlaF3uhPSl3PiXSXJag3DOWCktLBpQkIJ
6dgIvDMgs1gip6rrxOHmYYPF0pbf2dBPrdcCAwEAAaOCATowggE2MB0GA1UdDgQW
BBSzETLJkpiE4sn40DtuA0LKHw6OPDCByQYDVR0jBIHBMIG+gBQnjmcRdMMmHT/t
M2OzpNgdMOXo1aGBmqSBlzCBlDELMAkGA1UEBhMCVVMxEDAOBgNVBAgMB01vbnRh
bmExEDAOBgNVBAcMB0JvemVtYW4xETAPBgNVBAoMCFNhd3Rvb3RoMRMwEQYDVQQL
DApDb25zdWx0aW5nMRgwFgYDVQQDDA93d3cud29sZnNzbC5jb20xHzAdBgkqhkiG
9w0BCQEWEGluZm9Ad29sZnNzbC5jb22CCQCq0z+sGAo3TTAMBgNVHRMEBTADAQH/
MBwGA1UdEQQVMBOCC2V4YW1wbGUuY29thwR/AAABMB0GA1UdJQQWMBQGCCsGAQUF
BwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAGw2mRJMNDgw1KCZAMdLr
JkxHWxn7rf469TA6KNeqaaQV5yZutzNWrI80PfMhL1NYkdA+tDlIv5MRdDbTh0nD
NA0wMKv0TCcZ1cQMrUm9kfjansgtKqzidY6qCNm/Zf+jsU/wYG9NlcQGf69maiM7
OqRhtmzKvuGwd/Psg9WMHYV/jXTI7B5J7FdKzP3iOj5UUK5nzRewZ6VTf8MOPqdY
6N/VDPJk860ScOO5QrwIYHbVDKUxd1DgyPM6PUXPMnXvEN217W7SLVeClTi8fVTE
hF77foP18S2cmKxz46fSAjDWHwYe0Nw6rPTCwr5yQJrqzzUhO1Zt4VLygNc1g5cH
zA==
-----END CERTIFICATE-----

Binary file not shown.

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAwJUI4VdB8nFtt9JFQScBZcZFrvK8JDC4lc4vTtb2HIi8fJ/7
qGd//lycUXX3isoH5zUvj+G9e8AvfKtkqBf8yl17uuAh5XIuby6G2JVz2qwbU7lf
P9cZDSVP4WNjUYsLZD+tQ7ilHFw0s64AoGPF9n8LWWh4c6aMGKkCba/DGQEuuBDj
xsxAtGmjRjNph27Euxem8+jdrXO8ey8htf1mUQy9VLPhbV8cvCNz0QkDiRTSELlk
wyrQoZZKvOHUGlvHoMDBY3gPRDcwMpaAMiOVoXe6E9KXc+JdJclqDcM5YKS0sGlC
Qgnp2Ai8MyCzWCKnquvE4eZhg8XSlt/Z0E+t1wIDAQABAoIBAQCa0DQPUmIFUAHv
n+1kbsLE2hryhNeSEEiSxOlq64t1bMZ5OPLJckqGZFSVd8vDmp231B2kAMieTuTd
x7pnFsF0vKnWlI8rMBr77d8hBSPZSjm9mGtlmrjcxH3upkMVLj2+HSJgKnMw1T7Y
oqyGQy7E9WReP4l1DxHYUSVOn9iqo85gs+KK2X4b8GTKmlsFC1uqy+XjP24yIgXz
0PrvdFKB4l90073/MYNFdfpjepcu1rYZxpIm5CgGUFAOeC6peA0Ul7QS2DFAq6EB
QcIw+AdfFuRhd9Jg8p+N6PS662PeKpeB70xs5lU0USsoNPRTHMRYCj+7r7X3SoVD
LTzxWFiBAoGBAPIsVHY5I2PJEDK3k62vvhl1loFk5rW4iUJB0W3QHBv4G6xpyzY8
ZH3c9Bm4w2CxV0hfUk9ZOlV/MsAZQ1A/rs5vF/MOn0DKTq0VO8l56cBZOHNwnAp8
yTpIMqfYSXUKhcLC/RVz2pkJKmmanwpxv7AEpox6Wm9IWlQ7xrFTF9/nAoGBAMuT
3ncVXbdcXHzYkKmYLdZpDmOzo9ymzItqpKISjI57SCyySzfcBhh96v52odSh6T8N
zRtfr1+elltbD6F8r7ObkNtXczrtsCNErkFPHwdCEyNMy/r0FKTV9542fFufqDzB
hV900jkt/9CE3/uzIHoumxeu5roLrl9TpFLtG8SRAoGBAOyY2rvV/vlSSn0CVUlv
VW5SL4SjK7OGYrNU0mNS2uOIdqDvixWl0xgUcndex6MEH54ZYrUbG57D8rUy+UzB
qusMJn3UX0pRXKRFBnBEp1bA1CIUdp7YY1CJkNPiv4GVkjFBhzkaQwsYpVMfORpf
H0O8h2rfbtMiAP4imHBOGhkpAoGBAIpBVihRnl/Ungs7mKNU8mxW1KrpaTOFJAza
1AwtxL9PAmk4fNTm3Ezt1xYRwz4A58MmwFEC3rt1nG9WnHrzju/PisUr0toGakTJ
c/5umYf4W77xfOZltU9s8MnF/xbKixsX4lg9ojerAby/QM5TjI7t7+5ZneBj5nxe
9Y5L8TvBAoGATUX5QIzFW/QqGoq08hysa+kMVja3TnKW1eWK0uL/8fEYEz2GCbjY
dqfJHHFSlDBD4PF4dP1hG0wJzOZoKnGtHN9DvFbbpaS+NXCkXs9P/ABVmTo9I89n
WvUi+LUp0EQR6zUuRr79jhiyX6i/GTKh9dwD5nyaHwx8qbAOITc78bA=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,9 @@
-----BEGIN RSA PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwJUI4VdB8nFtt9JFQScB
ZcZFrvK8JDC4lc4vTtb2HIi8fJ/7qGd//lycUXX3isoH5zUvj+G9e8AvfKtkqBf8
yl17uuAh5XIuby6G2JVz2qwbU7lfP9cZDSVP4WNjUYsLZD+tQ7ilHFw0s64AoGPF
9n8LWWh4c6aMGKkCba/DGQEuuBDjxsxAtGmjRjNph27Euxem8+jdrXO8ey8htf1m
UQy9VLPhbV8cvCNz0QkDiRTSELlkwyrQoZZKvOHUGlvHoMDBY3gPRDcwMpaAMiOV
oXe6E9KXc+JdJclqDcM5YKS0sGlCQgnp2Ai8MyCzWCKnquvE4eZhg8XSlt/Z0E+t
1wIDAQAB
-----END RSA PUBLIC KEY-----

47
tests/test_asn.py 100644
View File

@ -0,0 +1,47 @@
from collections import namedtuple
import pytest
import os
from wolfcrypt._ffi import lib as _lib
from wolfcrypt.utils import h2b
if _lib.ASN_ENABLED:
from wolfcrypt.asn import pem_to_der, der_to_pem
if _lib.SHA256_ENABLED:
from wolfcrypt.hashes import Sha256
if _lib.RSA_ENABLED:
from wolfcrypt.ciphers import RsaPrivate, RsaPublic
certs_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "certs")
@pytest.fixture
def pem_der_conversion_vectors():
TestVector = namedtuple("TestVector", "pem der type")
TestVector.__new__.__defaults__ = (None,) * len(TestVector._fields)
vectors = []
if _lib.ASN_ENABLED:
files = [
("server-key.pem", "server-key.der", _lib.PRIVATEKEY_TYPE),
("server-cert.pem", "server-cert.der", _lib.CERT_TYPE),
]
for f in files:
pem_path = os.path.join(certs_dir, f[0])
with open(pem_path, "rb") as pem_handle:
pem = pem_handle.read()
der_path = os.path.join(certs_dir, f[1])
with open(der_path, "rb") as der_handle:
der = der_handle.read()
vectors.append(TestVector(pem=pem, der=der, type=f[2]))
return vectors
def test_pem_der_conversion(pem_der_conversion_vectors):
for vector in pem_der_conversion_vectors:
computed_der = pem_to_der(vector.pem, vector.type)
assert computed_der == vector.der
computed_pem = der_to_pem(vector.der, vector.type)
assert computed_pem == vector.pem

View File

@ -25,6 +25,9 @@ import pytest
from wolfcrypt._ffi import ffi as _ffi
from wolfcrypt._ffi import lib as _lib
from wolfcrypt.utils import t2b, h2b
import os
certs_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "certs")
if _lib.DES3_ENABLED:
from wolfcrypt.ciphers import Des3
@ -54,7 +57,8 @@ from wolfcrypt.ciphers import (
@pytest.fixture
def vectors():
TestVector = namedtuple("TestVector", "key iv plaintext ciphertext raw_key pkcs8_key")
TestVector = namedtuple("TestVector", """key iv plaintext ciphertext raw_key
pkcs8_key pem""")
TestVector.__new__.__defaults__ = (None,) * len(TestVector._fields)
# test vector dictionary
@ -88,7 +92,8 @@ def vectors():
"905F3ED9E4D5DF94CAC1A9D719DA86C9E84DC4613682FEABAD7E7725BB8D"
"11A5BC623AA838CC39A20466B4F7F7F3AADA4D020EBB5E8D6948DC77C928"
"0E22E96BA426BA4CE8C1FD4A6F2B1FEF8AAEF69062E5641EEB2B3C67C8DC"
"2700F6916865A90203010001")
"2700F6916865A90203010001"),
pem=os.path.join(certs_dir, "server-keyPub.pem")
)
vectorArray[RsaPrivate]=TestVector(
key=h2b(
@ -153,7 +158,8 @@ def vectors():
"30bab7488c48140ef49f7e779743e1b4"
"19353123759c3b44ad691256ee006164"
"1666d37c742b15b4a2febf086b1a5d3f"
"9012b105863129dbd9e2")
"9012b105863129dbd9e2"),
pem=os.path.join(certs_dir, "server-key.pem")
)
if _lib.ECC_ENABLED:
@ -323,6 +329,18 @@ if _lib.RSA_ENABLED:
def rsa_public(vectors):
return RsaPublic(vectors[RsaPublic].key)
@pytest.fixture
def rsa_private_pem(vectors):
with open(vectors[RsaPrivate].pem, "rb") as f:
pem = f.read()
return RsaPrivate.from_pem(pem)
@pytest.fixture
def rsa_public_pem(vectors):
with open(vectors[RsaPublic].pem, "rb") as f:
pem = f.read()
return RsaPublic.from_pem(pem)
def test_new_rsa_raises(vectors):
with pytest.raises(WolfCryptError):
@ -385,6 +403,22 @@ if _lib.RSA_ENABLED:
assert 1024 / 8 == len(signature) == rsa_private.output_size
assert plaintext == rsa_private.verify(signature)
def test_rsa_sign_verify_pem(rsa_private_pem, rsa_public_pem):
plaintext = t2b("Everyone gets Friday off.")
# normal usage, sign with private, verify with public
signature = rsa_private_pem.sign(plaintext)
assert 256 == len(signature) == rsa_private_pem.output_size
assert plaintext == rsa_public_pem.verify(signature)
# private object holds both private and public info, so it can also verify
# using the known public key.
signature = rsa_private_pem.sign(plaintext)
assert 256 == len(signature) == rsa_private_pem.output_size
assert plaintext == rsa_private_pem.verify(signature)
def test_rsa_pkcs8_sign_verify(rsa_private_pkcs8, rsa_public):
plaintext = t2b("Everyone gets Friday off.")