1 /** \ingroup DailyTracingKey
3 * @author David Llewellyn-Jones <david@flypig.co.uk>
8 * Copyright David Llewellyn-Jones, 2020
9 * Released under the GPLv2.
11 * @brief Daily Tracing Key functionality
12 * @section DESCRIPTION
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.
20 /** \addtogroup DailyTracingKey
31 #include <openssl/crypto.h>
32 #include <openssl/kdf.h>
33 #include <openssl/err.h>
34 #include <openssl/evp.h>
36 #include "contrac/contrac.h"
37 #include "contrac/utils.h"
38 #include "contrac/log.h"
40 #include "contrac/dtk.h"
47 * This is the prefix for the Info parameter provided to the HKDF and used to
50 #define DTK_INFO_PREFIX "CT-DTK"
55 * @brief The structure used to represent a Daily Tracing Key.
57 * This is an opaque structure that contains information about the DTK..
59 * This must be passed as the first parameter of every non-static function.
61 * The structure typedef is in dtk.h
65 unsigned char dtk
[DTK_SIZE
];
69 // Function prototypes
71 // Function definitions
74 * Creates a new instance of the class.
76 * @return The newly created object.
81 data
= calloc(sizeof(Dtk
), 1);
87 * Deletes an instance of the class, freeing up the memory allocated to it.
89 * @param data The instance to free.
91 void dtk_delete(Dtk
* data
) {
93 // Clear the data for security
94 memset(data
, 0, sizeof(Dtk
));
101 * Generates a Daily Tracing Key based on the day number provided.
103 * The operation may fail under certain circumstances, such as if the
104 * HKDF operation fails for some reason.
106 * For internal use. It generally makes more sense to use the
107 * contrac_set_day_number() function instead.
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.
113 bool dtk_generate_daily_key(Dtk
* data
, Contrac
const * contrac
, uint32_t day_number
) {
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
;
120 // dtk_i <- HKDF(tk, NULL, (UTF8("CT-DTK") || D_i), 16)
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
;
129 pctx
= EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF
, NULL
);
131 result
= EVP_PKEY_derive_init(pctx
);
135 result
= EVP_PKEY_CTX_set_hkdf_md(pctx
, EVP_sha256());
139 result
= EVP_PKEY_CTX_set1_hkdf_salt(pctx
, NULL
, 4);
143 tk
= contrac_get_tracing_key(contrac
);
144 result
= EVP_PKEY_CTX_set1_hkdf_key(pctx
, tk
, TK_SIZE
);
148 result
= EVP_PKEY_CTX_add1_hkdf_info(pctx
, encode
, sizeof(encode
));
152 out_length
= DTK_SIZE
;
153 result
= EVP_PKEY_derive(pctx
, data
->dtk
, &out_length
);
156 if ((result
> 0) && (out_length
== DTK_SIZE
)) {
157 data
->day_number
= day_number
;
162 LOG(LOG_ERR
, "Error generating daily key: %lu\n", ERR_get_error());
165 // Freeing a NULL value is safe
166 EVP_PKEY_CTX_free(pctx
);
172 * Gets the Daily Tracing Key for the device in binary format.
174 * For internal use. It generally makes more sense to use the
175 * contrac_get_daily_key() function instead.
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.
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
188 * @param data The context object to work with.
189 * @return The Daily Tracing Key in binary format, not null terminated.
191 unsigned char const * dtk_get_daily_key(Dtk
const * data
) {
196 * Gets the day number that applies to the current DTK.
198 * For internal use. It generally makes more sense to use the
199 * contrac_get_day_number() function instead.
201 * @param data The context object to work with.
202 * @return The day number stored in the object..
204 uint32_t dtk_get_day_number(Dtk
const * data
) {
205 return data
->day_number
;
209 * Populates the data structure.
211 * Allows the DTK and day number values of the object to be set explicitly.
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.
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.
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.
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
;
228 /** @} addtogroup DailyTracingKey */