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