Add proper per-file copyright notices/licenses and top-level license.
[bluesky.git] / bluesky / crypto.c
1 /* Blue Sky: File Systems in the Cloud
2  *
3  * Copyright (C) 2009  The Regents of the University of California
4  * Written by Michael Vrable <mvrable@cs.ucsd.edu>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the University nor the names of its contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 #include <stdint.h>
32 #include <assert.h>
33 #include <errno.h>
34 #include <pthread.h>
35 #include <glib.h>
36 #include <string.h>
37 #include <gcrypt.h>
38
39 #include "bluesky-private.h"
40
41 /* Cryptographic operations.  The rest of the BlueSky code merely calls into
42  * the functions in this file, so this is the only point where we interface
43  * with an external cryptographic library. */
44
45 /* TODO: We ought to switch to an authenticated encryption mode like EAX. */
46
47 GCRY_THREAD_OPTION_PTHREAD_IMPL;
48
49 void bluesky_crypt_init()
50 {
51     gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
52
53     if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
54         return;
55
56     if (!gcry_check_version(GCRYPT_VERSION))
57         g_error("libgcrypt version mismatch\n");
58
59     gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
60     gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
61 }
62
63 /* Return cryptographically-strong random data. */
64 void bluesky_crypt_random_bytes(guchar *buf, gint len)
65 {
66     gcry_randomize(buf, len, GCRY_STRONG_RANDOM);
67 }
68
69 /* Hash a string down to an encryption key. */
70 void bluesky_crypt_hash_key(const char *keystr, uint8_t *out)
71 {
72     guint8 raw_csum[32];
73     gsize csum_len = sizeof(raw_csum);
74
75     assert(CRYPTO_KEY_SIZE == 16);
76
77     GChecksum *csum = g_checksum_new(G_CHECKSUM_SHA256);
78     g_checksum_update(csum, (const guchar *)keystr, strlen(keystr));
79     g_checksum_get_digest(csum, raw_csum, &csum_len);
80     g_checksum_free(csum);
81
82     memcpy(out, raw_csum, CRYPTO_KEY_SIZE);
83 }
84
85 /* Compute an HMAC of a given data block with the given key.  The key and
86  * output should both as large as the hash output size. */
87 #define MD_ALGO GCRY_MD_SHA256
88 void bluesky_crypt_hmac(const char *buf, size_t bufsize,
89                         const uint8_t key[CRYPTO_HASH_SIZE],
90                         uint8_t output[CRYPTO_HASH_SIZE])
91 {
92     gcry_error_t status;
93     gcry_md_hd_t handle;
94
95     g_assert(gcry_md_get_algo_dlen(MD_ALGO) == CRYPTO_HASH_SIZE);
96
97     status = gcry_md_open(&handle, MD_ALGO, GCRY_MD_FLAG_HMAC);
98     if (status) {
99         g_error("gcrypt error setting up message digest: %s\n",
100                 gcry_strerror(status));
101         g_assert(FALSE);
102     }
103
104     status = gcry_md_setkey(handle, key, CRYPTO_HASH_SIZE);
105     if (status) {
106         g_error("gcrypt error setting HMAC key: %s\n",
107                 gcry_strerror(status));
108         g_assert(FALSE);
109     }
110
111     gcry_md_write(handle, buf, bufsize);
112
113     unsigned char *digest = gcry_md_read(handle, MD_ALGO);
114     memcpy(output, digest, CRYPTO_HASH_SIZE);
115
116     gcry_md_close(handle);
117 }
118
119 void bluesky_crypt_derive_keys(BlueSkyCryptKeys *keys, const gchar *master)
120 {
121     uint8_t outbuf[CRYPTO_HASH_SIZE];
122
123     const char *key_type = "ENCRYPT";
124     bluesky_crypt_hmac(key_type, strlen(key_type),
125                        (const uint8_t *)master, outbuf);
126     memcpy(keys->encryption_key, outbuf, sizeof(keys->encryption_key));
127
128     key_type = "AUTH";
129     bluesky_crypt_hmac(key_type, strlen(key_type),
130                        (const uint8_t *)master, outbuf);
131     memcpy(keys->authentication_key, outbuf, sizeof(keys->authentication_key));
132 }
133
134 /* A boolean: are blocks of the specified type encrypted in the BlueSky file
135  * system? */
136 gboolean bluesky_crypt_block_needs_encryption(uint8_t type)
137 {
138     type -= '0';
139
140     switch (type) {
141     case LOGTYPE_DATA:
142     case LOGTYPE_INODE:
143         return TRUE;
144     case LOGTYPE_INODE_MAP:
145     case LOGTYPE_CHECKPOINT:
146         return FALSE;
147     default:
148         g_warning("Unknown log item type in crypto layer: %d!\n",
149                   type);
150         return TRUE;
151     }
152 }
153
154 void bluesky_crypt_block_encrypt(gchar *cloud_block, size_t len,
155                                  BlueSkyCryptKeys *keys)
156 {
157     if (bluesky_options.disable_crypto)
158         return;
159
160     gcry_error_t status;
161     gcry_cipher_hd_t handle;
162
163     status = gcry_cipher_open(&handle, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CTR,
164                               0);
165     if (status) {
166         g_error("gcrypt error setting up encryption: %s\n",
167                 gcry_strerror(status));
168     }
169
170     struct cloudlog_header *header = (struct cloudlog_header *)cloud_block;
171     g_assert(memcmp(header->magic, CLOUDLOG_MAGIC, sizeof(header->magic)) == 0);
172
173     gcry_cipher_setkey(handle, keys->encryption_key, CRYPTO_KEY_SIZE);
174     if (status) {
175         g_error("gcrypt error setting key: %s\n",
176                 gcry_strerror(status));
177     }
178
179     gboolean encrypted = bluesky_crypt_block_needs_encryption(header->type);
180
181     if (encrypted) {
182         header->magic[3] ^= 0x10;
183
184         bluesky_crypt_random_bytes(header->crypt_iv, sizeof(header->crypt_iv));
185         status = gcry_cipher_setctr(handle, header->crypt_iv,
186                                     sizeof(header->crypt_iv));
187         if (status) {
188             g_error("gcrypt error setting IV: %s\n",
189                     gcry_strerror(status));
190         }
191
192         status = gcry_cipher_encrypt(handle,
193                                      cloud_block + sizeof(struct cloudlog_header),
194                                      GUINT32_FROM_LE(header->size1),
195                                      NULL, 0);
196         if (status) {
197             g_error("gcrypt error encrypting: %s\n",
198                     gcry_strerror(status));
199         }
200     } else {
201         memset(header->crypt_iv, 0, sizeof(header->crypt_iv));
202     }
203
204     bluesky_crypt_hmac((char *)&header->crypt_iv,
205                        cloud_block + len - (char *)&header->crypt_iv - GUINT32_FROM_LE(header->size3),
206                        keys->authentication_key,
207                        header->crypt_auth);
208
209     gcry_cipher_close(handle);
210 }
211
212 gboolean bluesky_crypt_block_decrypt(gchar *cloud_block, size_t len,
213                                      BlueSkyCryptKeys *keys,
214                                      gboolean allow_unauth)
215 {
216     gcry_error_t status;
217     uint8_t hmac_check[CRYPTO_HASH_SIZE];
218
219     gboolean encrypted = TRUE;
220
221     struct cloudlog_header *header = (struct cloudlog_header *)cloud_block;
222     if (memcmp(header->magic, CLOUDLOG_MAGIC,
223                     sizeof(header->magic)) == 0)
224         encrypted = FALSE;
225     else
226         g_assert(memcmp(header->magic, CLOUDLOG_MAGIC_ENCRYPTED,
227                         sizeof(header->magic)) == 0);
228
229     if (bluesky_options.disable_crypto) {
230         g_assert(encrypted == FALSE);
231         return TRUE;
232     }
233
234     if (encrypted != bluesky_crypt_block_needs_encryption(header->type)) {
235         g_warning("Encrypted status of item does not match expected!\n");
236     }
237
238     bluesky_crypt_hmac((char *)&header->crypt_iv,
239                        cloud_block + len - (char *)&header->crypt_iv - GUINT32_FROM_LE(header->size3),
240                        keys->authentication_key,
241                        hmac_check);
242     if (memcmp(hmac_check, header->crypt_auth, CRYPTO_HASH_SIZE) != 0) {
243         g_warning("Cloud block HMAC does not match!");
244         if (allow_unauth
245             && (header->type == LOGTYPE_INODE_MAP + '0'
246                 || header->type == LOGTYPE_CHECKPOINT + '0'))
247         {
248             g_warning("Allowing unauthenticated data from cleaner");
249         } else {
250             return FALSE;
251         }
252     }
253
254     if (encrypted) {
255         gcry_cipher_hd_t handle;
256         status = gcry_cipher_open(&handle, GCRY_CIPHER_AES,
257                                   GCRY_CIPHER_MODE_CTR, 0);
258         if (status) {
259             g_error("gcrypt error setting up encryption: %s\n",
260                     gcry_strerror(status));
261         }
262
263         gcry_cipher_setkey(handle, keys->encryption_key, CRYPTO_KEY_SIZE);
264         if (status) {
265             g_error("gcrypt error setting key: %s\n",
266                     gcry_strerror(status));
267         }
268
269         status = gcry_cipher_setctr(handle, header->crypt_iv,
270                                     sizeof(header->crypt_iv));
271         if (status) {
272             g_error("gcrypt error setting IV: %s\n",
273                     gcry_strerror(status));
274         }
275
276         status = gcry_cipher_decrypt(handle,
277                                      cloud_block + sizeof(struct cloudlog_header),
278                                      GUINT32_FROM_LE(header->size1),
279                                      NULL, 0);
280         if (status) {
281             g_error("gcrypt error decrypting: %s\n",
282                     gcry_strerror(status));
283         }
284         header->magic[3] ^= 0x10;
285         memset(header->crypt_iv, 0, sizeof(header->crypt_iv));
286
287         gcry_cipher_close(handle);
288     }
289
290     return TRUE;
291 }