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