venv added, updated

This commit is contained in:
Norbert
2024-09-13 09:46:28 +02:00
parent 577596d9f3
commit 82af8c809a
4812 changed files with 640223 additions and 2 deletions

View File

@@ -0,0 +1,17 @@
from ._jwe_algorithms import JWE_DRAFT_ALG_ALGORITHMS
from ._jwe_enc_cryptography import C20PEncAlgorithm
try:
from ._jwe_enc_cryptodome import XC20PEncAlgorithm
except ImportError:
XC20PEncAlgorithm = None
def register_jwe_draft(cls):
for alg in JWE_DRAFT_ALG_ALGORITHMS:
cls.register_algorithm(alg)
cls.register_algorithm(C20PEncAlgorithm(256)) # C20P
if XC20PEncAlgorithm is not None:
cls.register_algorithm(XC20PEncAlgorithm(256)) # XC20P
__all__ = ['register_jwe_draft']

View File

@@ -0,0 +1,188 @@
import struct
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.concatkdf import ConcatKDFHash
from authlib.jose.errors import InvalidEncryptionAlgorithmForECDH1PUWithKeyWrappingError
from authlib.jose.rfc7516 import JWEAlgorithmWithTagAwareKeyAgreement
from authlib.jose.rfc7518 import AESAlgorithm, CBCHS2EncAlgorithm, ECKey, u32be_len_input
from authlib.jose.rfc8037 import OKPKey
class ECDH1PUAlgorithm(JWEAlgorithmWithTagAwareKeyAgreement):
EXTRA_HEADERS = ['epk', 'apu', 'apv', 'skid']
ALLOWED_KEY_CLS = (ECKey, OKPKey)
# https://datatracker.ietf.org/doc/html/draft-madden-jose-ecdh-1pu-04
def __init__(self, key_size=None):
if key_size is None:
self.name = 'ECDH-1PU'
self.description = 'ECDH-1PU in the Direct Key Agreement mode'
else:
self.name = f'ECDH-1PU+A{key_size}KW'
self.description = (
'ECDH-1PU using Concat KDF and CEK wrapped '
'with A{}KW').format(key_size)
self.key_size = key_size
self.aeskw = AESAlgorithm(key_size)
def prepare_key(self, raw_data):
if isinstance(raw_data, self.ALLOWED_KEY_CLS):
return raw_data
return ECKey.import_key(raw_data)
def generate_preset(self, enc_alg, key):
epk = self._generate_ephemeral_key(key)
h = self._prepare_headers(epk)
preset = {'epk': epk, 'header': h}
if self.key_size is not None:
cek = enc_alg.generate_cek()
preset['cek'] = cek
return preset
def compute_shared_key(self, shared_key_e, shared_key_s):
return shared_key_e + shared_key_s
def compute_fixed_info(self, headers, bit_size, tag):
if tag is None:
cctag = b''
else:
cctag = u32be_len_input(tag)
# AlgorithmID
if self.key_size is None:
alg_id = u32be_len_input(headers['enc'])
else:
alg_id = u32be_len_input(headers['alg'])
# PartyUInfo
apu_info = u32be_len_input(headers.get('apu'), True)
# PartyVInfo
apv_info = u32be_len_input(headers.get('apv'), True)
# SuppPubInfo
pub_info = struct.pack('>I', bit_size) + cctag
return alg_id + apu_info + apv_info + pub_info
def compute_derived_key(self, shared_key, fixed_info, bit_size):
ckdf = ConcatKDFHash(
algorithm=hashes.SHA256(),
length=bit_size // 8,
otherinfo=fixed_info,
backend=default_backend()
)
return ckdf.derive(shared_key)
def deliver_at_sender(self, sender_static_key, sender_ephemeral_key, recipient_pubkey, headers, bit_size, tag):
shared_key_s = sender_static_key.exchange_shared_key(recipient_pubkey)
shared_key_e = sender_ephemeral_key.exchange_shared_key(recipient_pubkey)
shared_key = self.compute_shared_key(shared_key_e, shared_key_s)
fixed_info = self.compute_fixed_info(headers, bit_size, tag)
return self.compute_derived_key(shared_key, fixed_info, bit_size)
def deliver_at_recipient(self, recipient_key, sender_static_pubkey, sender_ephemeral_pubkey, headers, bit_size, tag):
shared_key_s = recipient_key.exchange_shared_key(sender_static_pubkey)
shared_key_e = recipient_key.exchange_shared_key(sender_ephemeral_pubkey)
shared_key = self.compute_shared_key(shared_key_e, shared_key_s)
fixed_info = self.compute_fixed_info(headers, bit_size, tag)
return self.compute_derived_key(shared_key, fixed_info, bit_size)
def _generate_ephemeral_key(self, key):
return key.generate_key(key['crv'], is_private=True)
def _prepare_headers(self, epk):
# REQUIRED_JSON_FIELDS contains only public fields
pub_epk = {k: epk[k] for k in epk.REQUIRED_JSON_FIELDS}
pub_epk['kty'] = epk.kty
return {'epk': pub_epk}
def generate_keys_and_prepare_headers(self, enc_alg, key, sender_key, preset=None):
if not isinstance(enc_alg, CBCHS2EncAlgorithm):
raise InvalidEncryptionAlgorithmForECDH1PUWithKeyWrappingError()
if preset and 'epk' in preset:
epk = preset['epk']
h = {}
else:
epk = self._generate_ephemeral_key(key)
h = self._prepare_headers(epk)
if preset and 'cek' in preset:
cek = preset['cek']
else:
cek = enc_alg.generate_cek()
return {'epk': epk, 'cek': cek, 'header': h}
def _agree_upon_key_at_sender(self, enc_alg, headers, key, sender_key, epk, tag=None):
if self.key_size is None:
bit_size = enc_alg.CEK_SIZE
else:
bit_size = self.key_size
public_key = key.get_op_key('wrapKey')
return self.deliver_at_sender(sender_key, epk, public_key, headers, bit_size, tag)
def _wrap_cek(self, cek, dk):
kek = self.aeskw.prepare_key(dk)
return self.aeskw.wrap_cek(cek, kek)
def agree_upon_key_and_wrap_cek(self, enc_alg, headers, key, sender_key, epk, cek, tag):
dk = self._agree_upon_key_at_sender(enc_alg, headers, key, sender_key, epk, tag)
return self._wrap_cek(cek, dk)
def wrap(self, enc_alg, headers, key, sender_key, preset=None):
# In this class this method is used in direct key agreement mode only
if self.key_size is not None:
raise RuntimeError('Invalid algorithm state detected')
if preset and 'epk' in preset:
epk = preset['epk']
h = {}
else:
epk = self._generate_ephemeral_key(key)
h = self._prepare_headers(epk)
dk = self._agree_upon_key_at_sender(enc_alg, headers, key, sender_key, epk)
return {'ek': b'', 'cek': dk, 'header': h}
def unwrap(self, enc_alg, ek, headers, key, sender_key, tag=None):
if 'epk' not in headers:
raise ValueError('Missing "epk" in headers')
if self.key_size is None:
bit_size = enc_alg.CEK_SIZE
else:
bit_size = self.key_size
sender_pubkey = sender_key.get_op_key('wrapKey')
epk = key.import_key(headers['epk'])
epk_pubkey = epk.get_op_key('wrapKey')
dk = self.deliver_at_recipient(key, sender_pubkey, epk_pubkey, headers, bit_size, tag)
if self.key_size is None:
return dk
kek = self.aeskw.prepare_key(dk)
return self.aeskw.unwrap(enc_alg, ek, headers, kek)
JWE_DRAFT_ALG_ALGORITHMS = [
ECDH1PUAlgorithm(None), # ECDH-1PU
ECDH1PUAlgorithm(128), # ECDH-1PU+A128KW
ECDH1PUAlgorithm(192), # ECDH-1PU+A192KW
ECDH1PUAlgorithm(256), # ECDH-1PU+A256KW
]
def register_jwe_alg_draft(cls):
for alg in JWE_DRAFT_ALG_ALGORITHMS:
cls.register_algorithm(alg)

View File

@@ -0,0 +1,52 @@
"""
authlib.jose.draft
~~~~~~~~~~~~~~~~~~~~
Content Encryption per `Section 4`_.
.. _`Section 4`: https://datatracker.ietf.org/doc/html/draft-amringer-jose-chacha-02#section-4
"""
from authlib.jose.rfc7516 import JWEEncAlgorithm
from Cryptodome.Cipher import ChaCha20_Poly1305 as Cryptodome_ChaCha20_Poly1305
class XC20PEncAlgorithm(JWEEncAlgorithm):
# Use of an IV of size 192 bits is REQUIRED with this algorithm.
# https://datatracker.ietf.org/doc/html/draft-amringer-jose-chacha-02#section-4.1
IV_SIZE = 192
def __init__(self, key_size):
self.name = 'XC20P'
self.description = 'XChaCha20-Poly1305'
self.key_size = key_size
self.CEK_SIZE = key_size
def encrypt(self, msg, aad, iv, key):
"""Content Encryption with AEAD_XCHACHA20_POLY1305
:param msg: text to be encrypt in bytes
:param aad: additional authenticated data in bytes
:param iv: initialization vector in bytes
:param key: encrypted key in bytes
:return: (ciphertext, tag)
"""
self.check_iv(iv)
chacha = Cryptodome_ChaCha20_Poly1305.new(key=key, nonce=iv)
chacha.update(aad)
ciphertext, tag = chacha.encrypt_and_digest(msg)
return ciphertext, tag
def decrypt(self, ciphertext, aad, iv, tag, key):
"""Content Decryption with AEAD_XCHACHA20_POLY1305
:param ciphertext: ciphertext in bytes
:param aad: additional authenticated data in bytes
:param iv: initialization vector in bytes
:param tag: authentication tag in bytes
:param key: encrypted key in bytes
:return: message
"""
self.check_iv(iv)
chacha = Cryptodome_ChaCha20_Poly1305.new(key=key, nonce=iv)
chacha.update(aad)
return chacha.decrypt_and_verify(ciphertext, tag)

View File

@@ -0,0 +1,50 @@
"""
authlib.jose.draft
~~~~~~~~~~~~~~~~~~~~
Content Encryption per `Section 4`_.
.. _`Section 4`: https://datatracker.ietf.org/doc/html/draft-amringer-jose-chacha-02#section-4
"""
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
from authlib.jose.rfc7516 import JWEEncAlgorithm
class C20PEncAlgorithm(JWEEncAlgorithm):
# Use of an IV of size 96 bits is REQUIRED with this algorithm.
# https://datatracker.ietf.org/doc/html/draft-amringer-jose-chacha-02#section-4.1
IV_SIZE = 96
def __init__(self, key_size):
self.name = 'C20P'
self.description = 'ChaCha20-Poly1305'
self.key_size = key_size
self.CEK_SIZE = key_size
def encrypt(self, msg, aad, iv, key):
"""Content Encryption with AEAD_CHACHA20_POLY1305
:param msg: text to be encrypt in bytes
:param aad: additional authenticated data in bytes
:param iv: initialization vector in bytes
:param key: encrypted key in bytes
:return: (ciphertext, tag)
"""
self.check_iv(iv)
chacha = ChaCha20Poly1305(key)
ciphertext = chacha.encrypt(iv, msg, aad)
return ciphertext[:-16], ciphertext[-16:]
def decrypt(self, ciphertext, aad, iv, tag, key):
"""Content Decryption with AEAD_CHACHA20_POLY1305
:param ciphertext: ciphertext in bytes
:param aad: additional authenticated data in bytes
:param iv: initialization vector in bytes
:param tag: authentication tag in bytes
:param key: encrypted key in bytes
:return: message
"""
self.check_iv(iv)
chacha = ChaCha20Poly1305(key)
return chacha.decrypt(iv, ciphertext + tag, aad)