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