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>
17 #include "bluesky-private.h"
19 /* Cryptographic operations. The rest of the BlueSky code merely calls into
20 * the functions in this file, so this is the only point where we interface
21 * with an external cryptographic library. */
23 /* TODO: We ought to switch to an authenticated encryption mode like EAX. */
25 GCRY_THREAD_OPTION_PTHREAD_IMPL;
27 void bluesky_crypt_init()
29 gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
31 if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
34 if (!gcry_check_version(GCRYPT_VERSION))
35 g_error("libgcrypt version mismatch\n");
37 gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
38 gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
41 /* Return cryptographically-strong random data. */
42 void bluesky_crypt_random_bytes(guchar *buf, gint len)
44 gcry_randomize(buf, len, GCRY_STRONG_RANDOM);
47 /* Hash a string down to an encryption key. */
48 void bluesky_crypt_hash_key(const char *keystr, uint8_t *out)
51 gsize csum_len = sizeof(raw_csum);
53 assert(CRYPTO_KEY_SIZE == 16);
55 GChecksum *csum = g_checksum_new(G_CHECKSUM_SHA256);
56 g_checksum_update(csum, (const guchar *)keystr, strlen(keystr));
57 g_checksum_get_digest(csum, raw_csum, &csum_len);
58 g_checksum_free(csum);
60 memcpy(out, raw_csum, CRYPTO_KEY_SIZE);
63 /* Compute an HMAC of a given data block with the given key. The key and
64 * output should both as large as the hash output size. */
65 #define MD_ALGO GCRY_MD_SHA256
66 void bluesky_crypt_hmac(const char *buf, size_t bufsize,
67 const uint8_t key[CRYPTO_HASH_SIZE],
68 uint8_t output[CRYPTO_HASH_SIZE])
73 g_assert(gcry_md_get_algo_dlen(MD_ALGO) == CRYPTO_HASH_SIZE);
75 status = gcry_md_open(&handle, MD_ALGO, GCRY_MD_FLAG_HMAC);
77 g_error("gcrypt error setting up message digest: %s\n",
78 gcry_strerror(status));
82 status = gcry_md_setkey(handle, key, CRYPTO_HASH_SIZE);
84 g_error("gcrypt error setting HMAC key: %s\n",
85 gcry_strerror(status));
89 gcry_md_write(handle, buf, bufsize);
91 unsigned char *digest = gcry_md_read(handle, MD_ALGO);
92 memcpy(output, digest, CRYPTO_HASH_SIZE);
94 gcry_md_close(handle);
97 void bluesky_crypt_derive_keys(BlueSkyCryptKeys *keys, const gchar *master)
99 uint8_t outbuf[CRYPTO_HASH_SIZE];
101 const char *key_type = "ENCRYPT";
102 bluesky_crypt_hmac(key_type, strlen(key_type),
103 (const uint8_t *)master, outbuf);
104 memcpy(keys->encryption_key, outbuf, sizeof(keys->encryption_key));
107 bluesky_crypt_hmac(key_type, strlen(key_type),
108 (const uint8_t *)master, outbuf);
109 memcpy(keys->authentication_key, outbuf, sizeof(keys->authentication_key));
112 /* A boolean: are blocks of the specified type encrypted in the BlueSky file
114 gboolean bluesky_crypt_block_needs_encryption(uint8_t type)
122 case LOGTYPE_INODE_MAP:
123 case LOGTYPE_CHECKPOINT:
126 g_warning("Unknown log item type in crypto layer: %d!\n",
132 void bluesky_crypt_block_encrypt(gchar *cloud_block, size_t len,
133 BlueSkyCryptKeys *keys)
136 gcry_cipher_hd_t handle;
138 status = gcry_cipher_open(&handle, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CTR,
141 g_error("gcrypt error setting up encryption: %s\n",
142 gcry_strerror(status));
145 struct cloudlog_header *header = (struct cloudlog_header *)cloud_block;
146 g_assert(memcmp(header->magic, CLOUDLOG_MAGIC, sizeof(header->magic)) == 0);
148 gcry_cipher_setkey(handle, keys->encryption_key, CRYPTO_KEY_SIZE);
150 g_error("gcrypt error setting key: %s\n",
151 gcry_strerror(status));
154 gboolean encrypted = bluesky_crypt_block_needs_encryption(header->type);
157 header->magic[3] ^= 0x10;
159 bluesky_crypt_random_bytes(header->crypt_iv, sizeof(header->crypt_iv));
160 status = gcry_cipher_setctr(handle, header->crypt_iv,
161 sizeof(header->crypt_iv));
163 g_error("gcrypt error setting IV: %s\n",
164 gcry_strerror(status));
167 status = gcry_cipher_encrypt(handle,
168 cloud_block + sizeof(struct cloudlog_header),
169 GUINT32_FROM_LE(header->size1),
172 g_error("gcrypt error encrypting: %s\n",
173 gcry_strerror(status));
176 memset(header->crypt_iv, 0, sizeof(header->crypt_iv));
179 bluesky_crypt_hmac((char *)&header->crypt_iv,
180 cloud_block + len - (char *)&header->crypt_iv - GUINT32_FROM_LE(header->size3),
181 keys->authentication_key,
184 gcry_cipher_close(handle);
187 gboolean bluesky_crypt_block_decrypt(gchar *cloud_block, size_t len,
188 BlueSkyCryptKeys *keys,
189 gboolean allow_unauth)
192 uint8_t hmac_check[CRYPTO_HASH_SIZE];
194 gboolean encrypted = TRUE;
196 struct cloudlog_header *header = (struct cloudlog_header *)cloud_block;
197 if (memcmp(header->magic, CLOUDLOG_MAGIC,
198 sizeof(header->magic)) == 0)
201 g_assert(memcmp(header->magic, CLOUDLOG_MAGIC_ENCRYPTED,
202 sizeof(header->magic)) == 0);
204 if (encrypted != bluesky_crypt_block_needs_encryption(header->type)) {
205 g_warning("Encrypted status of item does not match expected!\n");
208 bluesky_crypt_hmac((char *)&header->crypt_iv,
209 cloud_block + len - (char *)&header->crypt_iv - GUINT32_FROM_LE(header->size3),
210 keys->authentication_key,
212 if (memcmp(hmac_check, header->crypt_auth, CRYPTO_HASH_SIZE) != 0) {
213 g_warning("Cloud block HMAC does not match!");
215 && (header->type == LOGTYPE_INODE_MAP + '0'
216 || header->type == LOGTYPE_CHECKPOINT + '0'))
218 g_warning("Allowing unauthenticated data from cleaner");
225 gcry_cipher_hd_t handle;
226 status = gcry_cipher_open(&handle, GCRY_CIPHER_AES,
227 GCRY_CIPHER_MODE_CTR, 0);
229 g_error("gcrypt error setting up encryption: %s\n",
230 gcry_strerror(status));
233 gcry_cipher_setkey(handle, keys->encryption_key, CRYPTO_KEY_SIZE);
235 g_error("gcrypt error setting key: %s\n",
236 gcry_strerror(status));
239 status = gcry_cipher_setctr(handle, header->crypt_iv,
240 sizeof(header->crypt_iv));
242 g_error("gcrypt error setting IV: %s\n",
243 gcry_strerror(status));
246 status = gcry_cipher_decrypt(handle,
247 cloud_block + sizeof(struct cloudlog_header),
248 GUINT32_FROM_LE(header->size1),
251 g_error("gcrypt error decrypting: %s\n",
252 gcry_strerror(status));
254 header->magic[3] ^= 0x10;
255 memset(header->crypt_iv, 0, sizeof(header->crypt_iv));
257 gcry_cipher_close(handle);