/src/cjose/src/concatkdf.c
Line | Count | Source |
1 | | /*! |
2 | | * Copyrights |
3 | | * |
4 | | * Portions created or assigned to Cisco Systems, Inc. are |
5 | | * Copyright (c) 2018 Cisco Systems, Inc. All Rights Reserved. |
6 | | */ |
7 | | |
8 | | #include "include/concatkdf_int.h" |
9 | | #include "include/util_int.h" |
10 | | |
11 | | #ifdef _WIN32 |
12 | | #include <Winsock2.h> |
13 | | #else |
14 | | #include <arpa/inet.h> |
15 | | #endif |
16 | | #include <openssl/evp.h> |
17 | | #include <string.h> |
18 | | #include <cjose/base64.h> |
19 | | #include <cjose/util.h> |
20 | | |
21 | | //////////////////////////////////////////////////////////////////////////////// |
22 | | static uint8_t *_apply_uint32(const uint32_t value, uint8_t *buffer) |
23 | 0 | { |
24 | 0 | const uint32_t big_endian_int32 = htonl(value); |
25 | |
|
26 | 0 | memcpy(buffer, &big_endian_int32, 4); |
27 | 0 | return buffer + 4; |
28 | 0 | } |
29 | | |
30 | | static uint8_t *_apply_lendata(const uint8_t *data, const size_t len, uint8_t *buffer) |
31 | 0 | { |
32 | 0 | uint8_t *ptr = buffer; |
33 | |
|
34 | 0 | ptr = _apply_uint32(len, ptr); |
35 | 0 | if (0 < len) |
36 | 0 | { |
37 | 0 | memcpy(ptr, data, len); |
38 | 0 | ptr += len; |
39 | 0 | } |
40 | 0 | return ptr; |
41 | 0 | } |
42 | | |
43 | 0 | size_t min_len(size_t a, size_t b) { return (a < b) ? a : b; } |
44 | | |
45 | | //////////////////////////////////////////////////////////////////////////////// |
46 | | bool cjose_concatkdf_create_otherinfo( |
47 | | const char *alg, const size_t keylen, cjose_header_t *hdr, uint8_t **otherinfo, size_t *otherinfoLen, cjose_err *err) |
48 | 0 | { |
49 | 0 | bool result = false; |
50 | 0 | uint8_t *apu = NULL, *apv = NULL; |
51 | 0 | size_t apuLen = 0, apvLen = 0; |
52 | | |
53 | | // err is optional and may be NULL, so only dereference it when provided. |
54 | | // cjose_header_get() records an error only for an invalid header/attr; for a |
55 | | // valid hdr and the constant APU/APV attrs an absent field just yields NULL. |
56 | 0 | if (NULL != err) |
57 | 0 | { |
58 | 0 | memset(err, 0, sizeof(cjose_err)); |
59 | 0 | } |
60 | 0 | const char *apuStr = cjose_header_get(hdr, CJOSE_HDR_APU, err); |
61 | 0 | const char *apvStr = cjose_header_get(hdr, CJOSE_HDR_APV, err); |
62 | 0 | if (NULL != err && CJOSE_ERR_NONE != err->code) |
63 | 0 | { |
64 | 0 | return false; |
65 | 0 | } |
66 | | |
67 | 0 | apuLen = (NULL != apuStr) ? strlen(apuStr) : 0; |
68 | 0 | if (apuStr != NULL && !cjose_base64url_decode(apuStr, apuLen, &apu, &apuLen, err)) |
69 | 0 | { |
70 | 0 | goto concatkdf_create_otherinfo_finish; |
71 | 0 | } |
72 | 0 | apvLen = (NULL != apvStr) ? strlen(apvStr) : 0; |
73 | 0 | if (apvStr != NULL && !cjose_base64url_decode(apvStr, apvLen, &apv, &apvLen, err)) |
74 | 0 | { |
75 | 0 | goto concatkdf_create_otherinfo_finish; |
76 | 0 | } |
77 | | |
78 | 0 | const size_t algLen = strlen(alg); |
79 | 0 | const size_t bufferLen = (4 + algLen) + (4 + apuLen) + (4 + apvLen) + 4; |
80 | 0 | uint8_t *buffer = cjose_get_alloc()(bufferLen); |
81 | 0 | if (NULL == buffer) |
82 | 0 | { |
83 | 0 | CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); |
84 | 0 | goto concatkdf_create_otherinfo_finish; |
85 | 0 | } |
86 | 0 | uint8_t *ptr = buffer; |
87 | 0 | ptr = _apply_lendata((const uint8_t *)alg, algLen, ptr); |
88 | 0 | ptr = _apply_lendata(apu, apuLen, ptr); |
89 | 0 | ptr = _apply_lendata(apv, apvLen, ptr); |
90 | | // final write; the returned (end) pointer is intentionally not stored |
91 | 0 | _apply_uint32(keylen, ptr); |
92 | |
|
93 | 0 | *otherinfoLen = bufferLen; |
94 | 0 | *otherinfo = buffer; |
95 | 0 | result = true; |
96 | |
|
97 | 0 | concatkdf_create_otherinfo_finish: |
98 | 0 | cjose_get_dealloc()(apu); |
99 | 0 | cjose_get_dealloc()(apv); |
100 | |
|
101 | 0 | return result; |
102 | 0 | } |
103 | | |
104 | | //////////////////////////////////////////////////////////////////////////////// |
105 | | uint8_t *cjose_concatkdf_derive(const size_t keylen, |
106 | | const uint8_t *ikm, |
107 | | const size_t ikmLen, |
108 | | const uint8_t *otherinfo, |
109 | | const size_t otherinfoLen, |
110 | | cjose_err *err) |
111 | 0 | { |
112 | 0 | uint8_t *derived = NULL; |
113 | |
|
114 | 0 | uint8_t *buffer = NULL; |
115 | 0 | const EVP_MD *dgst = EVP_sha256(); |
116 | 0 | EVP_MD_CTX *ctx = EVP_MD_CTX_create(); |
117 | 0 | if (NULL == ctx) |
118 | 0 | { |
119 | 0 | CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); |
120 | 0 | goto concatkdf_derive_finish; |
121 | 0 | } |
122 | | |
123 | 0 | const size_t hashlen = EVP_MD_size(dgst); |
124 | 0 | const size_t N = (keylen + hashlen - 1) / hashlen; |
125 | 0 | buffer = cjose_get_alloc()(keylen); |
126 | 0 | if (NULL == buffer) |
127 | 0 | { |
128 | 0 | CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); |
129 | 0 | goto concatkdf_derive_finish; |
130 | 0 | } |
131 | | |
132 | 0 | size_t offset = 0; |
133 | 0 | for (int idx = 1; N >= idx; idx++) |
134 | 0 | { |
135 | 0 | uint8_t counter[4]; |
136 | 0 | _apply_uint32(idx, counter); |
137 | |
|
138 | 0 | uint8_t *hash = cjose_get_alloc()(hashlen * sizeof(uint8_t)); |
139 | 0 | if (NULL == hash) |
140 | 0 | { |
141 | 0 | CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); |
142 | 0 | goto concatkdf_derive_finish; |
143 | 0 | } |
144 | | |
145 | 0 | if (1 != EVP_DigestInit_ex(ctx, dgst, NULL) || 1 != EVP_DigestUpdate(ctx, counter, sizeof(counter)) |
146 | 0 | || 1 != EVP_DigestUpdate(ctx, ikm, ikmLen) || 1 != EVP_DigestUpdate(ctx, otherinfo, otherinfoLen) |
147 | 0 | || 1 != EVP_DigestFinal_ex(ctx, hash, NULL)) |
148 | 0 | { |
149 | 0 | _cjose_cleanse_dealloc(hash, hashlen); |
150 | 0 | CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); |
151 | 0 | goto concatkdf_derive_finish; |
152 | 0 | } |
153 | | |
154 | | // copy this digest block into the derived key; the final block may be |
155 | | // shorter than hashlen. offset stays < keylen on every iteration, so the |
156 | | // remaining count (keylen - offset) cannot underflow. |
157 | | // hash holds derived key material; wipe it before returning to the allocator |
158 | 0 | size_t amt = keylen - offset; |
159 | 0 | memcpy(buffer + offset, hash, min_len(hashlen, amt)); |
160 | 0 | _cjose_cleanse_dealloc(hash, hashlen); |
161 | 0 | offset += hashlen; |
162 | 0 | } |
163 | | |
164 | 0 | derived = buffer; |
165 | 0 | buffer = NULL; |
166 | |
|
167 | 0 | concatkdf_derive_finish: |
168 | 0 | EVP_MD_CTX_destroy(ctx); |
169 | 0 | _cjose_cleanse_dealloc(buffer, keylen); |
170 | |
|
171 | 0 | return derived; |
172 | 0 | } |