1 /*
2 chronyd/chronyc - Programs for keeping computer clocks accurate.
3
4 **********************************************************************
5 * Copyright (C) Miroslav Lichvar 2020
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of version 2 of the GNU General Public License as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 *
20 **********************************************************************
21
22 =======================================================================
23
24 SIV ciphers using the GnuTLS library
25 */
26
27 #include "config.h"
28
29 #include "sysincl.h"
30
31 #include <gnutls/crypto.h>
32
33 #include "logging.h"
34 #include "memory.h"
35 #include "siv.h"
36
37 struct SIV_Instance_Record {
38 gnutls_cipher_algorithm_t algorithm;
39 gnutls_aead_cipher_hd_t cipher;
40 };
41
42 /* ================================================== */
43
44 static int instance_counter = 0;
45 static int gnutls_initialised = 0;
46
47 /* ================================================== */
48
49 static void
init_gnutls(void)50 init_gnutls(void)
51 {
52 int r;
53
54 if (gnutls_initialised)
55 return;
56
57 r = gnutls_global_init();
58 if (r < 0)
59 LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
60
61 DEBUG_LOG("Initialised");
62 gnutls_initialised = 1;
63 }
64
65 /* ================================================== */
66
67 static void
deinit_gnutls(void)68 deinit_gnutls(void)
69 {
70 assert(gnutls_initialised);
71 gnutls_global_deinit();
72 gnutls_initialised = 0;
73 DEBUG_LOG("Deinitialised");
74 }
75
76 /* ================================================== */
77
78 static gnutls_cipher_algorithm_t
get_cipher_algorithm(SIV_Algorithm algorithm)79 get_cipher_algorithm(SIV_Algorithm algorithm)
80 {
81 switch (algorithm) {
82 case AEAD_AES_SIV_CMAC_256:
83 return GNUTLS_CIPHER_AES_128_SIV;
84 default:
85 return 0;
86 }
87 }
88
89 /* ================================================== */
90
91 SIV_Instance
SIV_CreateInstance(SIV_Algorithm algorithm)92 SIV_CreateInstance(SIV_Algorithm algorithm)
93 {
94 gnutls_cipher_algorithm_t calgo;
95 SIV_Instance instance;
96
97 calgo = get_cipher_algorithm(algorithm);
98 if (calgo == 0)
99 return NULL;
100
101 if (instance_counter == 0)
102 init_gnutls();
103
104 /* Check if the cipher is actually supported */
105 if (gnutls_cipher_get_tag_size(calgo) == 0) {
106 if (instance_counter == 0)
107 deinit_gnutls();
108 return NULL;
109 }
110
111 instance = MallocNew(struct SIV_Instance_Record);
112 instance->algorithm = calgo;
113 instance->cipher = NULL;
114
115 instance_counter++;
116
117 return instance;
118 }
119
120 /* ================================================== */
121
122 void
SIV_DestroyInstance(SIV_Instance instance)123 SIV_DestroyInstance(SIV_Instance instance)
124 {
125 if (instance->cipher)
126 gnutls_aead_cipher_deinit(instance->cipher);
127 Free(instance);
128
129 instance_counter--;
130 if (instance_counter == 0)
131 deinit_gnutls();
132 }
133
134 /* ================================================== */
135
136 int
SIV_GetKeyLength(SIV_Algorithm algorithm)137 SIV_GetKeyLength(SIV_Algorithm algorithm)
138 {
139 gnutls_cipher_algorithm_t calgo = get_cipher_algorithm(algorithm);
140 int len;
141
142 if (calgo == 0)
143 return 0;
144
145 len = gnutls_cipher_get_key_size(calgo);
146
147 if (len < 1 || len > SIV_MAX_KEY_LENGTH)
148 LOG_FATAL("Invalid key length");
149
150 return len;
151 }
152
153 /* ================================================== */
154
155 int
SIV_SetKey(SIV_Instance instance,const unsigned char * key,int length)156 SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
157 {
158 gnutls_aead_cipher_hd_t cipher;
159 gnutls_datum_t datum;
160 int r;
161
162 if (length <= 0 || length != gnutls_cipher_get_key_size(instance->algorithm))
163 return 0;
164
165 datum.data = (unsigned char *)key;
166 datum.size = length;
167
168 /* Initialise a new cipher with the provided key (gnutls does not seem to
169 have a function to change the key directly) */
170 r = gnutls_aead_cipher_init(&cipher, instance->algorithm, &datum);
171 if (r < 0) {
172 DEBUG_LOG("Could not initialise %s : %s", "cipher", gnutls_strerror(r));
173 return 0;
174 }
175
176 /* Replace the previous cipher */
177 if (instance->cipher)
178 gnutls_aead_cipher_deinit(instance->cipher);
179 instance->cipher = cipher;
180
181 return 1;
182 }
183
184 /* ================================================== */
185
186 int
SIV_GetTagLength(SIV_Instance instance)187 SIV_GetTagLength(SIV_Instance instance)
188 {
189 int len;
190
191 len = gnutls_cipher_get_tag_size(instance->algorithm);
192
193 if (len < 1 || len > SIV_MAX_TAG_LENGTH)
194 LOG_FATAL("Invalid tag length");
195
196 return len;
197 }
198
199 /* ================================================== */
200
201 int
SIV_Encrypt(SIV_Instance instance,const unsigned char * nonce,int nonce_length,const void * assoc,int assoc_length,const void * plaintext,int plaintext_length,unsigned char * ciphertext,int ciphertext_length)202 SIV_Encrypt(SIV_Instance instance,
203 const unsigned char *nonce, int nonce_length,
204 const void *assoc, int assoc_length,
205 const void *plaintext, int plaintext_length,
206 unsigned char *ciphertext, int ciphertext_length)
207 {
208 size_t clen = ciphertext_length;
209
210 if (!instance->cipher)
211 return 0;
212
213 if (nonce_length < 1 || assoc_length < 0 ||
214 plaintext_length < 0 || ciphertext_length < 0)
215 return 0;
216
217 assert(assoc && plaintext);
218
219 if (gnutls_aead_cipher_encrypt(instance->cipher,
220 nonce, nonce_length, assoc, assoc_length, 0,
221 plaintext, plaintext_length, ciphertext, &clen) < 0)
222 return 0;
223
224 if (clen != ciphertext_length)
225 return 0;
226
227 return 1;
228 }
229
230 /* ================================================== */
231
232 int
SIV_Decrypt(SIV_Instance instance,const unsigned char * nonce,int nonce_length,const void * assoc,int assoc_length,const unsigned char * ciphertext,int ciphertext_length,void * plaintext,int plaintext_length)233 SIV_Decrypt(SIV_Instance instance,
234 const unsigned char *nonce, int nonce_length,
235 const void *assoc, int assoc_length,
236 const unsigned char *ciphertext, int ciphertext_length,
237 void *plaintext, int plaintext_length)
238 {
239 size_t plen = plaintext_length;
240
241 if (!instance->cipher)
242 return 0;
243
244 if (nonce_length < 1 || assoc_length < 0 ||
245 plaintext_length < 0 || ciphertext_length < 0)
246 return 0;
247
248 assert(assoc && plaintext);
249
250 if (gnutls_aead_cipher_decrypt(instance->cipher,
251 nonce, nonce_length, assoc, assoc_length, 0,
252 ciphertext, ciphertext_length, plaintext, &plen) < 0)
253 return 0;
254
255 if (plen != plaintext_length)
256 return 0;
257
258 return 1;
259 }
260