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