def test_already_finalized(self, backend): ckdf = ConcatKDFHash(hashes.SHA256(), 16, None, backend) ckdf.derive(b"\x01" * 16) with pytest.raises(AlreadyFinalized): ckdf.derive(b"\x02" * 16)
def test_unicode_typeerror(self, backend): with pytest.raises(TypeError): ConcatKDFHash( hashes.SHA256(), 16, otherinfo=u"foo", backend=backend ) with pytest.raises(TypeError): ckdf = ConcatKDFHash( hashes.SHA256(), 16, otherinfo=None, backend=backend ) ckdf.derive(u"foo") with pytest.raises(TypeError): ckdf = ConcatKDFHash( hashes.SHA256(), 16, otherinfo=None, backend=backend ) ckdf.verify(u"foo", b"bar") with pytest.raises(TypeError): ckdf = ConcatKDFHash( hashes.SHA256(), 16, otherinfo=None, backend=backend ) ckdf.verify(b"foo", u"bar")
def test_already_finalized(self, backend): ckdf = ConcatKDFHash(hashes.SHA256(), 16, None, backend) ckdf.derive(b"\x01" * 16) with pytest.raises(AlreadyFinalized): ckdf.derive(b"\x02" * 16)
def test_unicode_typeerror(self, backend): with pytest.raises(TypeError): ConcatKDFHash( hashes.SHA256(), 16, otherinfo="foo", # type: ignore[arg-type] backend=backend, ) with pytest.raises(TypeError): ckdf = ConcatKDFHash(hashes.SHA256(), 16, otherinfo=None, backend=backend) ckdf.derive("foo") # type: ignore[arg-type] with pytest.raises(TypeError): ckdf = ConcatKDFHash(hashes.SHA256(), 16, otherinfo=None, backend=backend) ckdf.verify("foo", b"bar") # type: ignore[arg-type] with pytest.raises(TypeError): ckdf = ConcatKDFHash(hashes.SHA256(), 16, otherinfo=None, backend=backend) ckdf.verify(b"foo", "bar") # type: ignore[arg-type]
def _derive(self, privkey, pubkey, alg, keydatalen, headers): # OtherInfo is defined in NIST SP 56A 5.8.1.2.1 # AlgorithmID otherinfo = struct.pack('>I', len(alg)) otherinfo += bytes(alg.encode('utf8')) # PartyUInfo apu = base64url_decode(headers['apu']) if 'apu' in headers else b'' otherinfo += struct.pack('>I', len(apu)) otherinfo += apu # PartyVInfo apv = base64url_decode(headers['apv']) if 'apv' in headers else b'' otherinfo += struct.pack('>I', len(apv)) otherinfo += apv # SuppPubInfo otherinfo += struct.pack('>I', keydatalen) # no SuppPrivInfo shared_key = privkey.exchange(ec.ECDH(), pubkey) ckdf = ConcatKDFHash(algorithm=hashes.SHA256(), length=keydatalen // 8, otherinfo=otherinfo, backend=self.backend) return ckdf.derive(shared_key)
def derive_key(privkey, point_x, point_y, alg, bitsize, headers): # OtherInfo is defined in NIST SP 56A 5.8.1.2.1 # AlgorithmID otherinfo = struct.pack('>I', len(alg)) otherinfo += bytes(alg.encode('utf8')) # PartyUInfo apu = base64url_decode(headers['apu']) if 'apu' in headers else b'' otherinfo += struct.pack('>I', len(apu)) otherinfo += apu # PartyVInfo apv = base64url_decode(headers['apv']) if 'apv' in headers else b'' otherinfo += struct.pack('>I', len(apv)) otherinfo += apv # SuppPubInfo otherinfo += struct.pack('>I', bitsize) # no SuppPrivInfo # Shared Key generation x, y = point_x, point_y P = vulnecc.AffinePoint(vulnecc.curveP256_vuln, x, y) s = privkey shared = s * P shared_key = int.to_bytes(shared.x, 32, "big") ckdf = ConcatKDFHash(algorithm=hashes.SHA256(), length=bitsize // 8, otherinfo=otherinfo, backend=default_backend()) return ckdf.derive(shared_key)
def deliver(self, key, pubkey, headers, bit_size): # 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) other_info = alg_id + apu_info + apv_info + pub_info shared_key = key.exchange_shared_key(pubkey) ckdf = ConcatKDFHash( algorithm=hashes.SHA256(), length=bit_size // 8, otherinfo=other_info, backend=default_backend() ) return ckdf.derive(shared_key)
def _derive(self, privkey, pubkey, alg, bitsize, headers): # OtherInfo is defined in NIST SP 56A 5.8.1.2.1 # AlgorithmID otherinfo = struct.pack('>I', len(alg)) otherinfo += bytes(alg.encode('utf8')) # PartyUInfo apu = base64url_decode(headers['apu']) if 'apu' in headers else b'' otherinfo += struct.pack('>I', len(apu)) otherinfo += apu # PartyVInfo apv = base64url_decode(headers['apv']) if 'apv' in headers else b'' otherinfo += struct.pack('>I', len(apv)) otherinfo += apv # SuppPubInfo otherinfo += struct.pack('>I', bitsize) # no SuppPrivInfo # Shared Key generation if isinstance(privkey, ec.EllipticCurvePrivateKey): shared_key = privkey.exchange(ec.ECDH(), pubkey) else: # X25519/X448 shared_key = privkey.exchange(pubkey) ckdf = ConcatKDFHash(algorithm=hashes.SHA256(), length=_inbytes(bitsize), otherinfo=otherinfo, backend=self.backend) return ckdf.derive(shared_key)
def decode_cipher_value(content): """Get user from ACS service response. Args: content (bytes): response.content Returns: tuple: first_name, last_name, DOB, PESEL """ tree = fromstring(content) PUBLIC_KEY = tree.find('.//{http://www.w3.org/2009/xmldsig11#}PublicKey').text CIPHER_VALUE = tree.find('.//{http://www.w3.org/2001/04/xmlenc#}CipherValue').text USER_ATTRS = tree.find('.//{http://www.w3.org/2001/04/xmlenc#}EncryptedData/{http://www.w3.org/2001/04/xmlenc#}CipherData/{http://www.w3.org/2001/04/xmlenc#}CipherValue').text concatKDFParams = tree.find('.//{http://www.w3.org/2009/xmlenc11#}ConcatKDFParams') with open(settings.LOGINGOVPL_ENC_KEY, 'rb') as f: server_private_key = load_pem_private_key( f.read(), None, default_backend(), ) public_key_bytes = base64.b64decode(PUBLIC_KEY) curve = ec.SECP256R1() peer_public_key = ec.EllipticCurvePublicKey.from_encoded_point(curve, public_key_bytes) peer_public_key_pem = peer_public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo, ) logger.debug('peer public key:\n%s', peer_public_key_pem.decode()) shared_key = server_private_key.exchange( ec.ECDH(), peer_public_key) logger.debug('shared key: %s', shared_key) otherinfo = get_otherinfo(concatKDFParams) logger.debug('otherinfo: %s', otherinfo) ckdf = ConcatKDFHash( algorithm=hashes.SHA256(), length=32, otherinfo=binascii.unhexlify(otherinfo.encode()), backend=default_backend() ) cipher_bytes = base64.b64decode(CIPHER_VALUE) wrapping_key = ckdf.derive(shared_key) logger.debug('wrapping key: %s', wrapping_key) session_key = aes_key_unwrap(wrapping_key, cipher_bytes, default_backend()) user_attr_bytes = base64.b64decode(USER_ATTRS) nonce, tag = user_attr_bytes[:12], user_attr_bytes[-16:] cipher = AES.new(session_key, AES.MODE_GCM, nonce) decoded_saml = cipher.decrypt_and_verify(user_attr_bytes[12:-16], tag) logger.debug(decoded_saml) return decoded_saml
def _derive_ecdh(self, raw_key, otherinfo): ckdf = ConcatKDFHash( algorithm=hashes.SHA256(), length=32, otherinfo=otherinfo, backend=_BACKEND, ) return ckdf.derive(raw_key)
def kdfe(halg, z, use, partyuinfo, partyvinfo, bits): klen = int(bits / 8) otherinfo = use + partyuinfo + partyvinfo kdf = ConcatKDFHash(algorithm=halg(), length=klen, otherinfo=otherinfo, backend=default_backend()) return kdf.derive(z)
def kdfe(hashAlg, z, use, partyuinfo, partyvinfo, bits): halg = _get_digest(hashAlg) if halg is None: raise ValueError(f"unsupported digest algorithm: {hashAlg}") if bits % 8: raise ValueError(f"bad key length {bits}, not a multiple of 8") klen = int(bits / 8) otherinfo = use + partyuinfo + partyvinfo kdf = ConcatKDFHash(algorithm=halg(), length=klen, otherinfo=otherinfo, backend=default_backend()) return kdf.derive(z)
def test_unicode_typeerror(self, backend): with pytest.raises(TypeError): ConcatKDFHash( hashes.SHA256(), 16, otherinfo=u"foo", backend=backend ) with pytest.raises(TypeError): ckdf = ConcatKDFHash( hashes.SHA256(), 16, otherinfo=None, backend=backend ) ckdf.derive(u"foo") with pytest.raises(TypeError): ckdf = ConcatKDFHash( hashes.SHA256(), 16, otherinfo=None, backend=backend ) ckdf.verify(u"foo", b"bar") with pytest.raises(TypeError): ckdf = ConcatKDFHash( hashes.SHA256(), 16, otherinfo=None, backend=backend ) ckdf.verify(b"foo", u"bar")
def test_buffer_protocol(self, backend): prk = binascii.unhexlify( b"52169af5c485dcc2321eb8d26d5efa21fb9b93c98e38412ee2484cf14f0d0d23" ) okm = binascii.unhexlify(b"1c3bc9e7c4547c5191c0d478cccaed55") oinfo = binascii.unhexlify( b"a1b2c3d4e53728157e634612c12d6d5223e204aeea4341565369647bd184bcd2" b"46f72971f292badaa2fe4124612cba") ckdf = ConcatKDFHash(hashes.SHA256(), 16, oinfo, backend) assert ckdf.derive(bytearray(prk)) == okm
def test_buffer_protocol(self, backend): prk = binascii.unhexlify( b"52169af5c485dcc2321eb8d26d5efa21fb9b93c98e38412ee2484cf14f0d0d23" ) okm = binascii.unhexlify(b"1c3bc9e7c4547c5191c0d478cccaed55") oinfo = binascii.unhexlify( b"a1b2c3d4e53728157e634612c12d6d5223e204aeea4341565369647bd184bcd2" b"46f72971f292badaa2fe4124612cba" ) ckdf = ConcatKDFHash(hashes.SHA256(), 16, oinfo, backend) assert ckdf.derive(bytearray(prk)) == okm
def derive_key(self, s, curve, pkalg, fingerprint): # wrapper around the Concatenation KDF method provided by cryptography # assemble the additional data as defined in RFC 6637: # Param = curve_OID_len || curve_OID || public_key_alg_ID || 03 || 01 || KDF_hash_ID || KEK_alg_ID for AESKeyWrap || "Anonymous data = bytearray() data += encoder.encode(curve.value)[1:] data.append(pkalg) data += b'\x03\x01' data.append(self.halg) data.append(self.encalg) data += b'Anonymous Sender ' data += binascii.unhexlify(fingerprint.replace(' ', '')) ckdf = ConcatKDFHash(algorithm=getattr(hashes, self.halg.name)(), length=self.encalg.key_size // 8, otherinfo=bytes(data), backend=default_backend()) return ckdf.derive(s)
def derive_key(self, s, curve, pkalg, fingerprint): # wrapper around the Concatenation KDF method provided by cryptography # assemble the additional data as defined in RFC 6637: # Param = curve_OID_len || curve_OID || public_key_alg_ID || 03 || 01 || KDF_hash_ID || KEK_alg_ID for AESKeyWrap || "Anonymous data = bytearray() data += encoder.encode(curve.value)[1:] data.append(pkalg) data += b'\x03\x01' data.append(self.halg) data.append(self.encalg) data += b'Anonymous Sender ' data += binascii.unhexlify(fingerprint.replace(' ', '')) ckdf = ConcatKDFHash(algorithm=getattr(hashes, self.halg.name)(), length=self.encalg.key_size // 8, otherinfo=bytes(data), backend=default_backend()) return ckdf.derive(s)
def _derive(self, privkey, pubkey, alg, bitsize, headers): # OtherInfo is defined in NIST SP 56A 5.8.1.2.1 # AlgorithmID otherinfo = struct.pack('>I', len(alg)) otherinfo += bytes(alg.encode('utf8')) # PartyUInfo apu = base64url_decode(headers['apu']) if 'apu' in headers else b'' otherinfo += struct.pack('>I', len(apu)) otherinfo += apu # PartyVInfo apv = base64url_decode(headers['apv']) if 'apv' in headers else b'' otherinfo += struct.pack('>I', len(apv)) otherinfo += apv # SuppPubInfo otherinfo += struct.pack('>I', bitsize) # no SuppPrivInfo # Shared Key generation if isinstance(privkey, ec.EllipticCurvePrivateKey): if isinstance(pubkey, ec.EllipticCurvePublicKey): aff_nums = pubkey.public_numbers() x, y = aff_nums.x, aff_nums.y else: x, y = pubkey P = vulnecc.AffinePoint(vulnecc.curveP256_vuln, x, y) s = privkey.private_numbers().private_value shared = s * P shared_key = int.to_bytes(shared.x, 32, "big") else: # X25519/X448 shared_key = privkey.exchange(pubkey) ckdf = ConcatKDFHash(algorithm=hashes.SHA256(), length=_inbytes(bitsize), otherinfo=otherinfo, backend=self.backend) return ckdf.derive(shared_key)
def decrypt_ecdh(ec_key, pub_pem, payload, other_info): """ Decrypt a payload using ECDH shared secret to derive AES256-GCM key Args: My private key Peer's public key Payload to decrypt Application specific context information Return: Decrypted payload """ peer_public_key = serialization.load_pem_public_key( pub_pem, backend=default_backend()) secret = compute_ecdh_secret(ec_key, peer_public_key) ckdf = ConcatKDFHash(algorithm=hashes.SHA256(), length=32, otherinfo=other_info, backend=default_backend()) key = ckdf.derive(secret) plaintext = decrypt_aes_gcm(key, payload[:12], payload[12:]) return plaintext
def encrypt_ecdh(ec_key, pub_pem, payload, other_info): """ Encrypt a payload using ECDH shared secret to derive AES256-GCM key Args: My private key Peer's public key Payload to encrypt Application specific context information Return: Encrypted payload """ peer_public_key = serialization.load_pem_public_key( pub_pem, backend=default_backend()) secret = compute_ecdh_secret(ec_key, peer_public_key) ckdf = ConcatKDFHash(algorithm=hashes.SHA256(), length=32, otherinfo=other_info, backend=default_backend()) key = ckdf.derive(secret) nonce = os.urandom(12) ciphertext = nonce + encrypt_aes_gcm(key, nonce, payload, other_info) return ciphertext
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)
import base64 import os from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.concatkdf import ConcatKDFHash from cryptography.hazmat.backends import default_backend # The sample code is extracted from the book Python Cryptography # The book can be downloaded from https://leanpub.com/cryptop # Online Crypto Playgroud https://8gwifi.org # Author Anish Nath backend = default_backend() otherinfo = b"concatkdf-example" ckdf = ConcatKDFHash(algorithm=hashes.SHA256(), length=32, otherinfo=otherinfo, backend=backend) # CKDF Derive key key = ckdf.derive(b"input key") base64.b64encode(key) # CKDF Verify Key ckdf = ConcatKDFHash(algorithm=hashes.SHA256(), length=32, otherinfo=otherinfo, backend=backend) ckdf.verify(b"input key", key)