Coverage Report

Created: 2025-10-10 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/strongswan/src/libstrongswan/credentials/containers/pkcs12.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2013 Tobias Brunner
3
 *
4
 * Copyright (C) secunet Security Networks AG
5
 *
6
 * This program is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU General Public License as published by the
8
 * Free Software Foundation; either version 2 of the License, or (at your
9
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
10
 *
11
 * This program is distributed in the hope that it will be useful, but
12
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14
 * for more details.
15
 */
16
17
#include "pkcs12.h"
18
19
#include <library.h>
20
#include <utils/debug.h>
21
22
/**
23
 * v * ceiling(len/v)
24
 */
25
0
#define PKCS12_LEN(len, v) (((len) + v-1) & ~(v-1))
26
27
/**
28
 * Copy src to dst as many times as possible
29
 */
30
static inline void copy_chunk(chunk_t dst, chunk_t src)
31
0
{
32
0
  size_t i;
33
34
0
  for (i = 0; i < dst.len; i++)
35
0
  {
36
0
    dst.ptr[i] = src.ptr[i % src.len];
37
0
  }
38
0
}
39
40
/**
41
 * Treat two chunks as integers in network order and add them together.
42
 * The result is stored in the first chunk, if the second chunk is longer or the
43
 * result overflows this is ignored.
44
 */
45
static void add_chunks(chunk_t a, chunk_t b)
46
0
{
47
0
  uint16_t sum;
48
0
  uint8_t rem = 0;
49
0
  ssize_t i, j;
50
51
0
  for (i = a.len - 1, j = b.len -1; i >= 0 && j >= 0; i--, j--)
52
0
  {
53
0
    sum = a.ptr[i] + b.ptr[j] + rem;
54
0
    a.ptr[i] = (u_char)sum;
55
0
    rem = sum >> 8;
56
0
  }
57
0
  for (; i >= 0 && rem; i--)
58
0
  {
59
0
    sum = a.ptr[i] + rem;
60
0
    a.ptr[i] = (u_char)sum;
61
0
    rem = sum >> 8;
62
0
  }
63
0
}
64
65
/**
66
 * Do the actual key derivation with the given hasher, password and id.
67
 */
68
static bool derive_key(hash_algorithm_t hash, chunk_t unicode, chunk_t salt,
69
             uint64_t iterations, char id, chunk_t result)
70
0
{
71
0
  chunk_t out = result, D, S, P = chunk_empty, I, Ai, B, Ij;
72
0
  hasher_t *hasher;
73
0
  size_t Slen, v, u;
74
0
  uint64_t i;
75
0
  bool success = FALSE;
76
77
0
  hasher = lib->crypto->create_hasher(lib->crypto, hash);
78
0
  if (!hasher)
79
0
  {
80
0
    DBG1(DBG_ASN, "  %N hash algorithm not available",
81
0
       hash_algorithm_names, hash);
82
0
    return  FALSE;
83
0
  }
84
0
  switch (hash)
85
0
  {
86
0
    case HASH_MD5:
87
0
    case HASH_SHA1:
88
0
    case HASH_SHA224:
89
0
    case HASH_SHA256:
90
0
      v = 64;
91
0
      break;
92
0
    case HASH_SHA384:
93
0
    case HASH_SHA512:
94
0
      v = 128;
95
0
      break;
96
0
    default:
97
0
      goto end;
98
0
  }
99
0
  u = hasher->get_hash_size(hasher);
100
101
0
  D = chunk_alloca(v);
102
0
  memset(D.ptr, id, D.len);
103
104
0
  Slen = PKCS12_LEN(salt.len, v);
105
0
  I = chunk_alloca(Slen + PKCS12_LEN(unicode.len, v));
106
0
  S = chunk_create(I.ptr, Slen);
107
0
  P = chunk_create(I.ptr + Slen, I.len - Slen);
108
0
  copy_chunk(S, salt);
109
0
  copy_chunk(P, unicode);
110
111
0
  Ai = chunk_alloca(u);
112
0
  B = chunk_alloca(v);
113
114
0
  while (TRUE)
115
0
  {
116
0
    if (!hasher->get_hash(hasher, D, NULL) ||
117
0
      !hasher->get_hash(hasher, I, Ai.ptr))
118
0
    {
119
0
      goto end;
120
0
    }
121
0
    for (i = 1; i < iterations; i++)
122
0
    {
123
0
      if (!hasher->get_hash(hasher, Ai, Ai.ptr))
124
0
      {
125
0
        goto end;
126
0
      }
127
0
    }
128
0
    memcpy(out.ptr, Ai.ptr, min(out.len, Ai.len));
129
0
    out = chunk_skip(out, Ai.len);
130
0
    if (!out.len)
131
0
    {
132
0
      break;
133
0
    }
134
0
    copy_chunk(B, Ai);
135
    /* B = B+1 */
136
0
    add_chunks(B, chunk_from_chars(0x01));
137
0
    Ij = chunk_create(I.ptr, v);
138
0
    for (i = 0; i < I.len; i += v, Ij.ptr += v)
139
0
    { /* Ij = Ij + B + 1 */
140
0
      add_chunks(Ij, B);
141
0
    }
142
0
  }
143
0
  success = TRUE;
144
0
end:
145
0
  hasher->destroy(hasher);
146
0
  return success;
147
0
}
148
149
/*
150
 * Described in header
151
 */
152
bool pkcs12_derive_key(hash_algorithm_t hash, chunk_t password, chunk_t salt,
153
             uint64_t iterations, pkcs12_key_type_t type, chunk_t key)
154
0
{
155
0
  chunk_t unicode = chunk_empty;
156
0
  bool success;
157
0
  int i;
158
159
0
  if (password.ptr)
160
0
  { /* convert the password to UTF-16BE (without BOM) with 0 terminator */
161
0
    unicode = chunk_alloca(password.len * 2 + 2);
162
0
    for (i = 0; i < password.len; i++)
163
0
    {
164
0
      unicode.ptr[i * 2] = 0;
165
0
      unicode.ptr[i * 2 + 1] = password.ptr[i];
166
0
    }
167
0
    unicode.ptr[i * 2] = 0;
168
0
    unicode.ptr[i * 2 + 1] = 0;
169
0
  }
170
171
0
  success = derive_key(hash, unicode, salt, iterations, type, key);
172
0
  memwipe(unicode.ptr, unicode.len);
173
0
  return success;
174
0
}