Add proper per-file copyright notices/licenses and top-level license.
[bluesky.git] / bluesky / crypto.c
index 13e19c1..492e4ff 100644 (file)
@@ -3,7 +3,29 @@
  * Copyright (C) 2009  The Regents of the University of California
  * Written by Michael Vrable <mvrable@cs.ucsd.edu>
  *
- * TODO: Licensing
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 
 #include <stdint.h>
@@ -109,9 +131,32 @@ void bluesky_crypt_derive_keys(BlueSkyCryptKeys *keys, const gchar *master)
     memcpy(keys->authentication_key, outbuf, sizeof(keys->authentication_key));
 }
 
+/* A boolean: are blocks of the specified type encrypted in the BlueSky file
+ * system? */
+gboolean bluesky_crypt_block_needs_encryption(uint8_t type)
+{
+    type -= '0';
+
+    switch (type) {
+    case LOGTYPE_DATA:
+    case LOGTYPE_INODE:
+        return TRUE;
+    case LOGTYPE_INODE_MAP:
+    case LOGTYPE_CHECKPOINT:
+        return FALSE;
+    default:
+        g_warning("Unknown log item type in crypto layer: %d!\n",
+                  type);
+        return TRUE;
+    }
+}
+
 void bluesky_crypt_block_encrypt(gchar *cloud_block, size_t len,
                                  BlueSkyCryptKeys *keys)
 {
+    if (bluesky_options.disable_crypto)
+        return;
+
     gcry_error_t status;
     gcry_cipher_hd_t handle;
 
@@ -131,109 +176,116 @@ void bluesky_crypt_block_encrypt(gchar *cloud_block, size_t len,
                 gcry_strerror(status));
     }
 
-    bluesky_crypt_random_bytes(header->crypt_iv, sizeof(header->crypt_iv));
-    status = gcry_cipher_setctr(handle, header->crypt_iv,
-                                sizeof(header->crypt_iv));
-    if (status) {
-        g_error("gcrypt error setting IV: %s\n",
-                gcry_strerror(status));
-    }
-
-    status = gcry_cipher_encrypt(handle,
-                                 cloud_block + sizeof(struct cloudlog_header),
-                                 GUINT32_FROM_LE(header->size1),
-                                 NULL, 0);
-    if (status) {
-        g_error("gcrypt error encrypting: %s\n",
-                gcry_strerror(status));
+    gboolean encrypted = bluesky_crypt_block_needs_encryption(header->type);
+
+    if (encrypted) {
+        header->magic[3] ^= 0x10;
+
+        bluesky_crypt_random_bytes(header->crypt_iv, sizeof(header->crypt_iv));
+        status = gcry_cipher_setctr(handle, header->crypt_iv,
+                                    sizeof(header->crypt_iv));
+        if (status) {
+            g_error("gcrypt error setting IV: %s\n",
+                    gcry_strerror(status));
+        }
+
+        status = gcry_cipher_encrypt(handle,
+                                     cloud_block + sizeof(struct cloudlog_header),
+                                     GUINT32_FROM_LE(header->size1),
+                                     NULL, 0);
+        if (status) {
+            g_error("gcrypt error encrypting: %s\n",
+                    gcry_strerror(status));
+        }
+    } else {
+        memset(header->crypt_iv, 0, sizeof(header->crypt_iv));
     }
 
     bluesky_crypt_hmac((char *)&header->crypt_iv,
-                       cloud_block + len - (char *)&header->crypt_iv,
+                       cloud_block + len - (char *)&header->crypt_iv - GUINT32_FROM_LE(header->size3),
                        keys->authentication_key,
                        header->crypt_auth);
-}
-
-#if 0
-/* Encrypt a data block. */
-BlueSkyRCStr *bluesky_crypt_encrypt(BlueSkyRCStr *in, const uint8_t *key)
-{
-    gcry_error_t status;
-    gcry_cipher_hd_t handle;
-
-    status = gcry_cipher_open(&handle, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CTR,
-                              0);
-    if (status) {
-        g_error("gcrypt error setting up encryption: %s\n",
-                gcry_strerror(status));
-    }
-
-    uint8_t *out = g_malloc0(in->len + CRYPTO_BLOCK_SIZE);
-
-    gcry_cipher_setkey(handle, key, CRYPTO_KEY_SIZE);
-    if (status) {
-        g_error("gcrypt error setting key: %s\n",
-                gcry_strerror(status));
-    }
-
-    bluesky_crypt_random_bytes(out, CRYPTO_BLOCK_SIZE);
-    status = gcry_cipher_setctr(handle, out, CRYPTO_BLOCK_SIZE);
-    if (status) {
-        g_error("gcrypt error setting IV: %s\n",
-                gcry_strerror(status));
-    }
-
-    status = gcry_cipher_encrypt(handle, out + CRYPTO_BLOCK_SIZE, in->len,
-                                 in->data, in->len);
-    if (status) {
-        g_error("gcrypt error encrypting: %s\n",
-                gcry_strerror(status));
-    }
 
     gcry_cipher_close(handle);
-
-    return bluesky_string_new(out, in->len + CRYPTO_BLOCK_SIZE);
 }
 
-/* Decrypt a data block. */
-BlueSkyRCStr *bluesky_crypt_decrypt(BlueSkyRCStr *in, const uint8_t *key)
+gboolean bluesky_crypt_block_decrypt(gchar *cloud_block, size_t len,
+                                     BlueSkyCryptKeys *keys,
+                                     gboolean allow_unauth)
 {
     gcry_error_t status;
-    gcry_cipher_hd_t handle;
+    uint8_t hmac_check[CRYPTO_HASH_SIZE];
 
-    g_return_val_if_fail(in->len > CRYPTO_BLOCK_SIZE, NULL);
+    gboolean encrypted = TRUE;
 
-    status = gcry_cipher_open(&handle, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CTR,
-                              0);
-    if (status) {
-        g_error("gcrypt error setting up encryption: %s\n",
-                gcry_strerror(status));
+    struct cloudlog_header *header = (struct cloudlog_header *)cloud_block;
+    if (memcmp(header->magic, CLOUDLOG_MAGIC,
+                    sizeof(header->magic)) == 0)
+        encrypted = FALSE;
+    else
+        g_assert(memcmp(header->magic, CLOUDLOG_MAGIC_ENCRYPTED,
+                        sizeof(header->magic)) == 0);
+
+    if (bluesky_options.disable_crypto) {
+        g_assert(encrypted == FALSE);
+        return TRUE;
     }
 
-    uint8_t *out = g_malloc0(in->len - CRYPTO_BLOCK_SIZE);
-
-    gcry_cipher_setkey(handle, key, CRYPTO_KEY_SIZE);
-    if (status) {
-        g_error("gcrypt error setting key: %s\n",
-                gcry_strerror(status));
+    if (encrypted != bluesky_crypt_block_needs_encryption(header->type)) {
+        g_warning("Encrypted status of item does not match expected!\n");
     }
 
-    status = gcry_cipher_setctr(handle, in->data, CRYPTO_BLOCK_SIZE);
-    if (status) {
-        g_error("gcrypt error setting IV: %s\n",
-                gcry_strerror(status));
+    bluesky_crypt_hmac((char *)&header->crypt_iv,
+                       cloud_block + len - (char *)&header->crypt_iv - GUINT32_FROM_LE(header->size3),
+                       keys->authentication_key,
+                       hmac_check);
+    if (memcmp(hmac_check, header->crypt_auth, CRYPTO_HASH_SIZE) != 0) {
+        g_warning("Cloud block HMAC does not match!");
+        if (allow_unauth
+            && (header->type == LOGTYPE_INODE_MAP + '0'
+                || header->type == LOGTYPE_CHECKPOINT + '0'))
+        {
+            g_warning("Allowing unauthenticated data from cleaner");
+        } else {
+            return FALSE;
+        }
     }
 
-    status = gcry_cipher_decrypt(handle, out, in->len - CRYPTO_BLOCK_SIZE,
-                                 in->data + CRYPTO_BLOCK_SIZE,
-                                 in->len - CRYPTO_BLOCK_SIZE);
-    if (status) {
-        g_error("gcrypt error decrypting: %s\n",
-                gcry_strerror(status));
+    if (encrypted) {
+        gcry_cipher_hd_t handle;
+        status = gcry_cipher_open(&handle, GCRY_CIPHER_AES,
+                                  GCRY_CIPHER_MODE_CTR, 0);
+        if (status) {
+            g_error("gcrypt error setting up encryption: %s\n",
+                    gcry_strerror(status));
+        }
+
+        gcry_cipher_setkey(handle, keys->encryption_key, CRYPTO_KEY_SIZE);
+        if (status) {
+            g_error("gcrypt error setting key: %s\n",
+                    gcry_strerror(status));
+        }
+
+        status = gcry_cipher_setctr(handle, header->crypt_iv,
+                                    sizeof(header->crypt_iv));
+        if (status) {
+            g_error("gcrypt error setting IV: %s\n",
+                    gcry_strerror(status));
+        }
+
+        status = gcry_cipher_decrypt(handle,
+                                     cloud_block + sizeof(struct cloudlog_header),
+                                     GUINT32_FROM_LE(header->size1),
+                                     NULL, 0);
+        if (status) {
+            g_error("gcrypt error decrypting: %s\n",
+                    gcry_strerror(status));
+        }
+        header->magic[3] ^= 0x10;
+        memset(header->crypt_iv, 0, sizeof(header->crypt_iv));
+
+        gcry_cipher_close(handle);
     }
 
-    gcry_cipher_close(handle);
-
-    return bluesky_string_new(out, in->len - CRYPTO_BLOCK_SIZE);
+    return TRUE;
 }
-#endif