19
|
1 |
/*
|
|
2 |
* Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
|
|
3 |
* All rights reserved.
|
|
4 |
* This component and the accompanying materials are made available
|
|
5 |
* under the terms of the License "Eclipse Public License v1.0"
|
|
6 |
* which accompanies this distribution, and is available
|
|
7 |
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
|
|
8 |
*
|
|
9 |
* Initial Contributors:
|
|
10 |
* Nokia Corporation - initial contribution.
|
|
11 |
*
|
|
12 |
* Contributors:
|
|
13 |
*
|
|
14 |
* Description:
|
|
15 |
*
|
|
16 |
*/
|
|
17 |
|
|
18 |
|
|
19 |
#include <hash.h>
|
|
20 |
#include <bigint.h>
|
|
21 |
#include "pkcs12kdf.h"
|
|
22 |
|
|
23 |
|
|
24 |
EXPORT_C HBufC8* PKCS12KDF::GeneratePasswordLC(const TDesC& aDes)
|
|
25 |
/**
|
|
26 |
Convert the supplied string to a byte string, as described
|
|
27 |
in SB.1 of the PKCS 12 v1.0.
|
|
28 |
|
|
29 |
Each character is converted to a big endian two-byte value,
|
|
30 |
and a terminating NULL character is appended to the end.
|
|
31 |
|
|
32 |
@param aDes String to use as password.
|
|
33 |
*/
|
|
34 |
{
|
|
35 |
const TInt len = aDes.Length();
|
|
36 |
HBufC8* pwdBytes = HBufC8::NewMaxLC((len + 1) * 2);
|
|
37 |
TPtr8 pbDes = pwdBytes->Des();
|
|
38 |
|
|
39 |
TInt i = 0;
|
|
40 |
while (i < len)
|
|
41 |
{
|
|
42 |
TUint16 ch = aDes[i];
|
|
43 |
pbDes[i * 2] = ch >> 8;
|
|
44 |
pbDes[(i * 2) + 1] = ch;
|
|
45 |
++i;
|
|
46 |
}
|
|
47 |
pbDes[i * 2] = pbDes[(i * 2) + 1] = 0;
|
|
48 |
|
|
49 |
return pwdBytes;
|
|
50 |
}
|
|
51 |
|
|
52 |
static TInt CeilDiv(TInt aNumerator, TInt aDenominator)
|
|
53 |
/**
|
|
54 |
Utility function returns ceil(aNumerator / aDenominator).
|
|
55 |
|
|
56 |
@param aNumerator The numerator.
|
|
57 |
@param aDenominator Denominator, which cannot be zero.
|
|
58 |
@return ceil(aNumerator / aDenominator)
|
|
59 |
*/
|
|
60 |
{
|
|
61 |
TInt result = aNumerator / aDenominator;
|
|
62 |
if ((aNumerator % aDenominator) > 0)
|
|
63 |
++result;
|
|
64 |
return result;
|
|
65 |
}
|
|
66 |
|
|
67 |
EXPORT_C void PKCS12KDF::DeriveKeyL(
|
|
68 |
TDes8& aKey, TIDByteType aIDType,
|
|
69 |
const TDesC8& aPasswd, const TDesC8& aSalt, const TUint aIterations)
|
|
70 |
/**
|
|
71 |
Generate a key for the supplied password and salt.
|
|
72 |
This implementation uses SHA1 as the hashing algorithm.
|
|
73 |
|
|
74 |
@param aKey Descriptor which will hold key. On entry
|
|
75 |
its length must be set to the expected key length.
|
|
76 |
@param aIDType Whether this function is being called to generate
|
|
77 |
an (en|de)cryption key, an initialization vector,
|
|
78 |
or a key for MAC-ing. See SB.3 of spec.
|
|
79 |
@param aPasswd Password string. To comply with PKCS#12 spec,
|
|
80 |
this must have 2-byte big-endian characters with
|
|
81 |
a terminating null character.
|
|
82 |
@param aSalt Used with aPasswd to generate key.
|
|
83 |
@param aIterations Number of times to call the hash function for
|
|
84 |
each block in the key.
|
|
85 |
|
|
86 |
@panic PKCS#12 16 Password is empty (debug only.)
|
|
87 |
@panic PKCS#12 17 Password does not contain an even number of bytes,
|
|
88 |
and so can't use double-byte characters (debug only.)
|
|
89 |
@panic PKCS#12 18 The final two-byte character is not a null terminator,
|
|
90 |
or a null terminator occurs before the end (debug only.)
|
|
91 |
*/
|
|
92 |
{
|
|
93 |
__ASSERT_DEBUG(aPasswd.Length() >= 2, Panic(EDKEmptyPswd));
|
|
94 |
__ASSERT_DEBUG((aPasswd.Length() % 2) == 0, Panic(EDKOddPswdByteCount));
|
|
95 |
TInt useCharCount = aPasswd.Length() / 2;
|
|
96 |
TPtrC16 pswd16(reinterpret_cast<const TUint16*>(aPasswd.Ptr()), useCharCount);
|
|
97 |
TInt nullPos = pswd16.Locate(L'\0');
|
|
98 |
__ASSERT_DEBUG(nullPos == (useCharCount - 1), Panic(EDKBadNullTerminator));
|
|
99 |
|
|
100 |
// use the same notation as the standard
|
|
101 |
const TUint8 ID = static_cast<TUint8>(aIDType);
|
|
102 |
const TInt u = 160; // chaining variable length for SHA-1
|
|
103 |
const TInt v = 512; // message input length for SHA-1
|
|
104 |
const TInt n = aKey.Length() * 8; // number of bits required in key
|
|
105 |
const TInt p = aPasswd.Length();
|
|
106 |
const TInt s = aSalt.Length();
|
|
107 |
const TInt r = aIterations;
|
|
108 |
|
|
109 |
// (numbered steps are from the standard)
|
|
110 |
// 1. Construct a string, D (the "diversifier"), by concatenating
|
|
111 |
// v/8 copies of ID.
|
|
112 |
const TInt D_LEN = v / 8;
|
|
113 |
HBufC8* D_ = HBufC8::NewMaxLC(D_LEN);
|
|
114 |
TPtr8 D = D_->Des();
|
|
115 |
D.Fill(ID);
|
|
116 |
|
|
117 |
// 2. Concatenate copies of the salt together to create a string S
|
|
118 |
// of length v * ceil(s/v) bits (the final copy of the salt may be
|
|
119 |
// truncated to create S). Note that if the salt is the empty string,
|
|
120 |
// then so is S.
|
|
121 |
const TInt S_OVER_V_CEIL = CeilDiv(s, v);
|
|
122 |
const TInt S_LEN = (v * S_OVER_V_CEIL) / 8;
|
|
123 |
HBufC8* S_ = HBufC8::NewMaxLC(S_LEN);
|
|
124 |
TPtr8 S = S_->Des();
|
|
125 |
S.Repeat(aSalt);
|
|
126 |
|
|
127 |
// 3. Concatenate copies of the password together to create a string P
|
|
128 |
// of length v * ceil(p/v) bits (the final copy of the password may be
|
|
129 |
// truncated to create P). Note that if the password is the empty string
|
|
130 |
// then so is P.
|
|
131 |
const TInt P_OVER_V_CEIL = CeilDiv(p, v);
|
|
132 |
const TInt P_LEN = (v * P_OVER_V_CEIL) / 8;
|
|
133 |
HBufC8* P_ = HBufC8::NewMaxLC(P_LEN);
|
|
134 |
TPtr8 P = P_->Des();
|
|
135 |
P.Repeat(aPasswd);
|
|
136 |
|
|
137 |
// 4. Set I=S||P to be the concatenation of S and P.
|
|
138 |
const TInt I_LEN = S_LEN + P_LEN;
|
|
139 |
HBufC8* I_ = HBufC8::NewLC(I_LEN);
|
|
140 |
TPtr8 I = I_->Des();
|
|
141 |
I.Copy(S);
|
|
142 |
I.Append(P);
|
|
143 |
|
|
144 |
// 5. Set c=ceil(n/u).
|
|
145 |
const TInt c = CeilDiv(n, u);
|
|
146 |
|
|
147 |
// ahead 7: allocate result buffer A
|
|
148 |
// (Each Ai has SHA1_HASH bytes.)
|
|
149 |
HBufC8* A_ = HBufC8::NewLC(c * SHA1_HASH);
|
|
150 |
TPtr8 A = A_->Des();
|
|
151 |
|
|
152 |
// 6. For i=1, 2, ..., c, do the following
|
|
153 |
|
|
154 |
// pre-allocate SHA1 object, DI, and B buffers
|
|
155 |
CSHA1* sha1 = CSHA1::NewL();
|
|
156 |
CleanupStack::PushL(sha1);
|
|
157 |
|
|
158 |
const TInt DI_LEN = D_LEN + I_LEN;
|
|
159 |
HBufC8* DI_ = HBufC8::NewLC(DI_LEN);
|
|
160 |
TPtr8 DI = DI_->Des();
|
|
161 |
|
|
162 |
const TInt B_LEN = v / 8;
|
|
163 |
HBufC8* B_ = HBufC8::NewMaxLC(B_LEN);
|
|
164 |
TPtr8 B = B_->Des();
|
|
165 |
|
|
166 |
for (TInt i = 1; i <= c; ++i)
|
|
167 |
{
|
|
168 |
// 6a) Set Ai = H^r(D||I). (i.e. the rth hash of D||I,
|
|
169 |
// H(H(H(...H(D||I))))
|
|
170 |
DI.Copy(D);
|
|
171 |
DI.Append(I);
|
|
172 |
|
|
173 |
sha1->Reset();
|
|
174 |
TBuf8<SHA1_HASH> Ai(sha1->Final(DI));
|
|
175 |
|
|
176 |
for (TInt iterCount = 2; iterCount <= r; ++iterCount)
|
|
177 |
{
|
|
178 |
Ai.Copy(sha1->Final(Ai));
|
|
179 |
}
|
|
180 |
|
|
181 |
// 6b) Concatenate copies of Ai to create a string B of length
|
|
182 |
// v bits (the final copy of Ai may be truncated to create B).
|
|
183 |
B.Repeat(Ai);
|
|
184 |
|
|
185 |
// 6c) Treating I as a concatenation I0, I1, ..., Ik-1 of
|
|
186 |
// v-bit blocks, where k=ceil(s/v)+ceil(p/v), modify I by
|
|
187 |
// setting Ij=(Ij+B+1) mod 2^v for each j.
|
|
188 |
|
|
189 |
const TInt k = S_OVER_V_CEIL + P_OVER_V_CEIL;
|
|
190 |
for (TInt j = 0; j < k; ++j)
|
|
191 |
{
|
|
192 |
TPtr8 section = I.MidTPtr((v/8) * j, v/8);
|
|
193 |
Process6cL(section, B, v);
|
|
194 |
}
|
|
195 |
|
|
196 |
// 7. Concatenate A1, A2, ..., Ac together to form a pseudo-random
|
|
197 |
// bit string, A.
|
|
198 |
A.Append(Ai);
|
|
199 |
|
|
200 |
// stop building A if already have enough bits for key
|
|
201 |
if (A.Length() >= n / 8)
|
|
202 |
break;
|
|
203 |
}
|
|
204 |
|
|
205 |
// Use the first n bits of A as the output of this entire process.
|
|
206 |
aKey.Copy(A.Left(n / 8));
|
|
207 |
|
|
208 |
CleanupStack::PopAndDestroy(8, D_); // B_, DI_, sha1, A_, I_, P_, S_, D_
|
|
209 |
}
|
|
210 |
|
|
211 |
void PKCS12KDF::Process6cL(TDes8& Ij, const TDesC8& B, TInt v)
|
|
212 |
/**
|
|
213 |
Helper function for DeriveKeyL modifies part of I,
|
|
214 |
as described in step 6c of SB.2.
|
|
215 |
|
|
216 |
@param Ij Section of I (S || P).
|
|
217 |
@param B rth hash of D || I.
|
|
218 |
@param v Number of bits to preserve in result.
|
|
219 |
*/
|
|
220 |
{
|
|
221 |
// 6c) Treating I as a concatenation I0, I1, ..., Ik-1 of
|
|
222 |
// v-bit blocks, where k=ceil(s/v)+ceil(p/v), modify I by
|
|
223 |
// setting Ij=(Ij+B+1) mod 2^v for each j.
|
|
224 |
|
|
225 |
RInteger RI_Ij = RInteger::NewL(Ij);
|
|
226 |
TCleanupItem ciIj = RI_Ij;
|
|
227 |
CleanupStack::PushL(ciIj);
|
|
228 |
|
|
229 |
RInteger RI_B = RInteger::NewL(B);
|
|
230 |
TCleanupItem ciB = RI_B;
|
|
231 |
CleanupStack::PushL(ciB);
|
|
232 |
|
|
233 |
// these additions can leave
|
|
234 |
RI_Ij += RI_B;
|
|
235 |
RI_Ij += 1;
|
|
236 |
|
|
237 |
HBufC8* result = RI_Ij.BufferLC();
|
|
238 |
|
|
239 |
Ij.Zero();
|
|
240 |
TInt resultLen = result->Length();
|
|
241 |
|
|
242 |
TInt bytesToPreserve = v / 8;
|
|
243 |
TInt leadingZeroes = bytesToPreserve - resultLen;
|
|
244 |
if (leadingZeroes <= 0)
|
|
245 |
Ij.Copy(result->Right(bytesToPreserve));
|
|
246 |
else
|
|
247 |
{
|
|
248 |
Ij.FillZ(leadingZeroes);
|
|
249 |
Ij.Append(*result);
|
|
250 |
}
|
|
251 |
|
|
252 |
CleanupStack::PopAndDestroy(3, &RI_Ij); // result, ciB, ciIj
|
|
253 |
}
|
|
254 |
|
|
255 |
#ifdef _DEBUG
|
|
256 |
|
|
257 |
void PKCS12KDF::Panic(PKCS12KDF::TPanic aPanic)
|
|
258 |
/**
|
|
259 |
This function is used in debug builds to halt
|
|
260 |
the current thread when a logic error is detected.
|
|
261 |
|
|
262 |
The current thread is panicked with category "PKCS12KDF"
|
|
263 |
and the supplied reason.
|
|
264 |
|
|
265 |
@param aPanic Converted to numeric value and
|
|
266 |
used for the panic reason.
|
|
267 |
*/
|
|
268 |
{
|
|
269 |
_LIT(KPanicCat, "PKCS12KDF");
|
|
270 |
User::Panic(KPanicCat, aPanic);
|
|
271 |
}
|
|
272 |
|
|
273 |
#endif
|
|
274 |
|