1 #include "test_common.h"
2 
3 #include <CommonCrypto/CommonDigest.h>
4 #include <CommonCrypto/CommonHMAC.h>
5 #include <CommonCrypto/CommonCryptor.h>
6 
7 #include <stdio.h>
8 
test_random_generator(uint8_t * data,size_t len,void * user_data)9 int test_random_generator(uint8_t *data, size_t len, void *user_data)
10 {
11     arc4random_buf(data, len);
12     return 0;
13 
14 #if 0
15     /*
16      * Apple's documentation recommends this method for generating secure
17      * random numbers. However, it is too slow for the purpose of unit tests.
18      */
19     int result = 0;
20 
21     FILE *fp = fopen("/dev/random", "r");
22     if(!fp) {
23         result = SG_ERR_UNKNOWN;
24         goto complete;
25     }
26 
27     size_t n = fread(data, 1, len, fp);
28     if(n != len) {
29         result = SG_ERR_UNKNOWN;
30         goto complete;
31     }
32 
33 complete:
34     if(fp) {
35         fclose(fp);
36     }
37     return result;
38 #endif
39 }
40 
test_hmac_sha256_init(void ** hmac_context,const uint8_t * key,size_t key_len,void * user_data)41 int test_hmac_sha256_init(void **hmac_context, const uint8_t *key, size_t key_len, void *user_data)
42 {
43     CCHmacContext *ctx = malloc(sizeof(CCHmacContext));
44     if(!ctx) {
45         return SG_ERR_NOMEM;
46     }
47 
48     CCHmacInit(ctx, kCCHmacAlgSHA256, key, key_len);
49     *hmac_context = ctx;
50 
51     return 0;
52 }
53 
test_hmac_sha256_update(void * hmac_context,const uint8_t * data,size_t data_len,void * user_data)54 int test_hmac_sha256_update(void *hmac_context, const uint8_t *data, size_t data_len, void *user_data)
55 {
56     CCHmacContext *ctx = hmac_context;
57     CCHmacUpdate(ctx, data, data_len);
58     return 0;
59 }
60 
test_hmac_sha256_final(void * hmac_context,signal_buffer ** output,void * user_data)61 int test_hmac_sha256_final(void *hmac_context, signal_buffer **output, void *user_data)
62 {
63     CCHmacContext *ctx = hmac_context;
64 
65     signal_buffer *output_buffer = signal_buffer_alloc(CC_SHA256_DIGEST_LENGTH);
66     if(!output_buffer) {
67         return SG_ERR_NOMEM;
68     }
69 
70     CCHmacFinal(ctx, signal_buffer_data(output_buffer));
71 
72     *output = output_buffer;
73 
74     return 0;
75 }
76 
test_hmac_sha256_cleanup(void * hmac_context,void * user_data)77 void test_hmac_sha256_cleanup(void *hmac_context, void *user_data)
78 {
79     if(hmac_context) {
80         CCHmacContext *ctx = hmac_context;
81         free(ctx);
82     }
83 }
84 
test_sha512_digest_init(void ** digest_context,void * user_data)85 int test_sha512_digest_init(void **digest_context, void *user_data)
86 {
87     int result = 0;
88 
89     CC_SHA512_CTX *ctx = malloc(sizeof(CC_SHA512_CTX));
90     if(!ctx) {
91         result = SG_ERR_NOMEM;
92         goto complete;
93     }
94 
95     result = CC_SHA512_Init(ctx);
96     if(result != 1) {
97         result = SG_ERR_UNKNOWN;
98         goto complete;
99     }
100 
101 complete:
102     if(result < 0) {
103         if(ctx) {
104             free(ctx);
105         }
106     }
107     else {
108         *digest_context = ctx;
109     }
110     return result;
111 }
112 
test_sha512_digest_update(void * digest_context,const uint8_t * data,size_t data_len,void * user_data)113 int test_sha512_digest_update(void *digest_context, const uint8_t *data, size_t data_len, void *user_data)
114 {
115     CC_SHA512_CTX *ctx = digest_context;
116 
117     int result = CC_SHA512_Update(ctx, data, data_len);
118 
119     return (result == 1) ? SG_SUCCESS : SG_ERR_UNKNOWN;
120 }
121 
test_sha512_digest_final(void * digest_context,signal_buffer ** output,void * user_data)122 int test_sha512_digest_final(void *digest_context, signal_buffer **output, void *user_data)
123 {
124     int result = 0;
125     unsigned char md[CC_SHA512_DIGEST_LENGTH];
126     CC_SHA512_CTX *ctx = digest_context;
127 
128     result = CC_SHA512_Final(md, ctx);
129     if(result == 1) {
130         result = SG_SUCCESS;
131     }
132     else {
133         result = SG_ERR_UNKNOWN;
134         goto complete;
135     }
136 
137     result = CC_SHA512_Init(ctx);
138     if(result == 1) {
139         result = SG_SUCCESS;
140     }
141     else {
142         result = SG_ERR_UNKNOWN;
143         goto complete;
144     }
145 
146     signal_buffer *output_buffer = signal_buffer_create(md, CC_SHA512_DIGEST_LENGTH);
147     if(!output_buffer) {
148         result = SG_ERR_NOMEM;
149         goto complete;
150     }
151 
152     *output = output_buffer;
153 
154 complete:
155     return result;
156 }
157 
test_sha512_digest_cleanup(void * digest_context,void * user_data)158 void test_sha512_digest_cleanup(void *digest_context, void *user_data)
159 {
160     if(digest_context) {
161         CC_SHA512_CTX *ctx = digest_context;
162         free(ctx);
163     }
164 }
165 
cc_status_to_result(CCCryptorStatus status)166 int cc_status_to_result(CCCryptorStatus status)
167 {
168     switch(status) {
169     case kCCSuccess:
170         return SG_SUCCESS;
171     case kCCParamError:
172     case kCCBufferTooSmall:
173         return SG_ERR_INVAL;
174     case kCCMemoryFailure:
175         return SG_ERR_NOMEM;
176     case kCCAlignmentError:
177     case kCCDecodeError:
178     case kCCUnimplemented:
179     case kCCOverflow:
180     case kCCRNGFailure:
181     case kCCUnspecifiedError:
182     case kCCCallSequenceError:
183     default:
184         return SG_ERR_UNKNOWN;
185     }
186 }
187 
test_encrypt(signal_buffer ** output,int cipher,const uint8_t * key,size_t key_len,const uint8_t * iv,size_t iv_len,const uint8_t * plaintext,size_t plaintext_len,void * user_data)188 int test_encrypt(signal_buffer **output,
189         int cipher,
190         const uint8_t *key, size_t key_len,
191         const uint8_t *iv, size_t iv_len,
192         const uint8_t *plaintext, size_t plaintext_len,
193         void *user_data)
194 {
195     int result = 0;
196     uint8_t *out_buf = 0;
197     CCCryptorStatus status = kCCSuccess;
198     CCCryptorRef ref = 0;
199 
200     if(cipher == SG_CIPHER_AES_CBC_PKCS5) {
201         status = CCCryptorCreate(kCCEncrypt, kCCAlgorithmAES, kCCOptionPKCS7Padding, key, key_len, iv, &ref);
202     }
203     else if(cipher == SG_CIPHER_AES_CTR_NOPADDING) {
204         status = CCCryptorCreateWithMode(kCCEncrypt, kCCModeCTR, kCCAlgorithmAES, ccNoPadding,
205                 iv, key, key_len, 0, 0, 0, kCCModeOptionCTR_BE, &ref);
206     }
207     else {
208         status = kCCParamError;
209     }
210     if(status != kCCSuccess) {
211         result = cc_status_to_result(status);
212         goto complete;
213     }
214 
215     size_t available_len = CCCryptorGetOutputLength(ref, plaintext_len, 1);
216     out_buf = malloc(available_len);
217     if(!out_buf) {
218         fprintf(stderr, "cannot allocate output buffer\n");
219         result = SG_ERR_NOMEM;
220         goto complete;
221     }
222 
223     size_t update_moved_len = 0;
224     status = CCCryptorUpdate(ref, plaintext, plaintext_len, out_buf, available_len, &update_moved_len);
225     if(status != kCCSuccess) {
226         result = cc_status_to_result(status);
227         goto complete;
228     }
229 
230     size_t final_moved_len = 0;
231     status = CCCryptorFinal(ref, out_buf + update_moved_len, available_len - update_moved_len, &final_moved_len);
232     if(status != kCCSuccess) {
233         result = cc_status_to_result(status);
234         goto complete;
235     }
236 
237     signal_buffer *output_buffer = signal_buffer_create(out_buf, update_moved_len + final_moved_len);
238     if(!output_buffer) {
239         result = SG_ERR_NOMEM;
240         goto complete;
241     }
242 
243     *output = output_buffer;
244 
245 complete:
246     if(ref) {
247         CCCryptorRelease(ref);
248     }
249     if(out_buf) {
250         free(out_buf);
251     }
252     return result;
253 }
254 
test_decrypt(signal_buffer ** output,int cipher,const uint8_t * key,size_t key_len,const uint8_t * iv,size_t iv_len,const uint8_t * ciphertext,size_t ciphertext_len,void * user_data)255 int test_decrypt(signal_buffer **output,
256         int cipher,
257         const uint8_t *key, size_t key_len,
258         const uint8_t *iv, size_t iv_len,
259         const uint8_t *ciphertext, size_t ciphertext_len,
260         void *user_data)
261 {
262     int result = 0;
263     uint8_t *out_buf = 0;
264     CCCryptorStatus status = kCCSuccess;
265     CCCryptorRef ref = 0;
266 
267     if(cipher == SG_CIPHER_AES_CBC_PKCS5) {
268         status = CCCryptorCreate(kCCDecrypt, kCCAlgorithmAES, kCCOptionPKCS7Padding, key, key_len, iv, &ref);
269     }
270     else if(cipher == SG_CIPHER_AES_CTR_NOPADDING) {
271         status = CCCryptorCreateWithMode(kCCDecrypt, kCCModeCTR, kCCAlgorithmAES, ccNoPadding,
272                 iv, key, key_len, 0, 0, 0, kCCModeOptionCTR_BE, &ref);
273     }
274     else {
275         status = kCCParamError;
276     }
277     if(status != kCCSuccess) {
278         result = cc_status_to_result(status);
279         goto complete;
280     }
281 
282     out_buf = malloc(sizeof(uint8_t) * ciphertext_len);
283     if(!out_buf) {
284         fprintf(stderr, "cannot allocate output buffer\n");
285         result = SG_ERR_UNKNOWN;
286         goto complete;
287     }
288 
289     size_t update_moved_len = 0;
290     status = CCCryptorUpdate(ref, ciphertext, ciphertext_len, out_buf, ciphertext_len, &update_moved_len);
291     if(status != kCCSuccess) {
292         result = cc_status_to_result(status);
293         goto complete;
294     }
295 
296     size_t final_moved_len = 0;
297     status = CCCryptorFinal(ref, out_buf + update_moved_len, ciphertext_len - update_moved_len, &final_moved_len);
298     if(status != kCCSuccess) {
299         result = cc_status_to_result(status);
300         goto complete;
301     }
302 
303     signal_buffer *output_buffer = signal_buffer_create(out_buf, update_moved_len + final_moved_len);
304     if(!output_buffer) {
305         result = SG_ERR_NOMEM;
306         goto complete;
307     }
308 
309     *output = output_buffer;
310 
311 complete:
312     if(ref) {
313         CCCryptorRelease(ref);
314     }
315     if(out_buf) {
316         free(out_buf);
317     }
318     return result;
319 }
320