Coverage Report

Created: 2026-09-27 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}