1 /* Blue Sky: File Systems in the Cloud
3 * Copyright (C) 2009 The Regents of the University of California
4 * Written by Michael Vrable <mvrable@cs.ucsd.edu>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
39 #include "bluesky-private.h"
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. */
45 /* TODO: We ought to switch to an authenticated encryption mode like EAX. */
47 GCRY_THREAD_OPTION_PTHREAD_IMPL;
49 void bluesky_crypt_init()
51 gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
53 if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
56 if (!gcry_check_version(GCRYPT_VERSION))
57 g_error("libgcrypt version mismatch\n");
59 gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
60 gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
63 /* Return cryptographically-strong random data. */
64 void bluesky_crypt_random_bytes(guchar *buf, gint len)
66 gcry_randomize(buf, len, GCRY_STRONG_RANDOM);
69 /* Hash a string down to an encryption key. */
70 void bluesky_crypt_hash_key(const char *keystr, uint8_t *out)
73 gsize csum_len = sizeof(raw_csum);
75 assert(CRYPTO_KEY_SIZE == 16);
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);
82 memcpy(out, raw_csum, CRYPTO_KEY_SIZE);
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])
95 g_assert(gcry_md_get_algo_dlen(MD_ALGO) == CRYPTO_HASH_SIZE);
97 status = gcry_md_open(&handle, MD_ALGO, GCRY_MD_FLAG_HMAC);
99 g_error("gcrypt error setting up message digest: %s\n",
100 gcry_strerror(status));
104 status = gcry_md_setkey(handle, key, CRYPTO_HASH_SIZE);
106 g_error("gcrypt error setting HMAC key: %s\n",
107 gcry_strerror(status));
111 gcry_md_write(handle, buf, bufsize);
113 unsigned char *digest = gcry_md_read(handle, MD_ALGO);
114 memcpy(output, digest, CRYPTO_HASH_SIZE);
116 gcry_md_close(handle);
119 void bluesky_crypt_derive_keys(BlueSkyCryptKeys *keys, const gchar *master)
121 uint8_t outbuf[CRYPTO_HASH_SIZE];
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));
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));
134 /* A boolean: are blocks of the specified type encrypted in the BlueSky file
136 gboolean bluesky_crypt_block_needs_encryption(uint8_t type)
144 case LOGTYPE_INODE_MAP:
145 case LOGTYPE_CHECKPOINT:
148 g_warning("Unknown log item type in crypto layer: %d!\n",
154 void bluesky_crypt_block_encrypt(gchar *cloud_block, size_t len,
155 BlueSkyCryptKeys *keys)
157 if (bluesky_options.disable_crypto)
161 gcry_cipher_hd_t handle;
163 status = gcry_cipher_open(&handle, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CTR,
166 g_error("gcrypt error setting up encryption: %s\n",
167 gcry_strerror(status));
170 struct cloudlog_header *header = (struct cloudlog_header *)cloud_block;
171 g_assert(memcmp(header->magic, CLOUDLOG_MAGIC, sizeof(header->magic)) == 0);
173 gcry_cipher_setkey(handle, keys->encryption_key, CRYPTO_KEY_SIZE);
175 g_error("gcrypt error setting key: %s\n",
176 gcry_strerror(status));
179 gboolean encrypted = bluesky_crypt_block_needs_encryption(header->type);
182 header->magic[3] ^= 0x10;
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));
188 g_error("gcrypt error setting IV: %s\n",
189 gcry_strerror(status));
192 status = gcry_cipher_encrypt(handle,
193 cloud_block + sizeof(struct cloudlog_header),
194 GUINT32_FROM_LE(header->size1),
197 g_error("gcrypt error encrypting: %s\n",
198 gcry_strerror(status));
201 memset(header->crypt_iv, 0, sizeof(header->crypt_iv));
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,
209 gcry_cipher_close(handle);
212 gboolean bluesky_crypt_block_decrypt(gchar *cloud_block, size_t len,
213 BlueSkyCryptKeys *keys,
214 gboolean allow_unauth)
217 uint8_t hmac_check[CRYPTO_HASH_SIZE];
219 gboolean encrypted = TRUE;
221 struct cloudlog_header *header = (struct cloudlog_header *)cloud_block;
222 if (memcmp(header->magic, CLOUDLOG_MAGIC,
223 sizeof(header->magic)) == 0)
226 g_assert(memcmp(header->magic, CLOUDLOG_MAGIC_ENCRYPTED,
227 sizeof(header->magic)) == 0);
229 if (bluesky_options.disable_crypto) {
230 g_assert(encrypted == FALSE);
234 if (encrypted != bluesky_crypt_block_needs_encryption(header->type)) {
235 g_warning("Encrypted status of item does not match expected!\n");
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,
242 if (memcmp(hmac_check, header->crypt_auth, CRYPTO_HASH_SIZE) != 0) {
243 g_warning("Cloud block HMAC does not match!");
245 && (header->type == LOGTYPE_INODE_MAP + '0'
246 || header->type == LOGTYPE_CHECKPOINT + '0'))
248 g_warning("Allowing unauthenticated data from cleaner");
255 gcry_cipher_hd_t handle;
256 status = gcry_cipher_open(&handle, GCRY_CIPHER_AES,
257 GCRY_CIPHER_MODE_CTR, 0);
259 g_error("gcrypt error setting up encryption: %s\n",
260 gcry_strerror(status));
263 gcry_cipher_setkey(handle, keys->encryption_key, CRYPTO_KEY_SIZE);
265 g_error("gcrypt error setting key: %s\n",
266 gcry_strerror(status));
269 status = gcry_cipher_setctr(handle, header->crypt_iv,
270 sizeof(header->crypt_iv));
272 g_error("gcrypt error setting IV: %s\n",
273 gcry_strerror(status));
276 status = gcry_cipher_decrypt(handle,
277 cloud_block + sizeof(struct cloudlog_header),
278 GUINT32_FROM_LE(header->size1),
281 g_error("gcrypt error decrypting: %s\n",
282 gcry_strerror(status));
284 header->magic[3] ^= 0x10;
285 memset(header->crypt_iv, 0, sizeof(header->crypt_iv));
287 gcry_cipher_close(handle);