1# This file is part of Scapy
2# Copyright (C) 2017 Maxence Tury
3# This program is published under a GPLv2 license
4
5"""
6Stateless HKDF for TLS 1.3.
7"""
8
9import struct
10
11from scapy.config import conf
12from scapy.layers.tls.crypto.pkcs1 import _get_hash
13
14if conf.crypto_valid:
15    from cryptography.hazmat.backends import default_backend
16    from cryptography.hazmat.primitives.kdf.hkdf import HKDF, HKDFExpand
17    from cryptography.hazmat.primitives.hashes import Hash
18    from cryptography.hazmat.primitives.hmac import HMAC
19
20
21class TLS13_HKDF(object):
22    def __init__(self, hash_name="sha256"):
23        self.hash = _get_hash(hash_name)
24
25    def extract(self, salt, ikm):
26        h = self.hash
27        hkdf = HKDF(h, h.digest_size, salt, None, default_backend())
28        if ikm is None:
29            ikm = b"\x00" * h.digest_size
30        return hkdf._extract(ikm)
31
32    def expand(self, prk, info, L):
33        h = self.hash
34        hkdf = HKDFExpand(h, L, info, default_backend())
35        return hkdf.derive(prk)
36
37    def expand_label(self, secret, label, hash_value, length):
38        hkdf_label = struct.pack("!H", length)
39        hkdf_label += struct.pack("B", 6 + len(label))
40        hkdf_label += b"tls13 "
41        hkdf_label += label
42        hkdf_label += struct.pack("B", len(hash_value))
43        hkdf_label += hash_value
44        return self.expand(secret, hkdf_label, length)
45
46    def derive_secret(self, secret, label, messages):
47        h = Hash(self.hash, backend=default_backend())
48        h.update(messages)
49        hash_messages = h.finalize()
50        hash_len = self.hash.digest_size
51        return self.expand_label(secret, label, hash_messages, hash_len)
52
53    def compute_verify_data(self, basekey, handshake_context):
54        hash_len = self.hash.digest_size
55        finished_key = self.expand_label(basekey, b"finished", b"", hash_len)
56
57        h = Hash(self.hash, backend=default_backend())
58        h.update(handshake_context)
59        hash_value = h.finalize()
60
61        hm = HMAC(finished_key, self.hash, default_backend())
62        hm.update(hash_value)
63        return hm.finalize()
64