Add remaining documentation
[libcontrac.git] / src / dtk.c
1 /** \ingroup DailyTracingKey
2 * @file
3 * @author David Llewellyn-Jones <david@flypig.co.uk>
4 * @version $(VERSION)
5 *
6 * @section LICENSE
7 *
8 * Copyright David Llewellyn-Jones, 2020
9 * Released under the GPLv2.
10 *
11 * @brief Daily Tracing Key functionality
12 * @section DESCRIPTION
13 *
14 * This class is used to generate and manage the Daily Tracing Key (DTK). It's
15 * largely internal. The functionality from \ref Contrac should generally be
16 * used in preference to this.
17 *
18 */
19
20 /** \addtogroup DailyTracingKey
21 * @{
22 */
23
24 // Includes
25
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stddef.h>
29 #include <stdint.h>
30
31 #include <openssl/crypto.h>
32 #include <openssl/kdf.h>
33 #include <openssl/err.h>
34 #include <openssl/evp.h>
35
36 #include "contrac/contrac.h"
37 #include "contrac/utils.h"
38 #include "contrac/log.h"
39
40 #include "contrac/dtk.h"
41
42 // Defines
43
44 /**
45 * Used internally.
46 *
47 * This is the prefix for the Info parameter provided to the HKDF and used to
48 * generate the DTK.
49 */
50 #define DTK_INFO_PREFIX "CT-DTK"
51
52 // Structures
53
54 /**
55 * @brief The structure used to represent a Daily Tracing Key.
56 *
57 * This is an opaque structure that contains information about the DTK..
58 *
59 * This must be passed as the first parameter of every non-static function.
60 *
61 * The structure typedef is in dtk.h
62 */
63 struct _Dtk {
64 // Daily key
65 unsigned char dtk[DTK_SIZE];
66 uint32_t day_number;
67 };
68
69 // Function prototypes
70
71 // Function definitions
72
73 /**
74 * Creates a new instance of the class.
75 *
76 * @return The newly created object.
77 */
78 Dtk * dtk_new() {
79 Dtk * data;
80
81 data = calloc(sizeof(Dtk), 1);
82
83 return data;
84 }
85
86 /**
87 * Deletes an instance of the class, freeing up the memory allocated to it.
88 *
89 * @param data The instance to free.
90 */
91 void dtk_delete(Dtk * data) {
92 if (data) {
93 // Clear the data for security
94 memset(data, 0, sizeof(Dtk));
95
96 free(data);
97 }
98 }
99
100 /**
101 * Generates a Daily Tracing Key based on the day number provided.
102 *
103 * The operation may fail under certain circumstances, such as if the
104 * HKDF operation fails for some reason.
105 *
106 * For internal use. It generally makes more sense to use the
107 * contrac_set_day_number() function instead.
108 *
109 * @param data The context object to work with.
110 * @param day_number The day number to use to generate the key.
111 * @return true if the operation completed successfully, false otherwise.
112 */
113 bool dtk_generate_daily_key(Dtk * data, Contrac const * contrac, uint32_t day_number) {
114 int result = 1;
115 char encode[sizeof(DTK_INFO_PREFIX) + sizeof(day_number)];
116 size_t out_length = 0;
117 EVP_PKEY_CTX *pctx = NULL;
118 unsigned char const * tk;
119
120 // dtk_i <- HKDF(tk, NULL, (UTF8("CT-DTK") || D_i), 16)
121
122 if (result > 0) {
123 // Produce Info sequence UTF8("CT-DTK") || D_i)
124 // From the spec it's not clear whether this is string or byte concatenation.
125 // Here we use byte, but it might have to be changed
126 memcpy(encode, DTK_INFO_PREFIX, sizeof(DTK_INFO_PREFIX));
127 ((uint32_t *)(encode + sizeof(DTK_INFO_PREFIX)))[0] = day_number;
128
129 pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
130
131 result = EVP_PKEY_derive_init(pctx);
132 }
133
134 if (result > 0) {
135 result = EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256());
136 }
137
138 if (result > 0) {
139 result = EVP_PKEY_CTX_set1_hkdf_salt(pctx, NULL, 4);
140 }
141
142 if (result > 0) {
143 tk = contrac_get_tracing_key(contrac);
144 result = EVP_PKEY_CTX_set1_hkdf_key(pctx, tk, TK_SIZE);
145 }
146
147 if (result > 0) {
148 result = EVP_PKEY_CTX_add1_hkdf_info(pctx, encode, sizeof(encode));
149 }
150
151 if (result > 0) {
152 out_length = DTK_SIZE;
153 result = EVP_PKEY_derive(pctx, data->dtk, &out_length);
154 }
155
156 if ((result > 0) && (out_length == DTK_SIZE)) {
157 data->day_number = day_number;
158 result = 1;
159 }
160
161 if (result <= 0) {
162 LOG(LOG_ERR, "Error generating daily key: %lu\n", ERR_get_error());
163 }
164
165 // Freeing a NULL value is safe
166 EVP_PKEY_CTX_free(pctx);
167
168 return (result > 0);
169 }
170
171 /**
172 * Gets the Daily Tracing Key for the device in binary format.
173 *
174 * For internal use. It generally makes more sense to use the
175 * contrac_get_daily_key() function instead.
176 *
177 * This allows the Daily Tracing Key to be extracted. The Daily Tracing Key
178 * should be kept secret (to maintain privacy) until a positive test is
179 * confirmed, at which point the user may choose to upload the key to a
180 * Diagnosis Server, so that others can be notified.
181 *
182 * The buffer returned will contain exactly DTK_SIZE (16) bytes of data in
183 * binary format. This may therefore contain null bytes, and the buffer will not
184 * necessarily be null terminated. Future operations may cause the data to
185 * change, so the caller should make a copy of the buffer rather than keeping
186 * a pointer to it.
187 *
188 * @param data The context object to work with.
189 * @return The Daily Tracing Key in binary format, not null terminated.
190 */
191 unsigned char const * dtk_get_daily_key(Dtk const * data) {
192 return data->dtk;
193 }
194
195 /**
196 * Gets the day number that applies to the current DTK.
197 *
198 * For internal use. It generally makes more sense to use the
199 * contrac_get_day_number() function instead.
200 *
201 * @param data The context object to work with.
202 * @return The day number stored in the object..
203 */
204 uint32_t dtk_get_day_number(Dtk const * data) {
205 return data->day_number;
206 }
207
208 /**
209 * Populates the data structure.
210 *
211 * Allows the DTK and day number values of the object to be set explicitly.
212 *
213 * For internal use. To set the DTK it generally makes more sense to use one of
214 * eiher contrac_set_day_number() or contrac_update_current_time() instead.
215 *
216 * The dtk_bytes buffer passed in must contain exactly DTK_SIZE (16) bytes of
217 * data. It doen't have to be null terminated.
218 *
219 * @param data The context object to work with.
220 * @param dtk_bytes The DTK value to set, in binary format.
221 * @param day_number The day number to associate with this DTK.
222 */
223 void dtk_assign(Dtk * data, unsigned char const * dtk_bytes, uint32_t day_number) {
224 memcpy(data->dtk, dtk_bytes, DTK_SIZE);
225 data->day_number = day_number;
226 }
227
228 /** @} addtogroup DailyTracingKey */
229