Add documentation for Contrac and Dtk classes.
[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. It's
15 * largely internal. The functionality from \ref Contrac should generally be
16 * used instead of these functions.
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 * Create 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 * Delete 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 * Generate a random Daily Tracing Key.
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 * @return true if the operation completed successfully, false otherwise.
111 */
112 bool dtk_generate_daily_key(Dtk * data, Contrac const * contrac, uint32_t day_number) {
113 int result = 1;
114 char encode[sizeof(DTK_INFO_PREFIX) + sizeof(day_number)];
115 size_t out_length = 0;
116 EVP_PKEY_CTX *pctx = NULL;
117 const unsigned char * tk;
118
119 // dtk_i <- HKDF(tk, NULL, (UTF8("CT-DTK") || D_i), 16)
120
121 if (result > 0) {
122 // Produce Info sequence UTF8("CT-DTK") || D_i)
123 // From the spec it's not clear whether this is string or byte concatenation.
124 // Here we use byte, but it might have to be changed
125 memcpy(encode, DTK_INFO_PREFIX, sizeof(DTK_INFO_PREFIX));
126 ((uint32_t *)(encode + sizeof(DTK_INFO_PREFIX)))[0] = day_number;
127
128 pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
129
130 result = EVP_PKEY_derive_init(pctx);
131 }
132
133 if (result > 0) {
134 result = EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256());
135 }
136
137 if (result > 0) {
138 result = EVP_PKEY_CTX_set1_hkdf_salt(pctx, NULL, 4);
139 }
140
141 if (result > 0) {
142 tk = contrac_get_tracing_key(contrac);
143 result = EVP_PKEY_CTX_set1_hkdf_key(pctx, tk, TK_SIZE);
144 }
145
146 if (result > 0) {
147 result = EVP_PKEY_CTX_add1_hkdf_info(pctx, encode, sizeof(encode));
148 }
149
150 if (result > 0) {
151 out_length = DTK_SIZE;
152 result = EVP_PKEY_derive(pctx, data->dtk, &out_length);
153 }
154
155 if ((result > 0) && (out_length == DTK_SIZE)) {
156 data->day_number = day_number;
157 result = 1;
158 }
159
160 if (result <= 0) {
161 LOG(LOG_ERR, "Error generating daily key: %lu\n", ERR_get_error());
162 }
163
164 // Freeing a NULL value is safe
165 EVP_PKEY_CTX_free(pctx);
166
167 return (result > 0);
168 }
169
170 const unsigned char * dtk_get_daily_key(Dtk const * data) {
171 return data->dtk;
172 }
173
174 uint32_t dtk_get_day_number(Dtk const * data) {
175 return data->day_number;
176 }
177
178 void dtk_assign(Dtk * data, unsigned char const * dtk_bytes, uint32_t day_number) {
179 memcpy(data->dtk, dtk_bytes, DTK_SIZE);
180 data->day_number = day_number;
181 }
182
183 /** @} addtogroup DailyTracingKey */
184