1 /** \ingroup RandomProximityIdentifier
3 * @author David Llewellyn-Jones <david@flypig.co.uk>
8 * Copyright David Llewellyn-Jones, 2020
9 * Released under the GPLv2.
11 * @brief Random Proximity Identifier functionality
12 * @section DESCRIPTION
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.
20 /** \addtogroup RandomProximityIdentifier
31 #include <openssl/crypto.h>
32 #include <openssl/hmac.h>
33 #include <openssl/err.h>
35 #include "contrac/contrac.h"
36 #include "contrac/utils.h"
37 #include "contrac/log.h"
39 #include "contrac/rpi.h"
46 * This is the prefix for the Info parameter provided to the HMAC and used to
49 #define RPI_INFO_PREFIX "CT-RPI"
54 * @brief The structure used to represent a Rolling Proximity Identifier.
56 * This is an opaque structure that contains information about the RPI..
58 * This must be passed as the first parameter of every non-static function.
60 * The structure typedef is in dtk.h
63 // Rolling proximity identifier
64 unsigned char rpi
[RPI_SIZE
];
65 uint8_t time_interval_number
;
68 // Function prototypes
70 // Function definitions
73 * Creates a new instance of the class.
75 * @return The newly created object.
80 data
= calloc(sizeof(Rpi
), 1);
86 * Deletes an instance of the class, freeing up the memory allocated to it.
88 * @param data The instance to free.
90 void rpi_delete(Rpi
* data
) {
92 // Clear the data for security
93 memset(data
, 0, sizeof(Rpi
));
100 * Generates a Rolling Proximity Identifier based on the time interval number
103 * The operation may fail under certain circumstances, such as if the
104 * HMAC operation fails for some reason.
106 * For internal use. It generally makes more sense to use the
107 * contrac_set_time_interval_number() function instead.
109 * @param data The context object to work with.
110 * @param time_interval_number The time interval number to use to generate the
112 * @return true if the operation completed successfully, false otherwise.
114 bool rpi_generate_proximity_id(Rpi
* data
, Dtk
const * dtk
, uint8_t time_interval_number
) {
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;
121 unsigned char const * daily_key
;
123 // RPI_{i, j} <- Truncate(HMAC(dkt_i, (UTF8("CT-RPI") || TIN_j)), 16)
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
);
133 daily_key
= dtk_get_daily_key(dtk
);
134 HMAC(EVP_sha256(), daily_key
, DTK_SIZE
, encode
, sizeof(encode
), output
, &out_length
);
136 _Static_assert ((EVP_MAX_MD_SIZE
>= 16), "HMAC buffer size too small");
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
];
143 // Zero out padding if there is any
144 for (pos
= min
; pos
< 16; ++pos
) {
150 data
->time_interval_number
= time_interval_number
;
154 LOG(LOG_ERR
, "Error generating rolling proximity id: %lu\n", ERR_get_error());
161 * Gets the Rolling Proximity Identifier for the device in binary format.
163 * For internal use. It generally makes more sense to use the
164 * contrac_get_proximity_id() function instead.
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.
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
176 * @param data The context object to work with.
177 * @return The Rolling Proximity Identifier in binary format, not null
180 unsigned char const * rpi_get_proximity_id(Rpi
const * data
) {
185 * Gets the time interval number that applies to the current RPI.
187 * For internal use. It generally makes more sense to use the
188 * contrac_set_time_interval_number() function instead.
190 * @param data The context object to work with.
191 * @return The time interval number stored in the object.
193 uint8_t rpi_get_time_interval_number(Rpi
const * data
) {
194 return data
->time_interval_number
;
198 * Populates the data structure.
200 * Allows the RPI and time interval number values of the object to be set
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()
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.
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.
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
;
220 * Compares two RPI values.
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.
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];
231 size
= RPI_SIZE_BASE64
+ 1;
232 base64_encode_binary_to_base64(data
->rpi
, RPI_SIZE
, left
, &size
);
234 size
= RPI_SIZE_BASE64
+ 1;
235 base64_encode_binary_to_base64(comparitor
->rpi
, RPI_SIZE
, right
, &size
);
237 return (memcmp(data
, comparitor
, RPI_SIZE
) == 0);
240 /** @} addtogroup RandomProximityIdentifier */