Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | #include "nsSyncJPAKE.h" |
michael@0 | 6 | #include "mozilla/ModuleUtils.h" |
michael@0 | 7 | #include <pk11pub.h> |
michael@0 | 8 | #include <keyhi.h> |
michael@0 | 9 | #include <pkcs11.h> |
michael@0 | 10 | #include <nscore.h> |
michael@0 | 11 | #include <secmodt.h> |
michael@0 | 12 | #include <secport.h> |
michael@0 | 13 | #include <secerr.h> |
michael@0 | 14 | #include <nsDebug.h> |
michael@0 | 15 | #include <nsError.h> |
michael@0 | 16 | #include <base64.h> |
michael@0 | 17 | #include <nsString.h> |
michael@0 | 18 | |
michael@0 | 19 | using mozilla::fallible_t; |
michael@0 | 20 | |
michael@0 | 21 | static bool |
michael@0 | 22 | hex_from_2char(const unsigned char *c2, unsigned char *byteval) |
michael@0 | 23 | { |
michael@0 | 24 | int i; |
michael@0 | 25 | unsigned char offset; |
michael@0 | 26 | *byteval = 0; |
michael@0 | 27 | for (i=0; i<2; i++) { |
michael@0 | 28 | if (c2[i] >= '0' && c2[i] <= '9') { |
michael@0 | 29 | offset = c2[i] - '0'; |
michael@0 | 30 | *byteval |= offset << 4*(1-i); |
michael@0 | 31 | } else if (c2[i] >= 'a' && c2[i] <= 'f') { |
michael@0 | 32 | offset = c2[i] - 'a'; |
michael@0 | 33 | *byteval |= (offset + 10) << 4*(1-i); |
michael@0 | 34 | } else if (c2[i] >= 'A' && c2[i] <= 'F') { |
michael@0 | 35 | offset = c2[i] - 'A'; |
michael@0 | 36 | *byteval |= (offset + 10) << 4*(1-i); |
michael@0 | 37 | } else { |
michael@0 | 38 | return false; |
michael@0 | 39 | } |
michael@0 | 40 | } |
michael@0 | 41 | return true; |
michael@0 | 42 | } |
michael@0 | 43 | |
michael@0 | 44 | static bool |
michael@0 | 45 | fromHex(const char * str, unsigned char * p, size_t sLen) |
michael@0 | 46 | { |
michael@0 | 47 | size_t i; |
michael@0 | 48 | if (sLen & 1) |
michael@0 | 49 | return false; |
michael@0 | 50 | |
michael@0 | 51 | for (i = 0; i < sLen / 2; ++i) { |
michael@0 | 52 | if (!hex_from_2char((const unsigned char *) str + (2*i), |
michael@0 | 53 | (unsigned char *) p + i)) { |
michael@0 | 54 | return false; |
michael@0 | 55 | } |
michael@0 | 56 | } |
michael@0 | 57 | return true; |
michael@0 | 58 | } |
michael@0 | 59 | |
michael@0 | 60 | static nsresult |
michael@0 | 61 | fromHexString(const nsACString & str, unsigned char * p, size_t pMaxLen) |
michael@0 | 62 | { |
michael@0 | 63 | char * strData = (char *) str.Data(); |
michael@0 | 64 | unsigned len = str.Length(); |
michael@0 | 65 | NS_ENSURE_ARG(len / 2 <= pMaxLen); |
michael@0 | 66 | if (!fromHex(strData, p, len)) { |
michael@0 | 67 | return NS_ERROR_INVALID_ARG; |
michael@0 | 68 | } |
michael@0 | 69 | return NS_OK; |
michael@0 | 70 | } |
michael@0 | 71 | |
michael@0 | 72 | static bool |
michael@0 | 73 | toHexString(const unsigned char * str, unsigned len, nsACString & out) |
michael@0 | 74 | { |
michael@0 | 75 | static const char digits[] = "0123456789ABCDEF"; |
michael@0 | 76 | if (!out.SetCapacity(2 * len, fallible_t())) |
michael@0 | 77 | return false; |
michael@0 | 78 | out.SetLength(0); |
michael@0 | 79 | for (unsigned i = 0; i < len; ++i) { |
michael@0 | 80 | out.Append(digits[str[i] >> 4]); |
michael@0 | 81 | out.Append(digits[str[i] & 0x0f]); |
michael@0 | 82 | } |
michael@0 | 83 | return true; |
michael@0 | 84 | } |
michael@0 | 85 | |
michael@0 | 86 | static nsresult |
michael@0 | 87 | mapErrno() |
michael@0 | 88 | { |
michael@0 | 89 | int err = PORT_GetError(); |
michael@0 | 90 | switch (err) { |
michael@0 | 91 | case SEC_ERROR_NO_MEMORY: return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 92 | default: return NS_ERROR_UNEXPECTED; |
michael@0 | 93 | } |
michael@0 | 94 | } |
michael@0 | 95 | |
michael@0 | 96 | #define NUM_ELEM(x) (sizeof(x) / sizeof (x)[0]) |
michael@0 | 97 | |
michael@0 | 98 | static const char p[] = |
michael@0 | 99 | "90066455B5CFC38F9CAA4A48B4281F292C260FEEF01FD61037E56258A7795A1C" |
michael@0 | 100 | "7AD46076982CE6BB956936C6AB4DCFE05E6784586940CA544B9B2140E1EB523F" |
michael@0 | 101 | "009D20A7E7880E4E5BFA690F1B9004A27811CD9904AF70420EEFD6EA11EF7DA1" |
michael@0 | 102 | "29F58835FF56B89FAA637BC9AC2EFAAB903402229F491D8D3485261CD068699B" |
michael@0 | 103 | "6BA58A1DDBBEF6DB51E8FE34E8A78E542D7BA351C21EA8D8F1D29F5D5D159394" |
michael@0 | 104 | "87E27F4416B0CA632C59EFD1B1EB66511A5A0FBF615B766C5862D0BD8A3FE7A0" |
michael@0 | 105 | "E0DA0FB2FE1FCB19E8F9996A8EA0FCCDE538175238FC8B0EE6F29AF7F642773E" |
michael@0 | 106 | "BE8CD5402415A01451A840476B2FCEB0E388D30D4B376C37FE401C2A2C2F941D" |
michael@0 | 107 | "AD179C540C1C8CE030D460C4D983BE9AB0B20F69144C1AE13F9383EA1C08504F" |
michael@0 | 108 | "B0BF321503EFE43488310DD8DC77EC5B8349B8BFE97C2C560EA878DE87C11E3D" |
michael@0 | 109 | "597F1FEA742D73EEC7F37BE43949EF1A0D15C3F3E3FC0A8335617055AC91328E" |
michael@0 | 110 | "C22B50FC15B941D3D1624CD88BC25F3E941FDDC6200689581BFEC416B4B2CB73"; |
michael@0 | 111 | static const char q[] = |
michael@0 | 112 | "CFA0478A54717B08CE64805B76E5B14249A77A4838469DF7F7DC987EFCCFB11D"; |
michael@0 | 113 | static const char g[] = |
michael@0 | 114 | "5E5CBA992E0A680D885EB903AEA78E4A45A469103D448EDE3B7ACCC54D521E37" |
michael@0 | 115 | "F84A4BDD5B06B0970CC2D2BBB715F7B82846F9A0C393914C792E6A923E2117AB" |
michael@0 | 116 | "805276A975AADB5261D91673EA9AAFFEECBFA6183DFCB5D3B7332AA19275AFA1" |
michael@0 | 117 | "F8EC0B60FB6F66CC23AE4870791D5982AAD1AA9485FD8F4A60126FEB2CF05DB8" |
michael@0 | 118 | "A7F0F09B3397F3937F2E90B9E5B9C9B6EFEF642BC48351C46FB171B9BFA9EF17" |
michael@0 | 119 | "A961CE96C7E7A7CC3D3D03DFAD1078BA21DA425198F07D2481622BCE45969D9C" |
michael@0 | 120 | "4D6063D72AB7A0F08B2F49A7CC6AF335E08C4720E31476B67299E231F8BD90B3" |
michael@0 | 121 | "9AC3AE3BE0C6B6CACEF8289A2E2873D58E51E029CAFBD55E6841489AB66B5B4B" |
michael@0 | 122 | "9BA6E2F784660896AFF387D92844CCB8B69475496DE19DA2E58259B090489AC8" |
michael@0 | 123 | "E62363CDF82CFD8EF2A427ABCD65750B506F56DDE3B988567A88126B914D7828" |
michael@0 | 124 | "E2B63A6D7ED0747EC59E0E0A23CE7D8A74C1D2C2A7AFB6A29799620F00E11C33" |
michael@0 | 125 | "787F7DED3B30E1A22D09F1FBDA1ABBBFBF25CAE05A13F812E34563F99410E73B"; |
michael@0 | 126 | |
michael@0 | 127 | NS_IMETHODIMP nsSyncJPAKE::Round1(const nsACString & aSignerID, |
michael@0 | 128 | nsACString & aGX1, |
michael@0 | 129 | nsACString & aGV1, |
michael@0 | 130 | nsACString & aR1, |
michael@0 | 131 | nsACString & aGX2, |
michael@0 | 132 | nsACString & aGV2, |
michael@0 | 133 | nsACString & aR2) |
michael@0 | 134 | { |
michael@0 | 135 | NS_ENSURE_STATE(round == JPAKENotStarted); |
michael@0 | 136 | NS_ENSURE_STATE(key == nullptr); |
michael@0 | 137 | |
michael@0 | 138 | static CK_MECHANISM_TYPE mechanisms[] = { |
michael@0 | 139 | CKM_NSS_JPAKE_ROUND1_SHA256, |
michael@0 | 140 | CKM_NSS_JPAKE_ROUND2_SHA256, |
michael@0 | 141 | CKM_NSS_JPAKE_FINAL_SHA256 |
michael@0 | 142 | }; |
michael@0 | 143 | |
michael@0 | 144 | PK11SlotInfo * slot = PK11_GetBestSlotMultiple(mechanisms, |
michael@0 | 145 | NUM_ELEM(mechanisms), |
michael@0 | 146 | nullptr); |
michael@0 | 147 | NS_ENSURE_STATE(slot != nullptr); |
michael@0 | 148 | |
michael@0 | 149 | CK_BYTE pBuf[(NUM_ELEM(p) - 1) / 2]; |
michael@0 | 150 | CK_BYTE qBuf[(NUM_ELEM(q) - 1) / 2]; |
michael@0 | 151 | CK_BYTE gBuf[(NUM_ELEM(g) - 1) / 2]; |
michael@0 | 152 | |
michael@0 | 153 | CK_KEY_TYPE keyType = CKK_NSS_JPAKE_ROUND1; |
michael@0 | 154 | NS_ENSURE_STATE(fromHex(p, pBuf, (NUM_ELEM(p) - 1))); |
michael@0 | 155 | NS_ENSURE_STATE(fromHex(q, qBuf, (NUM_ELEM(q) - 1))); |
michael@0 | 156 | NS_ENSURE_STATE(fromHex(g, gBuf, (NUM_ELEM(g) - 1))); |
michael@0 | 157 | CK_ATTRIBUTE keyTemplate[] = { |
michael@0 | 158 | { CKA_NSS_JPAKE_SIGNERID, (CK_BYTE *) aSignerID.Data(), |
michael@0 | 159 | aSignerID.Length() }, |
michael@0 | 160 | { CKA_KEY_TYPE, &keyType, sizeof keyType }, |
michael@0 | 161 | { CKA_PRIME, pBuf, sizeof pBuf }, |
michael@0 | 162 | { CKA_SUBPRIME, qBuf, sizeof qBuf }, |
michael@0 | 163 | { CKA_BASE, gBuf, sizeof gBuf } |
michael@0 | 164 | }; |
michael@0 | 165 | |
michael@0 | 166 | CK_BYTE gx1Buf[NUM_ELEM(p) / 2]; |
michael@0 | 167 | CK_BYTE gv1Buf[NUM_ELEM(p) / 2]; |
michael@0 | 168 | CK_BYTE r1Buf [NUM_ELEM(p) / 2]; |
michael@0 | 169 | CK_BYTE gx2Buf[NUM_ELEM(p) / 2]; |
michael@0 | 170 | CK_BYTE gv2Buf[NUM_ELEM(p) / 2]; |
michael@0 | 171 | CK_BYTE r2Buf [NUM_ELEM(p) / 2]; |
michael@0 | 172 | CK_NSS_JPAKERound1Params rp = { |
michael@0 | 173 | { gx1Buf, sizeof gx1Buf, gv1Buf, sizeof gv1Buf, r1Buf, sizeof r1Buf }, |
michael@0 | 174 | { gx2Buf, sizeof gx2Buf, gv2Buf, sizeof gv2Buf, r2Buf, sizeof r2Buf } |
michael@0 | 175 | }; |
michael@0 | 176 | SECItem paramsItem; |
michael@0 | 177 | paramsItem.data = (unsigned char *) &rp; |
michael@0 | 178 | paramsItem.len = sizeof rp; |
michael@0 | 179 | key = PK11_KeyGenWithTemplate(slot, CKM_NSS_JPAKE_ROUND1_SHA256, |
michael@0 | 180 | CKM_NSS_JPAKE_ROUND1_SHA256, |
michael@0 | 181 | ¶msItem, keyTemplate, |
michael@0 | 182 | NUM_ELEM(keyTemplate), nullptr); |
michael@0 | 183 | nsresult rv = key != nullptr |
michael@0 | 184 | ? NS_OK |
michael@0 | 185 | : mapErrno(); |
michael@0 | 186 | if (rv == NS_OK) { |
michael@0 | 187 | NS_ENSURE_TRUE(toHexString(rp.gx1.pGX, rp.gx1.ulGXLen, aGX1) && |
michael@0 | 188 | toHexString(rp.gx1.pGV, rp.gx1.ulGVLen, aGV1) && |
michael@0 | 189 | toHexString(rp.gx1.pR, rp.gx1.ulRLen, aR1) && |
michael@0 | 190 | toHexString(rp.gx2.pGX, rp.gx2.ulGXLen, aGX2) && |
michael@0 | 191 | toHexString(rp.gx2.pGV, rp.gx2.ulGVLen, aGV2) && |
michael@0 | 192 | toHexString(rp.gx2.pR, rp.gx2.ulRLen, aR2), |
michael@0 | 193 | NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 194 | round = JPAKEBeforeRound2; |
michael@0 | 195 | } |
michael@0 | 196 | return rv; |
michael@0 | 197 | } |
michael@0 | 198 | |
michael@0 | 199 | NS_IMETHODIMP nsSyncJPAKE::Round2(const nsACString & aPeerID, |
michael@0 | 200 | const nsACString & aPIN, |
michael@0 | 201 | const nsACString & aGX3, |
michael@0 | 202 | const nsACString & aGV3, |
michael@0 | 203 | const nsACString & aR3, |
michael@0 | 204 | const nsACString & aGX4, |
michael@0 | 205 | const nsACString & aGV4, |
michael@0 | 206 | const nsACString & aR4, |
michael@0 | 207 | nsACString & aA, |
michael@0 | 208 | nsACString & aGVA, |
michael@0 | 209 | nsACString & aRA) |
michael@0 | 210 | { |
michael@0 | 211 | NS_ENSURE_STATE(round == JPAKEBeforeRound2); |
michael@0 | 212 | NS_ENSURE_STATE(key != nullptr); |
michael@0 | 213 | NS_ENSURE_ARG(!aPeerID.IsEmpty()); |
michael@0 | 214 | |
michael@0 | 215 | /* PIN cannot be equal to zero when converted to a bignum. NSS 3.12.9 J-PAKE |
michael@0 | 216 | assumes that the caller has already done this check. Future versions of |
michael@0 | 217 | NSS J-PAKE will do this check internally. See Bug 609068 Comment 4 */ |
michael@0 | 218 | bool foundNonZero = false; |
michael@0 | 219 | for (size_t i = 0; i < aPIN.Length(); ++i) { |
michael@0 | 220 | if (aPIN[i] != 0) { |
michael@0 | 221 | foundNonZero = true; |
michael@0 | 222 | break; |
michael@0 | 223 | } |
michael@0 | 224 | } |
michael@0 | 225 | NS_ENSURE_ARG(foundNonZero); |
michael@0 | 226 | |
michael@0 | 227 | CK_BYTE gx3Buf[NUM_ELEM(p)/2], gv3Buf[NUM_ELEM(p)/2], r3Buf [NUM_ELEM(p)/2]; |
michael@0 | 228 | CK_BYTE gx4Buf[NUM_ELEM(p)/2], gv4Buf[NUM_ELEM(p)/2], r4Buf [NUM_ELEM(p)/2]; |
michael@0 | 229 | CK_BYTE gxABuf[NUM_ELEM(p)/2], gvABuf[NUM_ELEM(p)/2], rABuf [NUM_ELEM(p)/2]; |
michael@0 | 230 | nsresult rv = fromHexString(aGX3, gx3Buf, sizeof gx3Buf); |
michael@0 | 231 | if (rv == NS_OK) rv = fromHexString(aGV3, gv3Buf, sizeof gv3Buf); |
michael@0 | 232 | if (rv == NS_OK) rv = fromHexString(aR3, r3Buf, sizeof r3Buf); |
michael@0 | 233 | if (rv == NS_OK) rv = fromHexString(aGX4, gx4Buf, sizeof gx4Buf); |
michael@0 | 234 | if (rv == NS_OK) rv = fromHexString(aGV4, gv4Buf, sizeof gv4Buf); |
michael@0 | 235 | if (rv == NS_OK) rv = fromHexString(aR4, r4Buf, sizeof r4Buf); |
michael@0 | 236 | if (rv != NS_OK) |
michael@0 | 237 | return rv; |
michael@0 | 238 | |
michael@0 | 239 | CK_NSS_JPAKERound2Params rp; |
michael@0 | 240 | rp.pSharedKey = (CK_BYTE *) aPIN.Data(); |
michael@0 | 241 | rp.ulSharedKeyLen = aPIN.Length(); |
michael@0 | 242 | rp.gx3.pGX = gx3Buf; rp.gx3.ulGXLen = aGX3.Length() / 2; |
michael@0 | 243 | rp.gx3.pGV = gv3Buf; rp.gx3.ulGVLen = aGV3.Length() / 2; |
michael@0 | 244 | rp.gx3.pR = r3Buf; rp.gx3.ulRLen = aR3 .Length() / 2; |
michael@0 | 245 | rp.gx4.pGX = gx4Buf; rp.gx4.ulGXLen = aGX4.Length() / 2; |
michael@0 | 246 | rp.gx4.pGV = gv4Buf; rp.gx4.ulGVLen = aGV4.Length() / 2; |
michael@0 | 247 | rp.gx4.pR = r4Buf; rp.gx4.ulRLen = aR4 .Length() / 2; |
michael@0 | 248 | rp.A.pGX = gxABuf; rp.A .ulGXLen = sizeof gxABuf; |
michael@0 | 249 | rp.A.pGV = gvABuf; rp.A .ulGVLen = sizeof gxABuf; |
michael@0 | 250 | rp.A.pR = rABuf; rp.A .ulRLen = sizeof gxABuf; |
michael@0 | 251 | |
michael@0 | 252 | // Bug 629090: NSS 3.12.9 J-PAKE fails to check that gx^4 != 1, so check here. |
michael@0 | 253 | bool gx4Good = false; |
michael@0 | 254 | for (unsigned i = 0; i < rp.gx4.ulGXLen; ++i) { |
michael@0 | 255 | if (rp.gx4.pGX[i] > 1 || (rp.gx4.pGX[i] != 0 && i < rp.gx4.ulGXLen - 1)) { |
michael@0 | 256 | gx4Good = true; |
michael@0 | 257 | break; |
michael@0 | 258 | } |
michael@0 | 259 | } |
michael@0 | 260 | NS_ENSURE_ARG(gx4Good); |
michael@0 | 261 | |
michael@0 | 262 | SECItem paramsItem; |
michael@0 | 263 | paramsItem.data = (unsigned char *) &rp; |
michael@0 | 264 | paramsItem.len = sizeof rp; |
michael@0 | 265 | CK_KEY_TYPE keyType = CKK_NSS_JPAKE_ROUND2; |
michael@0 | 266 | CK_ATTRIBUTE keyTemplate[] = { |
michael@0 | 267 | { CKA_NSS_JPAKE_PEERID, (CK_BYTE *) aPeerID.Data(), aPeerID.Length(), }, |
michael@0 | 268 | { CKA_KEY_TYPE, &keyType, sizeof keyType } |
michael@0 | 269 | }; |
michael@0 | 270 | PK11SymKey * newKey = PK11_DeriveWithTemplate(key, |
michael@0 | 271 | CKM_NSS_JPAKE_ROUND2_SHA256, |
michael@0 | 272 | ¶msItem, |
michael@0 | 273 | CKM_NSS_JPAKE_FINAL_SHA256, |
michael@0 | 274 | CKA_DERIVE, 0, |
michael@0 | 275 | keyTemplate, |
michael@0 | 276 | NUM_ELEM(keyTemplate), |
michael@0 | 277 | false); |
michael@0 | 278 | if (newKey != nullptr) { |
michael@0 | 279 | if (toHexString(rp.A.pGX, rp.A.ulGXLen, aA) && |
michael@0 | 280 | toHexString(rp.A.pGV, rp.A.ulGVLen, aGVA) && |
michael@0 | 281 | toHexString(rp.A.pR, rp.A.ulRLen, aRA)) { |
michael@0 | 282 | round = JPAKEAfterRound2; |
michael@0 | 283 | PK11_FreeSymKey(key); |
michael@0 | 284 | key = newKey; |
michael@0 | 285 | return NS_OK; |
michael@0 | 286 | } else { |
michael@0 | 287 | PK11_FreeSymKey(newKey); |
michael@0 | 288 | rv = NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 289 | } |
michael@0 | 290 | } else |
michael@0 | 291 | rv = mapErrno(); |
michael@0 | 292 | |
michael@0 | 293 | return rv; |
michael@0 | 294 | } |
michael@0 | 295 | |
michael@0 | 296 | static nsresult |
michael@0 | 297 | setBase64(const unsigned char * data, unsigned len, nsACString & out) |
michael@0 | 298 | { |
michael@0 | 299 | nsresult rv = NS_OK; |
michael@0 | 300 | const char * base64 = BTOA_DataToAscii(data, len); |
michael@0 | 301 | |
michael@0 | 302 | if (base64 != nullptr) { |
michael@0 | 303 | size_t len = PORT_Strlen(base64); |
michael@0 | 304 | if (out.SetCapacity(len, fallible_t())) { |
michael@0 | 305 | out.SetLength(0); |
michael@0 | 306 | out.Append(base64, len); |
michael@0 | 307 | PORT_Free((void*) base64); |
michael@0 | 308 | } else { |
michael@0 | 309 | rv = NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 310 | } |
michael@0 | 311 | } else { |
michael@0 | 312 | rv = NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 313 | } |
michael@0 | 314 | return rv; |
michael@0 | 315 | } |
michael@0 | 316 | |
michael@0 | 317 | static nsresult |
michael@0 | 318 | base64KeyValue(PK11SymKey * key, nsACString & keyString) |
michael@0 | 319 | { |
michael@0 | 320 | nsresult rv = NS_OK; |
michael@0 | 321 | if (PK11_ExtractKeyValue(key) == SECSuccess) { |
michael@0 | 322 | const SECItem * value = PK11_GetKeyData(key); |
michael@0 | 323 | rv = value != nullptr && value->data != nullptr && value->len > 0 |
michael@0 | 324 | ? setBase64(value->data, value->len, keyString) |
michael@0 | 325 | : NS_ERROR_UNEXPECTED; |
michael@0 | 326 | } else { |
michael@0 | 327 | rv = mapErrno(); |
michael@0 | 328 | } |
michael@0 | 329 | return rv; |
michael@0 | 330 | } |
michael@0 | 331 | |
michael@0 | 332 | static nsresult |
michael@0 | 333 | extractBase64KeyValue(PK11SymKey * keyBlock, CK_ULONG bitPosition, |
michael@0 | 334 | CK_MECHANISM_TYPE destMech, int keySize, |
michael@0 | 335 | nsACString & keyString) |
michael@0 | 336 | { |
michael@0 | 337 | SECItem paramsItem; |
michael@0 | 338 | paramsItem.data = (CK_BYTE *) &bitPosition; |
michael@0 | 339 | paramsItem.len = sizeof bitPosition; |
michael@0 | 340 | PK11SymKey * key = PK11_Derive(keyBlock, CKM_EXTRACT_KEY_FROM_KEY, |
michael@0 | 341 | ¶msItem, destMech, |
michael@0 | 342 | CKA_SIGN, keySize); |
michael@0 | 343 | if (key == nullptr) |
michael@0 | 344 | return mapErrno(); |
michael@0 | 345 | nsresult rv = base64KeyValue(key, keyString); |
michael@0 | 346 | PK11_FreeSymKey(key); |
michael@0 | 347 | return rv; |
michael@0 | 348 | } |
michael@0 | 349 | |
michael@0 | 350 | |
michael@0 | 351 | NS_IMETHODIMP nsSyncJPAKE::Final(const nsACString & aB, |
michael@0 | 352 | const nsACString & aGVB, |
michael@0 | 353 | const nsACString & aRB, |
michael@0 | 354 | const nsACString & aHKDFInfo, |
michael@0 | 355 | nsACString & aAES256Key, |
michael@0 | 356 | nsACString & aHMAC256Key) |
michael@0 | 357 | { |
michael@0 | 358 | static const unsigned AES256_KEY_SIZE = 256 / 8; |
michael@0 | 359 | static const unsigned HMAC_SHA256_KEY_SIZE = 256 / 8; |
michael@0 | 360 | CK_EXTRACT_PARAMS aesBitPosition = 0; |
michael@0 | 361 | CK_EXTRACT_PARAMS hmacBitPosition = aesBitPosition + (AES256_KEY_SIZE * 8); |
michael@0 | 362 | |
michael@0 | 363 | NS_ENSURE_STATE(round == JPAKEAfterRound2); |
michael@0 | 364 | NS_ENSURE_STATE(key != nullptr); |
michael@0 | 365 | |
michael@0 | 366 | CK_BYTE gxBBuf[NUM_ELEM(p)/2], gvBBuf[NUM_ELEM(p)/2], rBBuf [NUM_ELEM(p)/2]; |
michael@0 | 367 | nsresult rv = fromHexString(aB, gxBBuf, sizeof gxBBuf); |
michael@0 | 368 | if (rv == NS_OK) rv = fromHexString(aGVB, gvBBuf, sizeof gvBBuf); |
michael@0 | 369 | if (rv == NS_OK) rv = fromHexString(aRB, rBBuf, sizeof rBBuf); |
michael@0 | 370 | if (rv != NS_OK) |
michael@0 | 371 | return rv; |
michael@0 | 372 | |
michael@0 | 373 | CK_NSS_JPAKEFinalParams rp; |
michael@0 | 374 | rp.B.pGX = gxBBuf; rp.B.ulGXLen = aB .Length() / 2; |
michael@0 | 375 | rp.B.pGV = gvBBuf; rp.B.ulGVLen = aGVB.Length() / 2; |
michael@0 | 376 | rp.B.pR = rBBuf; rp.B.ulRLen = aRB .Length() / 2; |
michael@0 | 377 | SECItem paramsItem; |
michael@0 | 378 | paramsItem.data = (unsigned char *) &rp; |
michael@0 | 379 | paramsItem.len = sizeof rp; |
michael@0 | 380 | PK11SymKey * keyMaterial = PK11_Derive(key, CKM_NSS_JPAKE_FINAL_SHA256, |
michael@0 | 381 | ¶msItem, CKM_NSS_HKDF_SHA256, |
michael@0 | 382 | CKA_DERIVE, 0); |
michael@0 | 383 | PK11SymKey * keyBlock = nullptr; |
michael@0 | 384 | |
michael@0 | 385 | if (keyMaterial == nullptr) |
michael@0 | 386 | rv = mapErrno(); |
michael@0 | 387 | |
michael@0 | 388 | if (rv == NS_OK) { |
michael@0 | 389 | CK_NSS_HKDFParams hkdfParams; |
michael@0 | 390 | hkdfParams.bExtract = CK_TRUE; |
michael@0 | 391 | hkdfParams.pSalt = nullptr; |
michael@0 | 392 | hkdfParams.ulSaltLen = 0; |
michael@0 | 393 | hkdfParams.bExpand = CK_TRUE; |
michael@0 | 394 | hkdfParams.pInfo = (CK_BYTE *) aHKDFInfo.Data(); |
michael@0 | 395 | hkdfParams.ulInfoLen = aHKDFInfo.Length(); |
michael@0 | 396 | paramsItem.data = (unsigned char *) &hkdfParams; |
michael@0 | 397 | paramsItem.len = sizeof hkdfParams; |
michael@0 | 398 | keyBlock = PK11_Derive(keyMaterial, CKM_NSS_HKDF_SHA256, |
michael@0 | 399 | ¶msItem, CKM_EXTRACT_KEY_FROM_KEY, |
michael@0 | 400 | CKA_DERIVE, AES256_KEY_SIZE + HMAC_SHA256_KEY_SIZE); |
michael@0 | 401 | if (keyBlock == nullptr) |
michael@0 | 402 | rv = mapErrno(); |
michael@0 | 403 | } |
michael@0 | 404 | |
michael@0 | 405 | if (rv == NS_OK) { |
michael@0 | 406 | rv = extractBase64KeyValue(keyBlock, aesBitPosition, CKM_AES_CBC, |
michael@0 | 407 | AES256_KEY_SIZE, aAES256Key); |
michael@0 | 408 | } |
michael@0 | 409 | if (rv == NS_OK) { |
michael@0 | 410 | rv = extractBase64KeyValue(keyBlock, hmacBitPosition, CKM_SHA256_HMAC, |
michael@0 | 411 | HMAC_SHA256_KEY_SIZE, aHMAC256Key); |
michael@0 | 412 | } |
michael@0 | 413 | |
michael@0 | 414 | if (rv == NS_OK) { |
michael@0 | 415 | SECStatus srv = PK11_ExtractKeyValue(keyMaterial); |
michael@0 | 416 | NS_ENSURE_TRUE(srv == SECSuccess, NS_ERROR_UNEXPECTED); // XXX leaks |
michael@0 | 417 | SECItem * keyMaterialBytes = PK11_GetKeyData(keyMaterial); |
michael@0 | 418 | NS_ENSURE_TRUE(keyMaterialBytes != nullptr, NS_ERROR_UNEXPECTED); |
michael@0 | 419 | } |
michael@0 | 420 | |
michael@0 | 421 | if (keyBlock != nullptr) |
michael@0 | 422 | PK11_FreeSymKey(keyBlock); |
michael@0 | 423 | if (keyMaterial != nullptr) |
michael@0 | 424 | PK11_FreeSymKey(keyMaterial); |
michael@0 | 425 | |
michael@0 | 426 | return rv; |
michael@0 | 427 | } |
michael@0 | 428 | |
michael@0 | 429 | NS_GENERIC_FACTORY_CONSTRUCTOR(nsSyncJPAKE) |
michael@0 | 430 | NS_DEFINE_NAMED_CID(NS_SYNCJPAKE_CID); |
michael@0 | 431 | |
michael@0 | 432 | nsSyncJPAKE::nsSyncJPAKE() : round(JPAKENotStarted), key(nullptr) { } |
michael@0 | 433 | |
michael@0 | 434 | nsSyncJPAKE::~nsSyncJPAKE() |
michael@0 | 435 | { |
michael@0 | 436 | if (key != nullptr) |
michael@0 | 437 | PK11_FreeSymKey(key); |
michael@0 | 438 | } |
michael@0 | 439 | |
michael@0 | 440 | static const mozilla::Module::CIDEntry kServicesCryptoCIDs[] = { |
michael@0 | 441 | { &kNS_SYNCJPAKE_CID, false, nullptr, nsSyncJPAKEConstructor }, |
michael@0 | 442 | { nullptr } |
michael@0 | 443 | }; |
michael@0 | 444 | |
michael@0 | 445 | static const mozilla::Module::ContractIDEntry kServicesCryptoContracts[] = { |
michael@0 | 446 | { NS_SYNCJPAKE_CONTRACTID, &kNS_SYNCJPAKE_CID }, |
michael@0 | 447 | { nullptr } |
michael@0 | 448 | }; |
michael@0 | 449 | |
michael@0 | 450 | static const mozilla::Module kServicesCryptoModule = { |
michael@0 | 451 | mozilla::Module::kVersion, |
michael@0 | 452 | kServicesCryptoCIDs, |
michael@0 | 453 | kServicesCryptoContracts |
michael@0 | 454 | }; |
michael@0 | 455 | |
michael@0 | 456 | NSMODULE_DEFN(nsServicesCryptoModule) = &kServicesCryptoModule; |