-
Notifications
You must be signed in to change notification settings - Fork 57
/
Copy pathmulti_keyring.c
189 lines (165 loc) · 7.77 KB
/
multi_keyring.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/*
* Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use
* this file except in compliance with the License. A copy of the License is
* located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <assert.h>
#include <aws/cryptosdk/list_utils.h>
#include <aws/cryptosdk/materials.h>
#include <aws/cryptosdk/private/multi_keyring.h>
static int call_on_encrypt_on_list(
const struct aws_array_list *keyrings,
struct aws_allocator *request_alloc,
struct aws_byte_buf *unencrypted_data_key,
struct aws_array_list *keyring_trace,
struct aws_array_list *edks,
const struct aws_hash_table *enc_ctx,
enum aws_cryptosdk_alg_id alg) {
size_t num_keyrings = aws_array_list_length(keyrings);
for (size_t list_idx = 0; list_idx < num_keyrings; ++list_idx) {
struct aws_cryptosdk_keyring *child;
if (aws_array_list_get_at(keyrings, (void *)&child, list_idx)) return AWS_OP_ERR;
if (aws_cryptosdk_keyring_on_encrypt(
child, request_alloc, unencrypted_data_key, keyring_trace, edks, enc_ctx, alg))
return AWS_OP_ERR;
}
return AWS_OP_SUCCESS;
}
static int multi_keyring_on_encrypt(
struct aws_cryptosdk_keyring *multi,
struct aws_allocator *request_alloc,
struct aws_byte_buf *unencrypted_data_key,
struct aws_array_list *keyring_trace,
struct aws_array_list *edks,
const struct aws_hash_table *enc_ctx,
enum aws_cryptosdk_alg_id alg) {
struct multi_keyring *self = (struct multi_keyring *)multi;
struct aws_array_list my_edks;
if (aws_cryptosdk_edk_list_init(request_alloc, &my_edks)) return AWS_OP_ERR;
struct aws_array_list my_trace;
if (aws_cryptosdk_keyring_trace_init(request_alloc, &my_trace)) {
aws_cryptosdk_edk_list_clean_up(&my_edks);
return AWS_OP_ERR;
}
int ret = AWS_OP_SUCCESS;
if (self->generator &&
aws_cryptosdk_keyring_on_encrypt(
self->generator, request_alloc, unencrypted_data_key, &my_trace, &my_edks, enc_ctx, alg)) {
ret = AWS_OP_ERR;
goto out;
}
if (!unencrypted_data_key->buffer) {
/* If we are here, it means we are in one of two possible error cases:
*
* (1) This multi-keyring has a generator that did not generate a data key. Keyrings are not
* required to generate a data key when it is not provided, but generator keyrings are.
*
* (2) This multi-keyring did not have a generator assigned and it was called as the first or
* only keyring for encryption. See comments in multi_keyring.h
*/
ret = aws_raise_error(AWS_CRYPTOSDK_ERR_BAD_STATE);
goto out;
}
if (call_on_encrypt_on_list(
&self->children, request_alloc, unencrypted_data_key, &my_trace, &my_edks, enc_ctx, alg) ||
aws_cryptosdk_transfer_list(edks, &my_edks)) {
ret = AWS_OP_ERR;
goto out;
}
aws_cryptosdk_transfer_list(keyring_trace, &my_trace);
out:
aws_cryptosdk_edk_list_clean_up(&my_edks);
aws_cryptosdk_keyring_trace_clean_up(&my_trace);
return ret;
}
static int multi_keyring_on_decrypt(
struct aws_cryptosdk_keyring *multi,
struct aws_allocator *request_alloc,
struct aws_byte_buf *unencrypted_data_key,
struct aws_array_list *keyring_trace,
const struct aws_array_list *edks,
const struct aws_hash_table *enc_ctx,
enum aws_cryptosdk_alg_id alg) {
/* If one of the contained keyrings succeeds at decrypting the data key, return success,
* but if we fail to decrypt the data key, only return success if there were no
* errors reported from child keyrings.
*/
int ret_if_no_decrypt = AWS_OP_SUCCESS;
struct multi_keyring *self = (struct multi_keyring *)multi;
if (self->generator) {
int decrypt_err = aws_cryptosdk_keyring_on_decrypt(
self->generator, request_alloc, unencrypted_data_key, keyring_trace, edks, enc_ctx, alg);
if (unencrypted_data_key->buffer) return AWS_OP_SUCCESS;
if (decrypt_err) ret_if_no_decrypt = AWS_OP_ERR;
}
size_t num_children = aws_array_list_length(&self->children);
for (size_t child_idx = 0; child_idx < num_children; ++child_idx) {
struct aws_cryptosdk_keyring *child;
if (aws_array_list_get_at(&self->children, (void *)&child, child_idx)) return AWS_OP_ERR;
// if decrypt data key fails, keep trying with other keyrings
int decrypt_err = aws_cryptosdk_keyring_on_decrypt(
child, request_alloc, unencrypted_data_key, keyring_trace, edks, enc_ctx, alg);
if (unencrypted_data_key->buffer) return AWS_OP_SUCCESS;
if (decrypt_err) ret_if_no_decrypt = AWS_OP_ERR;
}
return ret_if_no_decrypt;
}
static void multi_keyring_destroy(struct aws_cryptosdk_keyring *multi) {
struct multi_keyring *self = (struct multi_keyring *)multi;
size_t n_keys = aws_array_list_length(&self->children);
for (size_t i = 0; i < n_keys; i++) {
struct aws_cryptosdk_keyring *child;
if (!aws_array_list_get_at(&self->children, (void *)&child, i)) {
aws_cryptosdk_keyring_release(child);
}
}
aws_cryptosdk_keyring_release(self->generator);
aws_array_list_clean_up(&self->children);
aws_mem_release(self->alloc, self);
}
static const struct aws_cryptosdk_keyring_vt vt = { .vt_size = sizeof(struct aws_cryptosdk_keyring_vt),
.name = "multi keyring",
.destroy = multi_keyring_destroy,
.on_encrypt = multi_keyring_on_encrypt,
.on_decrypt = multi_keyring_on_decrypt };
struct aws_cryptosdk_keyring *aws_cryptosdk_multi_keyring_new(
struct aws_allocator *alloc, struct aws_cryptosdk_keyring *generator) {
AWS_PRECONDITION(aws_allocator_is_valid(alloc));
AWS_PRECONDITION(generator == NULL || aws_cryptosdk_keyring_is_valid(generator));
struct multi_keyring *multi = aws_mem_acquire(alloc, sizeof(struct multi_keyring));
if (!multi) return NULL;
if (aws_array_list_init_dynamic(&multi->children, alloc, 4, sizeof(struct aws_cryptosdk_keyring *))) {
aws_mem_release(alloc, multi);
return NULL;
}
aws_cryptosdk_keyring_base_init(&multi->base, &vt);
if (generator) aws_cryptosdk_keyring_retain(generator);
multi->generator = generator;
multi->alloc = alloc;
AWS_POSTCONDITION(aws_cryptosdk_multi_keyring_is_valid((struct aws_cryptosdk_keyring *)multi));
return (struct aws_cryptosdk_keyring *)multi;
}
bool aws_cryptosdk_multi_keyring_is_valid(struct aws_cryptosdk_keyring *multi) {
if (multi == NULL) {
return false;
}
struct multi_keyring *self = (struct multi_keyring *)multi;
return (self->alloc != NULL) && (self->generator == NULL || aws_cryptosdk_keyring_is_valid(self->generator)) &&
aws_cryptosdk_keyring_is_valid(&self->base) && aws_array_list_is_valid(&self->children);
}
int aws_cryptosdk_multi_keyring_add_child(struct aws_cryptosdk_keyring *multi, struct aws_cryptosdk_keyring *child) {
AWS_PRECONDITION(aws_cryptosdk_multi_keyring_is_valid(multi));
AWS_PRECONDITION(aws_cryptosdk_keyring_is_valid(child));
struct multi_keyring *self = (struct multi_keyring *)multi;
aws_cryptosdk_keyring_retain(child);
return aws_array_list_push_back(&self->children, (void *)&child);
}