Coverage Report

Created: 2026-09-01 06:14

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
#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
}