Move encryption into S3 backend.
[bluesky.git] / bluesky / s3store.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  * TODO: Licensing
7  */
8
9 #include <stdint.h>
10 #include <stdlib.h>
11 #include <glib.h>
12 #include <string.h>
13
14 #include "bluesky-private.h"
15 #include "libs3.h"
16
17 /* Interface to Amazon S3 storage. */
18
19 /* Simple in-memory data store for test purposes. */
20 typedef struct {
21     S3BucketContext bucket;
22     uint8_t encryption_key[CRYPTO_KEY_SIZE];
23 } S3Store;
24
25 static gpointer s3store_new()
26 {
27     S3Store *store = g_new(S3Store, 1);
28     store->bucket.bucketName = "mvrable-bluesky";
29     store->bucket.protocol = S3ProtocolHTTP;
30     store->bucket.uriStyle = S3UriStylePath;
31     store->bucket.accessKeyId = getenv("AWS_ACCESS_KEY_ID");
32     store->bucket.secretAccessKey = getenv("AWS_SECRET_ACCESS_KEY");
33
34     const char *key = getenv("BLUESKY_KEY");
35     if (key == NULL) {
36         g_error("Encryption key not defined; please set BLUESKY_KEY environment variable");
37         exit(1);
38     }
39
40     bluesky_crypt_hash_key(key, store->encryption_key);
41
42     g_print("Initializing S3 with bucket %s, access key %s, encryption key %s\n",
43             store->bucket.bucketName, store->bucket.accessKeyId, key);
44
45     return store;
46 }
47
48 static void s3store_destroy(gpointer store)
49 {
50     g_free(store);
51 }
52
53 struct get_info {
54     gchar *buf;
55     gint offset;
56 };
57
58 static S3Status s3store_get_handler(int bufferSize, const char *buffer,
59                                     void *callbackData)
60 {
61     struct get_info *info = (struct get_info *)callbackData;
62     gint bytes = MIN(bufferSize, (int)(BLUESKY_BLOCK_SIZE - info->offset));
63     memcpy(info->buf + info->offset, buffer, bytes);
64     info->offset += bytes;
65     return S3StatusOK;
66 }
67
68 struct put_info {
69     BlueSkyRCStr *val;
70     gint offset;
71 };
72
73 static int s3store_put_handler(int bufferSize, char *buffer,
74                                void *callbackData)
75 {
76     struct put_info *info = (struct put_info *)callbackData;
77     gint bytes = MIN(bufferSize, (int)(info->val->len - info->offset));
78     memcpy(buffer, (char *)info->val->data + info->offset, bytes);
79     info->offset += bytes;
80     return bytes;
81 }
82
83 static S3Status s3store_properties_callback(const S3ResponseProperties *properties,
84                                      void *callbackData)
85 {
86     g_print("(Properties callback)\n");
87     return S3StatusOK;
88 }
89
90 static void s3store_response_callback(S3Status status,
91                                const S3ErrorDetails *errorDetails,
92                                void *callbackData)
93 {
94     g_print("S3 operation complete, status=%s, now=%ld\n",
95             S3_get_status_name(status), bluesky_now_hires());
96     if (errorDetails != NULL) {
97         g_print("  Error message: %s\n", errorDetails->message);
98     }
99 }
100
101 static BlueSkyRCStr *s3store_get(gpointer s, const gchar *key)
102 {
103     S3Store *store = (S3Store *)s;
104
105     struct get_info info;
106     info.buf = (char *)g_malloc0(BLUESKY_BLOCK_SIZE);
107     info.offset = 0;
108
109     struct S3GetObjectHandler handler;
110     handler.responseHandler.propertiesCallback = s3store_properties_callback;
111     handler.responseHandler.completeCallback = s3store_response_callback;
112     handler.getObjectDataCallback = s3store_get_handler;
113
114     g_print("Starting fetch of %s from S3...\n", key);
115     S3_get_object(&store->bucket, key, NULL, 0, 0, NULL,
116                   &handler, &info);
117
118     BlueSkyRCStr *raw, *decrypted;
119     raw = bluesky_string_new(info.buf, BLUESKY_BLOCK_SIZE);
120     decrypted = bluesky_crypt_decrypt(raw, store->encryption_key);
121     bluesky_string_unref(raw);
122     return decrypted;
123 }
124
125 static void s3store_put(gpointer s, const gchar *key, BlueSkyRCStr *val)
126 {
127     S3Store *store = (S3Store *)s;
128     BlueSkyRCStr *encrypted = bluesky_crypt_encrypt(val, store->encryption_key);
129
130     struct put_info info;
131     info.val = encrypted;
132     info.offset = 0;
133
134     struct S3PutObjectHandler handler;
135     handler.responseHandler.propertiesCallback = s3store_properties_callback;
136     handler.responseHandler.completeCallback = s3store_response_callback;
137     handler.putObjectDataCallback = s3store_put_handler;
138
139     g_print("Starting store of %s to S3 at %ld...\n", key, bluesky_now_hires());
140     S3_put_object(&store->bucket, key, encrypted->len, NULL, NULL,
141                   &handler, &info);
142
143     /* TODO: unref encrypted */
144 }
145
146 static BlueSkyStoreImplementation store_impl = {
147     .create = s3store_new,
148     .destroy = s3store_destroy,
149     .get = s3store_get,
150     .put = s3store_put,
151 };
152
153 void bluesky_store_init_s3(void)
154 {
155     S3_initialize(NULL, S3_INIT_ALL);
156     bluesky_store_register(&store_impl, "s3");
157 }