-
Notifications
You must be signed in to change notification settings - Fork 0
/
prf.py
125 lines (95 loc) · 4.67 KB
/
prf.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
"""
This file implements the TLS Pseudo-Random Function as specified by Section 5
of RFC 5246 (https://tools.ietf.org/html/rfc5246#section-5). The section
states:
.. code-block:: none
The TLS record layer uses a keyed Message Authentication Code (MAC) to
protect message integrity. The cipher suites defined in this document use
a construction known as HMAC, described in [HMAC], which is based on a hash
function. Other cipher suites MAY define their own MAC constructions, if
needed.
In addition, a construction is required to do expansion of secrets into
blocks of data for the purposes of key generation or validation. This
pseudorandom function (PRF) takes as input a secret, a seed, and an
identifying label and produces an output of arbitrary length.
In this section, we define one PRF, based on HMAC. This PRF with the
SHA-256 hash function is used for all cipher suites defined in this
document and in TLS documents published prior to this document when TLS 1.2
is negotiated. New cipher suites MUST explicitly specify a PRF and, in
general, SHOULD use the TLS PRF with SHA-256 or a stronger standard hash
function.
First, we define a data expansion function, P_hash(secret, data), that uses
a single hash function to expand a secret and seed into an arbitrary
quantity of output:
P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
HMAC_hash(secret, A(2) + seed) + HMAC_hash(secret, A(3) + seed) + ...
where + indicates concatenation.
A() is defined as:
A(0) = seed A(i) = HMAC_hash(secret, A(i-1))
P_hash can be iterated as many times as necessary to produce the required
quantity of data. For example, if P_SHA256 is being used to create 80
bytes of data, it will have to be iterated three times (through A(3)),
creating 96 bytes of output data; the last 16 bytes of the final iteration
will then be discarded, leaving 80 bytes of output data.
TLS's PRF is created by applying P_hash to the secret as:
PRF(secret, label, seed) = P_<hash>(secret, label + seed)
The label is an ASCII string. It should be included in the exact form it
is given without a length byte or trailing null character. For example,
the label "slithy toves" would be processed by hashing the following bytes:
73 6C 69 74 68 79 20 74 6F 76 65 73
"""
from __future__ import absolute_import, division, print_function
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.hmac import HMAC
def _p_hash(hash_algorithm, secret, seed, output_length):
"""
A seed expansion function that uses a single hash function to expand a
secret and seed into the number of bytes specified by output_length.
"""
result = bytearray()
i = 1
while len(result) < output_length:
h = HMAC(secret, hash_algorithm, default_backend())
h.update(_a(secret, hash_algorithm, i, seed))
h.update(seed)
result.extend(h.finalize())
i += 1
return bytes(result[:output_length])
def _a(secret, hash_algorithm, n, seed):
"""
a() is defined as:
a(0) = seed
a(i) = HMAC_hash(secret, A(i-1))
"""
if n == 0:
return seed
else:
h = HMAC(secret, hash_algorithm, default_backend())
h.update(_a(secret, hash_algorithm, n - 1, seed))
return h.finalize()
def prf(secret, label, seed, hash_algorithm, output_length):
"""
A construction to expand secrets into blocks of data for the purposes of
key generation or validation.
This pseudo-random function (PRF) takes as input a secret, a seed, an
identifying label and a hash algorithm and produces an output of length
specified in output_length.
:param secret: Secret key as ``bytes``. The key should be randomly
generated bytes and is recommended to be equal in length to the
digest_size of the hash function chosen. You must keep the key secret.
:type secret: :py:class:`bytes`
:param label: An ASCII string.
:type label: :py:class:`bytes`
:param seed: The seed as ``bytes``.
:type label: :py:class:`bytes`
:param hash_algorithm: The hash algorithm to use with HMAC.
:type hash_algorithm: a
:py:class:`cryptography.hazmat.primitives.hashes.HashAlgorithm`
provider.
:param output_length: The number of bytes to expand the seed into.
:type output_length: :py:class:`int`
"""
return _p_hash(hash_algorithm, secret, label + seed, output_length)