Add remaining documentation
[libcontrac.git] / src / rpi.c
1 /** \ingroup RandomProximityIdentifier
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 Random Proximity Identifier functionality
12 * @section DESCRIPTION
13 *
14 * This class is used to generate and manage the Random Proximity Identifier
15 * (RPI). It's largely internal. The functionality from \ref Contrac should
16 * generally be used in preference to this.
17 *
18 */
19
20 /** \addtogroup RandomProximityIdentifier
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/hmac.h>
33 #include <openssl/err.h>
34
35 #include "contrac/contrac.h"
36 #include "contrac/utils.h"
37 #include "contrac/log.h"
38
39 #include "contrac/rpi.h"
40
41 // Defines
42
43 /**
44 * Used internally.
45 *
46 * This is the prefix for the Info parameter provided to the HMAC and used to
47 * generate the RPI.
48 */
49 #define RPI_INFO_PREFIX "CT-RPI"
50
51 // Structures
52
53 /**
54 * @brief The structure used to represent a Rolling Proximity Identifier.
55 *
56 * This is an opaque structure that contains information about the RPI..
57 *
58 * This must be passed as the first parameter of every non-static function.
59 *
60 * The structure typedef is in dtk.h
61 */
62 struct _Rpi {
63 // Rolling proximity identifier
64 unsigned char rpi[RPI_SIZE];
65 uint8_t time_interval_number;
66 };
67
68 // Function prototypes
69
70 // Function definitions
71
72 /**
73 * Creates a new instance of the class.
74 *
75 * @return The newly created object.
76 */
77 Rpi * rpi_new() {
78 Rpi * data;
79
80 data = calloc(sizeof(Rpi), 1);
81
82 return data;
83 }
84
85 /**
86 * Deletes an instance of the class, freeing up the memory allocated to it.
87 *
88 * @param data The instance to free.
89 */
90 void rpi_delete(Rpi * data) {
91 if (data) {
92 // Clear the data for security
93 memset(data, 0, sizeof(Rpi));
94
95 free(data);
96 }
97 }
98
99 /**
100 * Generates a Rolling Proximity Identifier based on the time interval number
101 * provided.
102 *
103 * The operation may fail under certain circumstances, such as if the
104 * HMAC operation fails for some reason.
105 *
106 * For internal use. It generally makes more sense to use the
107 * contrac_set_time_interval_number() function instead.
108 *
109 * @param data The context object to work with.
110 * @param time_interval_number The time interval number to use to generate the
111 * key.
112 * @return true if the operation completed successfully, false otherwise.
113 */
114 bool rpi_generate_proximity_id(Rpi * data, Dtk const * dtk, uint8_t time_interval_number) {
115 int result = 1;
116 unsigned char encode[sizeof(RPI_INFO_PREFIX) + sizeof(time_interval_number)];
117 unsigned char output[EVP_MAX_MD_SIZE];
118 unsigned int out_length = 0;
119 unsigned int pos;
120 unsigned int min;
121 unsigned char const * daily_key;
122
123 // RPI_{i, j} <- Truncate(HMAC(dkt_i, (UTF8("CT-RPI") || TIN_j)), 16)
124
125 if (result > 0) {
126 // Produce Info sequence UTF8("CT-DTK") || D_i)
127 // From the spec it's not clear whether this is string or byte concatenation.
128 // Here we use byte, but it might have to be changed
129 memcpy(encode, RPI_INFO_PREFIX, sizeof(RPI_INFO_PREFIX));
130 ((uint8_t *)(encode + sizeof(RPI_INFO_PREFIX)))[0] = time_interval_number;
131 out_length = sizeof(output);
132
133 daily_key = dtk_get_daily_key(dtk);
134 HMAC(EVP_sha256(), daily_key, DTK_SIZE, encode, sizeof(encode), output, &out_length);
135
136 _Static_assert ((EVP_MAX_MD_SIZE >= 16), "HMAC buffer size too small");
137
138 // Truncate and copy the result
139 min = MIN(out_length, 16);
140 for (pos = 0; pos < min; ++pos) {
141 data->rpi[pos] = output[pos];
142 }
143 // Zero out padding if there is any
144 for (pos = min; pos < 16; ++pos) {
145 data->rpi[pos] = 0;
146 }
147 }
148
149 if (result > 0) {
150 data->time_interval_number = time_interval_number;
151 }
152
153 if (result <= 0) {
154 LOG(LOG_ERR, "Error generating rolling proximity id: %lu\n", ERR_get_error());
155 }
156
157 return (result > 0);
158 }
159
160 /**
161 * Gets the Rolling Proximity Identifier for the device in binary format.
162 *
163 * For internal use. It generally makes more sense to use the
164 * contrac_get_proximity_id() function instead.
165 *
166 * This allows the Rolling Proximity Identifier to be extracted. The Rolling
167 * Proximity Identifier is public, in the sense that it is usual to broadcast
168 * the value in Bluetooth beacons.
169 *
170 * The buffer returned will contain exactly RPI_SIZE (16) bytes of data in
171 * binary format. This may therefore contain null bytes, and the buffer will not
172 * necessarily be null terminated. Future operations may cause the data to
173 * change, so the caller should make a copy of the buffer rather than keeping
174 * a pointer to it.
175 *
176 * @param data The context object to work with.
177 * @return The Rolling Proximity Identifier in binary format, not null
178 * terminated.
179 */
180 unsigned char const * rpi_get_proximity_id(Rpi const * data) {
181 return data->rpi;
182 }
183
184 /**
185 * Gets the time interval number that applies to the current RPI.
186 *
187 * For internal use. It generally makes more sense to use the
188 * contrac_set_time_interval_number() function instead.
189 *
190 * @param data The context object to work with.
191 * @return The time interval number stored in the object.
192 */
193 uint8_t rpi_get_time_interval_number(Rpi const * data) {
194 return data->time_interval_number;
195 }
196
197 /**
198 * Populates the data structure.
199 *
200 * Allows the RPI and time interval number values of the object to be set
201 * explicitly.
202 *
203 * For internal use. To set the RPI it generally makes more sense to use one of
204 * eiher contrac_set_time_interval_number() or contrac_update_current_time()
205 * instead.
206 *
207 * The rpi_bytes buffer passed in must contain exactly RPI_SIZE (16) bytes of
208 * data. It doen't have to be null terminated.
209 *
210 * @param data The context object to work with.
211 * @param rpi_bytes The RPI value to set, in binary format.
212 * @param day_number The time interval number to associate with this RPI.
213 */
214 void rpi_assign(Rpi * data, unsigned char const * rpi_bytes, uint8_t time_interval_number) {
215 memcpy(data->rpi, rpi_bytes, RPI_SIZE);
216 data->time_interval_number = time_interval_number;
217 }
218
219 /**
220 * Compares two RPI values.
221 *
222 * @param data The RPI to compare with.
223 * @param comparitor The RPI to compare against.
224 * @return true if the two RPIs are the same, false otherwise.
225 */
226 bool rpi_compare(Rpi const * data, Rpi const * comparitor) {
227 unsigned char left[RPI_SIZE_BASE64 + 1];
228 unsigned char right[RPI_SIZE_BASE64 + 1];
229 size_t size;
230
231 size = RPI_SIZE_BASE64 + 1;
232 base64_encode_binary_to_base64(data->rpi, RPI_SIZE, left, &size);
233
234 size = RPI_SIZE_BASE64 + 1;
235 base64_encode_binary_to_base64(comparitor->rpi, RPI_SIZE, right, &size);
236
237 return (memcmp(data, comparitor, RPI_SIZE) == 0);
238 }
239
240 /** @} addtogroup RandomProximityIdentifier */
241