Add support for AES GCM streaming
parent
bd4432b7be
commit
02ecb1bf09
|
@ -6,6 +6,7 @@ __pycache__/
|
||||||
# C extensions
|
# C extensions
|
||||||
*.so
|
*.so
|
||||||
*.a
|
*.a
|
||||||
|
wolfcrypt/_ffi.*
|
||||||
|
|
||||||
# Distribution / packaging
|
# Distribution / packaging
|
||||||
.Python
|
.Python
|
||||||
|
|
|
@ -11,5 +11,6 @@ Summary
|
||||||
digest
|
digest
|
||||||
mac
|
mac
|
||||||
random
|
random
|
||||||
|
streaming
|
||||||
|
|
||||||
.. include:: ../LICENSING.rst
|
.. include:: ../LICENSING.rst
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
Streaming Encryption Algorithms
|
||||||
|
===============================
|
||||||
|
|
||||||
|
.. module:: wolfcrypt.ciphers
|
||||||
|
|
||||||
|
Steaming Encryption Classes
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
Interface
|
||||||
|
~~~~~~~~~
|
||||||
|
|
||||||
|
AesGcmStreamEncrypt
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. autoclass:: AesGcmStreamEncrypt
|
||||||
|
:members:
|
||||||
|
:inherited-members:
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> from wolfcrypt.ciphers import AesGcmStreamEncrypt
|
||||||
|
>>> from binascii import hexlify as b2h
|
||||||
|
>>> gcm = AesGcmStreamEncrypt(b'fedcba9876543210', b'0123456789abcdef')
|
||||||
|
>>> buf = gcm.update("hello world")
|
||||||
|
>>> authTag = gcm.final()
|
||||||
|
>>> b2h(buf)
|
||||||
|
b'5ba7d42e1bf01d7998e932'
|
||||||
|
>>> b2h(authTag)
|
||||||
|
b'cef91ba0c8c6431c7e19f64c9d9e371b'
|
||||||
|
|
||||||
|
AesGcmStreamDecrypt
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. autoclass:: AesGcmStreamDecrypt
|
||||||
|
:members:
|
||||||
|
:inherited-members:
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> from wolfcrypt.ciphers import AesGcmStreamDecrypt, t2b
|
||||||
|
>>> from binascii import unhexlify as h2b
|
||||||
|
>>> gcm = AesGcmStreamDecrypt(b'fedcba9876543210', b'0123456789abcdef')
|
||||||
|
>>> buf = gcm.update(h2b(b'5ba7d42e1bf01d7998e932'))
|
||||||
|
>>> gcm.final(h2b(b'cef91ba0c8c6431c7e19f64c9d9e371b'))
|
||||||
|
>>> t2b(buf)
|
||||||
|
b'hello world'
|
|
@ -0,0 +1,58 @@
|
||||||
|
# test_hashes.py
|
||||||
|
#
|
||||||
|
# Copyright (C) 2006-2022 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=redefined-outer-name
|
||||||
|
|
||||||
|
from collections import namedtuple
|
||||||
|
import pytest
|
||||||
|
from wolfcrypt._ffi import ffi as _ffi
|
||||||
|
from wolfcrypt._ffi import lib as _lib
|
||||||
|
from wolfcrypt.utils import t2b
|
||||||
|
from binascii import hexlify as b2h, unhexlify as h2b
|
||||||
|
|
||||||
|
from wolfcrypt.ciphers import AesGcmStreamEncrypt, AesGcmStreamDecrypt
|
||||||
|
|
||||||
|
def test_encrypt():
|
||||||
|
key = "fedcba9876543210"
|
||||||
|
iv = "0123456789abcdef"
|
||||||
|
gcm = AesGcmStreamEncrypt(key, iv)
|
||||||
|
buf = gcm.update("hello world")
|
||||||
|
authTag = gcm.final()
|
||||||
|
assert b2h(authTag) == bytes('cef91ba0c8c6431c7e19f64c9d9e371b', 'utf-8')
|
||||||
|
assert b2h(buf) == bytes('5ba7d42e1bf01d7998e932', "utf-8")
|
||||||
|
gcmdec = AesGcmStreamDecrypt(key, iv)
|
||||||
|
bufdec = gcmdec.update(buf)
|
||||||
|
gcmdec.final(authTag)
|
||||||
|
assert bufdec == t2b("hello world")
|
||||||
|
|
||||||
|
def test_multipart():
|
||||||
|
key = "fedcba9876543210"
|
||||||
|
iv = "0123456789abcdef"
|
||||||
|
gcm = AesGcmStreamEncrypt(key, iv)
|
||||||
|
buf = gcm.update("hello")
|
||||||
|
buf += gcm.update(" world")
|
||||||
|
authTag = gcm.final()
|
||||||
|
assert b2h(authTag) == bytes('6862647a27c7b6aa0a6882b3e117e944', 'utf-8')
|
||||||
|
assert b2h(buf) == bytes('5ba7d42e1bf01d7998e932', "utf-8")
|
||||||
|
gcmdec = AesGcmStreamDecrypt(key, iv)
|
||||||
|
bufdec = gcmdec.update(buf[:5])
|
||||||
|
bufdec += gcmdec.update(buf[5:])
|
||||||
|
gcmdec.final(authTag)
|
||||||
|
assert bufdec == t2b("hello world")
|
|
@ -102,6 +102,7 @@ FIPS_VERSION = 0
|
||||||
ERROR_STRINGS_ENABLED = 1
|
ERROR_STRINGS_ENABLED = 1
|
||||||
ASN_ENABLED = 1
|
ASN_ENABLED = 1
|
||||||
WC_RNG_SEED_CB_ENABLED = 0
|
WC_RNG_SEED_CB_ENABLED = 0
|
||||||
|
AESGCM_STREAM = 1
|
||||||
|
|
||||||
# detect native features based on options.h defines
|
# detect native features based on options.h defines
|
||||||
if featureDetection:
|
if featureDetection:
|
||||||
|
@ -126,6 +127,7 @@ if featureDetection:
|
||||||
ERROR_STRINGS_ENABLED = 0 if '#define NO_ERROR_STRINGS' 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
|
ASN_ENABLED = 0 if '#define NO_ASN' in optionsHeaderStr else 1
|
||||||
WC_RNG_SEED_CB_ENABLED = 1 if '#define WC_RNG_SEED_CB' in optionsHeaderStr else 0
|
WC_RNG_SEED_CB_ENABLED = 1 if '#define WC_RNG_SEED_CB' in optionsHeaderStr else 0
|
||||||
|
AESGCM_STREAM = 1 if '#define WOLFSSL_AESGCM_STREAM' in optionsHeaderStr else 0
|
||||||
|
|
||||||
if '#define HAVE_FIPS' in optionsHeaderStr:
|
if '#define HAVE_FIPS' in optionsHeaderStr:
|
||||||
FIPS_ENABLED = 1
|
FIPS_ENABLED = 1
|
||||||
|
@ -202,6 +204,7 @@ extern "C" {
|
||||||
int FIPS_VERSION = """ + str(FIPS_VERSION) + """;
|
int FIPS_VERSION = """ + str(FIPS_VERSION) + """;
|
||||||
int ASN_ENABLED = """ + str(ASN_ENABLED) + """;
|
int ASN_ENABLED = """ + str(ASN_ENABLED) + """;
|
||||||
int WC_RNG_SEED_CB_ENABLED = """ + str(WC_RNG_SEED_CB_ENABLED) + """;
|
int WC_RNG_SEED_CB_ENABLED = """ + str(WC_RNG_SEED_CB_ENABLED) + """;
|
||||||
|
int AESGCM_STREAM = """ + str(AESGCM_STREAM) + """;
|
||||||
""",
|
""",
|
||||||
include_dirs=[wolfssl_inc_path()],
|
include_dirs=[wolfssl_inc_path()],
|
||||||
library_dirs=[wolfssl_lib_path()],
|
library_dirs=[wolfssl_lib_path()],
|
||||||
|
@ -231,6 +234,7 @@ _cdef = """
|
||||||
extern int FIPS_VERSION;
|
extern int FIPS_VERSION;
|
||||||
extern int ASN_ENABLED;
|
extern int ASN_ENABLED;
|
||||||
extern int WC_RNG_SEED_CB_ENABLED;
|
extern int WC_RNG_SEED_CB_ENABLED;
|
||||||
|
extern int AESGCM_STREAM;
|
||||||
|
|
||||||
typedef unsigned char byte;
|
typedef unsigned char byte;
|
||||||
typedef unsigned int word32;
|
typedef unsigned int word32;
|
||||||
|
@ -322,6 +326,27 @@ if AES_ENABLED:
|
||||||
int wc_AesCbcDecrypt(Aes*, byte*, const byte*, word32);
|
int wc_AesCbcDecrypt(Aes*, byte*, const byte*, word32);
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if AES_ENABLED and AESGCM_STREAM:
|
||||||
|
_cdef += """
|
||||||
|
int wc_AesInit(Aes* aes, void* heap, int devId);
|
||||||
|
int wc_AesGcmInit(Aes* aes, const byte* key, word32 len,
|
||||||
|
const byte* iv, word32 ivSz);
|
||||||
|
int wc_AesGcmEncryptInit(Aes* aes, const byte* key, word32 len,
|
||||||
|
const byte* iv, word32 ivSz);
|
||||||
|
int wc_AesGcmEncryptInit_ex(Aes* aes, const byte* key, word32 len,
|
||||||
|
byte* ivOut, word32 ivOutSz);
|
||||||
|
int wc_AesGcmEncryptUpdate(Aes* aes, byte* out, const byte* in,
|
||||||
|
word32 sz, const byte* authIn, word32 authInSz);
|
||||||
|
int wc_AesGcmEncryptFinal(Aes* aes, byte* authTag,
|
||||||
|
word32 authTagSz);
|
||||||
|
int wc_AesGcmDecryptInit(Aes* aes, const byte* key, word32 len,
|
||||||
|
const byte* iv, word32 ivSz);
|
||||||
|
int wc_AesGcmDecryptUpdate(Aes* aes, byte* out, const byte* in,
|
||||||
|
word32 sz, const byte* authIn, word32 authInSz);
|
||||||
|
int wc_AesGcmDecryptFinal(Aes* aes, const byte* authTag,
|
||||||
|
word32 authTagSz);
|
||||||
|
"""
|
||||||
|
|
||||||
if CHACHA_ENABLED:
|
if CHACHA_ENABLED:
|
||||||
_cdef += """
|
_cdef += """
|
||||||
typedef struct { ...; } ChaCha;
|
typedef struct { ...; } ChaCha;
|
||||||
|
|
|
@ -142,7 +142,7 @@ def make_flags(prefix):
|
||||||
flags.append("-DWOLFSSL_AES=yes")
|
flags.append("-DWOLFSSL_AES=yes")
|
||||||
flags.append("-DWOLFSSL_DES3=yes")
|
flags.append("-DWOLFSSL_DES3=yes")
|
||||||
flags.append("-DWOLFSSL_CHACHA=yes")
|
flags.append("-DWOLFSSL_CHACHA=yes")
|
||||||
flags.append("-DWOLFSSL_AESGCM=no")
|
flags.append("-DWOLFSSL_AESGCM=yes")
|
||||||
flags.append("-DWOLFSSL_SHA=yes")
|
flags.append("-DWOLFSSL_SHA=yes")
|
||||||
flags.append("-DWOLFSSL_SHA384=yes")
|
flags.append("-DWOLFSSL_SHA384=yes")
|
||||||
flags.append("-DWOLFSSL_SHA512=yes")
|
flags.append("-DWOLFSSL_SHA512=yes")
|
||||||
|
@ -164,7 +164,7 @@ def make_flags(prefix):
|
||||||
flags.append("-DWOLFSSL_EXTENDED_MASTER=no")
|
flags.append("-DWOLFSSL_EXTENDED_MASTER=no")
|
||||||
flags.append("-DWOLFSSL_ERROR_STRINGS=no")
|
flags.append("-DWOLFSSL_ERROR_STRINGS=no")
|
||||||
# Part of hack for missing CMake option
|
# Part of hack for missing CMake option
|
||||||
flags.append("-DCMAKE_C_FLAGS=\"/DWOLFSSL_KEY_GEN=1 /DWOLFCRYPT_ONLY=1\"")
|
flags.append("-DCMAKE_C_FLAGS=\"/DWOLFSSL_KEY_GEN=1 /DWOLFCRYPT_ONLY=1 /DWOLFSSL_AESGCM_STREAM=1\"")
|
||||||
|
|
||||||
return " ".join(flags)
|
return " ".join(flags)
|
||||||
else:
|
else:
|
||||||
|
@ -186,7 +186,9 @@ def make_flags(prefix):
|
||||||
flags.append("--enable-des3")
|
flags.append("--enable-des3")
|
||||||
flags.append("--enable-chacha")
|
flags.append("--enable-chacha")
|
||||||
|
|
||||||
flags.append("--disable-aesgcm")
|
flags.append("--enable-aesgcm-stream")
|
||||||
|
|
||||||
|
flags.append("--enable-aesgcm")
|
||||||
|
|
||||||
# hashes and MACs
|
# hashes and MACs
|
||||||
flags.append("--enable-sha")
|
flags.append("--enable-sha")
|
||||||
|
@ -232,6 +234,8 @@ def cmake_hack():
|
||||||
contents.insert(27, "#define WOLFSSL_KEY_GEN\n")
|
contents.insert(27, "#define WOLFSSL_KEY_GEN\n")
|
||||||
contents.insert(28, "#undef WOLFCRYPT_ONLY\n")
|
contents.insert(28, "#undef WOLFCRYPT_ONLY\n")
|
||||||
contents.insert(29, "#define WOLFCRYPT_ONLY\n")
|
contents.insert(29, "#define WOLFCRYPT_ONLY\n")
|
||||||
|
contents.insert(30, "#undef WOLFSSL_AESGCM_STREAM\n")
|
||||||
|
contents.insert(31, "#define WOLFSSL_AESGCM_STREAM\n")
|
||||||
|
|
||||||
with open(options_file, "w") as f:
|
with open(options_file, "w") as f:
|
||||||
contents = "".join(contents)
|
contents = "".join(contents)
|
||||||
|
@ -272,9 +276,9 @@ def build_wolfssl(version="master"):
|
||||||
else:
|
else:
|
||||||
libfile = os.path.join(prefix, 'lib/libwolfssl.la')
|
libfile = os.path.join(prefix, 'lib/libwolfssl.la')
|
||||||
|
|
||||||
rebuild = ensure_wolfssl_src(version)
|
ensure_wolfssl_src(version)
|
||||||
|
|
||||||
if rebuild or not os.path.isfile(libfile):
|
if not os.path.isfile(libfile):
|
||||||
make(make_flags(prefix))
|
make(make_flags(prefix))
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -230,6 +230,74 @@ if _lib.AES_ENABLED:
|
||||||
return _lib.wc_AesCbcDecrypt(self._dec, destination,
|
return _lib.wc_AesCbcDecrypt(self._dec, destination,
|
||||||
source, len(source))
|
source, len(source))
|
||||||
|
|
||||||
|
if _lib.AESGCM_STREAM:
|
||||||
|
class _AesGcmStream(object):
|
||||||
|
"""
|
||||||
|
AES GCM Stream
|
||||||
|
"""
|
||||||
|
block_size = 16
|
||||||
|
_key_sizes = [16, 24, 32]
|
||||||
|
_native_type = "Aes *"
|
||||||
|
|
||||||
|
def __init__(self, key, IV):
|
||||||
|
key = t2b(key)
|
||||||
|
IV = t2b(IV)
|
||||||
|
if len(key) not in self._key_sizes:
|
||||||
|
raise ValueError("key must be %s in length, not %d" %
|
||||||
|
(self._key_sizes, len(key)))
|
||||||
|
self._native_object = _ffi.new(self._native_type)
|
||||||
|
_lib.wc_AesInit(self._native_object, _ffi.NULL, -2)
|
||||||
|
self._authIn = _ffi.new("byte[%d]" % self.block_size)
|
||||||
|
ret = _lib.wc_AesGcmInit(self._native_object, key, len(key), IV, len(IV))
|
||||||
|
if ret < 0:
|
||||||
|
raise WolfCryptError("Init error (%d)" % ret)
|
||||||
|
|
||||||
|
def update(self, data):
|
||||||
|
"""
|
||||||
|
Updates the stream with another segment of data.
|
||||||
|
"""
|
||||||
|
ret = 0
|
||||||
|
data = t2b(data)
|
||||||
|
self._buf = _ffi.new("byte[%d]" % (len(data)))
|
||||||
|
ret = self._update(data)
|
||||||
|
if ret < 0:
|
||||||
|
raise WolfCryptError("Decryption error (%d)" % ret)
|
||||||
|
return bytes(self._buf)
|
||||||
|
|
||||||
|
class AesGcmStreamEncrypt(_AesGcmStream):
|
||||||
|
"""
|
||||||
|
AES GCM Streaming Encryption
|
||||||
|
"""
|
||||||
|
def _update(self, data):
|
||||||
|
return _lib.wc_AesGcmEncryptUpdate(self._native_object, self._buf, data, len(data), self._authIn, self.block_size)
|
||||||
|
|
||||||
|
def final(self):
|
||||||
|
"""
|
||||||
|
Finalize the stream and return an authentication tag for the stream.
|
||||||
|
"""
|
||||||
|
authTag = _ffi.new("byte[%d]" % self.block_size)
|
||||||
|
ret = _lib.wc_AesGcmEncryptFinal(self._native_object, authTag, self.block_size)
|
||||||
|
if ret < 0:
|
||||||
|
raise WolfCryptError("Encryption error (%d)" % ret)
|
||||||
|
return _ffi.buffer(authTag)[:]
|
||||||
|
|
||||||
|
|
||||||
|
class AesGcmStreamDecrypt(_AesGcmStream):
|
||||||
|
"""
|
||||||
|
AES GCM Streaming Decryption
|
||||||
|
"""
|
||||||
|
def _update(self, data):
|
||||||
|
return _lib.wc_AesGcmDecryptUpdate(self._native_object, self._buf, data, len(data), self._authIn, self.block_size)
|
||||||
|
|
||||||
|
def final(self, authTag):
|
||||||
|
"""
|
||||||
|
Finalize the stream and verify using the provided authentication tag.
|
||||||
|
"""
|
||||||
|
authTag = t2b(authTag)
|
||||||
|
ret = _lib.wc_AesGcmDecryptFinal(self._native_object, authTag, self.block_size)
|
||||||
|
if ret < 0:
|
||||||
|
raise WolfCryptError("Decryption error (%d)" % ret)
|
||||||
|
|
||||||
if _lib.CHACHA_ENABLED:
|
if _lib.CHACHA_ENABLED:
|
||||||
class ChaCha(_Cipher):
|
class ChaCha(_Cipher):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue