1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/lib/pk11wrap/pk11pqg.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,512 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 +/* Thse functions are stub functions which will get replaced with calls through 1.8 + * PKCS #11. 1.9 + */ 1.10 + 1.11 +#include "pk11func.h" 1.12 +#include "secmod.h" 1.13 +#include "secmodi.h" 1.14 +#include "secmodti.h" 1.15 +#include "pkcs11t.h" 1.16 +#include "pk11pqg.h" 1.17 +#include "secerr.h" 1.18 + 1.19 + 1.20 +/* Generate PQGParams and PQGVerify structs. 1.21 + * Length of P specified by L. 1.22 + * if L is greater than 1024 then the resulting verify parameters will be 1.23 + * DSA2. 1.24 + * Length of Q specified by N. If zero, The PKCS #11 module will 1.25 + * pick an appropriately sized Q for P. If N is specified and L = 1024, then 1.26 + * the resulting verify parameters will be DSA2, Otherwise DSA1 parameters 1.27 + * will be returned. 1.28 + * Length of SEED in bytes specified in seedBytes. 1.29 + * 1.30 + * The underlying PKCS #11 module will check the values for L, N, 1.31 + * and seedBytes. The rules for softoken are: 1.32 + * 1.33 + * If L <= 1024, then L must be between 512 and 1024 in increments of 64 bits. 1.34 + * If L <= 1024, then N must be 0 or 160. 1.35 + * If L >= 1024, then L and N must match the following table: 1.36 + * L=1024 N=0 or 160 1.37 + * L=2048 N=0 or 224 1.38 + * L=2048 N=256 1.39 + * L=3072 N=0 or 256 1.40 + * if L <= 1024 1.41 + * seedBbytes must be in the range [20..256]. 1.42 + * if L >= 1024 1.43 + * seedBbytes must be in the range [20..L/16]. 1.44 + */ 1.45 +extern SECStatus 1.46 +PK11_PQG_ParamGenV2(unsigned int L, unsigned int N, 1.47 + unsigned int seedBytes, PQGParams **pParams, PQGVerify **pVfy) 1.48 +{ 1.49 + PK11SlotInfo *slot = NULL; 1.50 + CK_ATTRIBUTE genTemplate[5]; 1.51 + CK_ATTRIBUTE *attrs = genTemplate; 1.52 + int count = sizeof(genTemplate)/sizeof(genTemplate[0]); 1.53 + CK_MECHANISM mechanism; 1.54 + CK_OBJECT_HANDLE objectID = CK_INVALID_HANDLE; 1.55 + CK_RV crv; 1.56 + CK_ATTRIBUTE pTemplate[] = { 1.57 + { CKA_PRIME, NULL, 0 }, 1.58 + { CKA_SUBPRIME, NULL, 0 }, 1.59 + { CKA_BASE, NULL, 0 }, 1.60 + }; 1.61 + CK_ATTRIBUTE vTemplate[] = { 1.62 + { CKA_NETSCAPE_PQG_COUNTER, NULL, 0 }, 1.63 + { CKA_NETSCAPE_PQG_SEED, NULL, 0 }, 1.64 + { CKA_NETSCAPE_PQG_H, NULL, 0 }, 1.65 + }; 1.66 + CK_ULONG primeBits = L; 1.67 + CK_ULONG subPrimeBits = N; 1.68 + int pTemplateCount = sizeof(pTemplate)/sizeof(pTemplate[0]); 1.69 + int vTemplateCount = sizeof(vTemplate)/sizeof(vTemplate[0]); 1.70 + PLArenaPool *parena = NULL; 1.71 + PLArenaPool *varena = NULL; 1.72 + PQGParams *params = NULL; 1.73 + PQGVerify *verify = NULL; 1.74 + CK_ULONG seedBits = seedBytes*8; 1.75 + 1.76 + *pParams = NULL; 1.77 + *pVfy = NULL; 1.78 + 1.79 + if (primeBits == (CK_ULONG)-1) { 1.80 + PORT_SetError(SEC_ERROR_INVALID_ARGS); 1.81 + goto loser; 1.82 + } 1.83 + PK11_SETATTRS(attrs, CKA_PRIME_BITS,&primeBits,sizeof(primeBits)); attrs++; 1.84 + if (subPrimeBits != 0) { 1.85 + PK11_SETATTRS(attrs, CKA_SUB_PRIME_BITS, 1.86 + &subPrimeBits, sizeof(subPrimeBits)); attrs++; 1.87 + } 1.88 + if (seedBits != 0) { 1.89 + PK11_SETATTRS(attrs, CKA_NETSCAPE_PQG_SEED_BITS, 1.90 + &seedBits, sizeof(seedBits)); attrs++; 1.91 + } 1.92 + count = attrs - genTemplate; 1.93 + PR_ASSERT(count <= sizeof(genTemplate)/sizeof(CK_ATTRIBUTE)); 1.94 + 1.95 + slot = PK11_GetInternalSlot(); 1.96 + if (slot == NULL) { 1.97 + /* set error */ 1.98 + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);/* shouldn't happen */ 1.99 + goto loser; 1.100 + } 1.101 + 1.102 + /* make sure the internal slot can handle DSA2 type parameters. */ 1.103 + if (primeBits > 1024) { 1.104 + CK_MECHANISM_INFO mechanism_info; 1.105 + 1.106 + if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot); 1.107 + crv = PK11_GETTAB(slot)->C_GetMechanismInfo(slot->slotID, 1.108 + CKM_DSA_PARAMETER_GEN, &mechanism_info); 1.109 + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); 1.110 + /* a bug in the old softoken left CKM_DSA_PARAMETER_GEN off of the 1.111 + * mechanism List. If we get a failure asking for this value, we know 1.112 + * it can't handle DSA2 */ 1.113 + if ((crv != CKR_OK) || (mechanism_info.ulMaxKeySize < primeBits)) { 1.114 + PK11_FreeSlot(slot); 1.115 + slot = PK11_GetBestSlotWithAttributes(CKM_DSA_PARAMETER_GEN, 0, 1.116 + primeBits, NULL); 1.117 + if (slot == NULL) { 1.118 + PORT_SetError(SEC_ERROR_NO_TOKEN); /* can happen */ 1.119 + goto loser; 1.120 + } 1.121 + /* ditch seedBits in this case, they are NSS specific and at 1.122 + * this point we have a token that claims to handle DSA2 */ 1.123 + if (seedBits) { 1.124 + attrs--; 1.125 + } 1.126 + } 1.127 + } 1.128 + 1.129 + /* Initialize the Key Gen Mechanism */ 1.130 + mechanism.mechanism = CKM_DSA_PARAMETER_GEN; 1.131 + mechanism.pParameter = NULL; 1.132 + mechanism.ulParameterLen = 0; 1.133 + 1.134 + PK11_EnterSlotMonitor(slot); 1.135 + crv = PK11_GETTAB(slot)->C_GenerateKey(slot->session, 1.136 + &mechanism, genTemplate, count, &objectID); 1.137 + PK11_ExitSlotMonitor(slot); 1.138 + 1.139 + if (crv != CKR_OK) { 1.140 + PORT_SetError( PK11_MapError(crv) ); 1.141 + goto loser; 1.142 + } 1.143 + 1.144 + parena = PORT_NewArena(60); 1.145 + if (!parena) { 1.146 + goto loser; 1.147 + } 1.148 + 1.149 + crv = PK11_GetAttributes(parena, slot, objectID, pTemplate, pTemplateCount); 1.150 + if (crv != CKR_OK) { 1.151 + PORT_SetError( PK11_MapError(crv) ); 1.152 + goto loser; 1.153 + } 1.154 + 1.155 + 1.156 + params = (PQGParams *)PORT_ArenaAlloc(parena,sizeof(PQGParams)); 1.157 + if (params == NULL) { 1.158 + goto loser; 1.159 + } 1.160 + 1.161 + /* fill in Params */ 1.162 + params->arena = parena; 1.163 + params->prime.type = siUnsignedInteger; 1.164 + params->prime.data = pTemplate[0].pValue; 1.165 + params->prime.len = pTemplate[0].ulValueLen; 1.166 + params->subPrime.type = siUnsignedInteger; 1.167 + params->subPrime.data = pTemplate[1].pValue; 1.168 + params->subPrime.len = pTemplate[1].ulValueLen; 1.169 + params->base.type = siUnsignedInteger; 1.170 + params->base.data = pTemplate[2].pValue; 1.171 + params->base.len = pTemplate[2].ulValueLen; 1.172 + 1.173 + 1.174 + varena = PORT_NewArena(60); 1.175 + if (!varena) { 1.176 + goto loser; 1.177 + } 1.178 + 1.179 + crv = PK11_GetAttributes(varena, slot, objectID, vTemplate, vTemplateCount); 1.180 + if (crv != CKR_OK) { 1.181 + PORT_SetError( PK11_MapError(crv) ); 1.182 + goto loser; 1.183 + } 1.184 + 1.185 + 1.186 + verify = (PQGVerify *)PORT_ArenaAlloc(varena,sizeof(PQGVerify)); 1.187 + if (verify == NULL) { 1.188 + goto loser; 1.189 + } 1.190 + /* fill in Params */ 1.191 + verify->arena = varena; 1.192 + verify->counter = (unsigned int)(*(CK_ULONG*)vTemplate[0].pValue); 1.193 + verify->seed.type = siUnsignedInteger; 1.194 + verify->seed.data = vTemplate[1].pValue; 1.195 + verify->seed.len = vTemplate[1].ulValueLen; 1.196 + verify->h.type = siUnsignedInteger; 1.197 + verify->h.data = vTemplate[2].pValue; 1.198 + verify->h.len = vTemplate[2].ulValueLen; 1.199 + 1.200 + PK11_DestroyObject(slot,objectID); 1.201 + PK11_FreeSlot(slot); 1.202 + 1.203 + *pParams = params; 1.204 + *pVfy = verify; 1.205 + 1.206 + return SECSuccess; 1.207 + 1.208 +loser: 1.209 + if (objectID != CK_INVALID_HANDLE) { 1.210 + PK11_DestroyObject(slot,objectID); 1.211 + } 1.212 + if (parena != NULL) { 1.213 + PORT_FreeArena(parena,PR_FALSE); 1.214 + } 1.215 + if (varena != NULL) { 1.216 + PORT_FreeArena(varena,PR_FALSE); 1.217 + } 1.218 + if (slot) { 1.219 + PK11_FreeSlot(slot); 1.220 + } 1.221 + return SECFailure; 1.222 +} 1.223 + 1.224 +/* Generate PQGParams and PQGVerify structs. 1.225 + * Length of P specified by j. Length of h will match length of P. 1.226 + * Length of SEED in bytes specified in seedBytes. 1.227 + * seedBbytes must be in the range [20..255] or an error will result. 1.228 + */ 1.229 +extern SECStatus 1.230 +PK11_PQG_ParamGenSeedLen( unsigned int j, unsigned int seedBytes, 1.231 + PQGParams **pParams, PQGVerify **pVfy) 1.232 +{ 1.233 + unsigned int primeBits = PQG_INDEX_TO_PBITS(j); 1.234 + return PK11_PQG_ParamGenV2(primeBits, 0, seedBytes, pParams, pVfy); 1.235 +} 1.236 + 1.237 +/* Generate PQGParams and PQGVerify structs. 1.238 + * Length of seed and length of h both equal length of P. 1.239 + * All lengths are specified by "j", according to the table above. 1.240 + */ 1.241 +extern SECStatus 1.242 +PK11_PQG_ParamGen(unsigned int j, PQGParams **pParams, PQGVerify **pVfy) 1.243 +{ 1.244 + unsigned int primeBits = PQG_INDEX_TO_PBITS(j); 1.245 + return PK11_PQG_ParamGenV2(primeBits, 0, 0, pParams, pVfy); 1.246 +} 1.247 + 1.248 +/* Test PQGParams for validity as DSS PQG values. 1.249 + * If vfy is non-NULL, test PQGParams to make sure they were generated 1.250 + * using the specified seed, counter, and h values. 1.251 + * 1.252 + * Return value indicates whether Verification operation ran successfully 1.253 + * to completion, but does not indicate if PQGParams are valid or not. 1.254 + * If return value is SECSuccess, then *pResult has these meanings: 1.255 + * SECSuccess: PQGParams are valid. 1.256 + * SECFailure: PQGParams are invalid. 1.257 + */ 1.258 + 1.259 +extern SECStatus 1.260 +PK11_PQG_VerifyParams(const PQGParams *params, const PQGVerify *vfy, 1.261 + SECStatus *result) 1.262 +{ 1.263 + CK_ATTRIBUTE keyTempl[] = { 1.264 + { CKA_CLASS, NULL, 0 }, 1.265 + { CKA_KEY_TYPE, NULL, 0 }, 1.266 + { CKA_PRIME, NULL, 0 }, 1.267 + { CKA_SUBPRIME, NULL, 0 }, 1.268 + { CKA_BASE, NULL, 0 }, 1.269 + { CKA_TOKEN, NULL, 0 }, 1.270 + { CKA_NETSCAPE_PQG_COUNTER, NULL, 0 }, 1.271 + { CKA_NETSCAPE_PQG_SEED, NULL, 0 }, 1.272 + { CKA_NETSCAPE_PQG_H, NULL, 0 }, 1.273 + }; 1.274 + CK_ATTRIBUTE *attrs; 1.275 + CK_BBOOL ckfalse = CK_FALSE; 1.276 + CK_OBJECT_CLASS class = CKO_KG_PARAMETERS; 1.277 + CK_KEY_TYPE keyType = CKK_DSA; 1.278 + SECStatus rv = SECSuccess; 1.279 + PK11SlotInfo *slot; 1.280 + int keyCount; 1.281 + CK_OBJECT_HANDLE objectID; 1.282 + CK_ULONG counter; 1.283 + CK_RV crv; 1.284 + 1.285 + attrs = keyTempl; 1.286 + PK11_SETATTRS(attrs, CKA_CLASS, &class, sizeof(class)); attrs++; 1.287 + PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType)); attrs++; 1.288 + PK11_SETATTRS(attrs, CKA_PRIME, params->prime.data, 1.289 + params->prime.len); attrs++; 1.290 + PK11_SETATTRS(attrs, CKA_SUBPRIME, params->subPrime.data, 1.291 + params->subPrime.len); attrs++; 1.292 + if (params->base.len) { 1.293 + PK11_SETATTRS(attrs, CKA_BASE,params->base.data,params->base.len); 1.294 + attrs++; 1.295 + } 1.296 + PK11_SETATTRS(attrs, CKA_TOKEN, &ckfalse, sizeof(ckfalse)); attrs++; 1.297 + if (vfy) { 1.298 + if (vfy->counter != -1) { 1.299 + counter = vfy->counter; 1.300 + PK11_SETATTRS(attrs, CKA_NETSCAPE_PQG_COUNTER, 1.301 + &counter, sizeof(counter)); attrs++; 1.302 + } 1.303 + PK11_SETATTRS(attrs, CKA_NETSCAPE_PQG_SEED, 1.304 + vfy->seed.data, vfy->seed.len); attrs++; 1.305 + if (vfy->h.len) { 1.306 + PK11_SETATTRS(attrs, CKA_NETSCAPE_PQG_H, 1.307 + vfy->h.data, vfy->h.len); attrs++; 1.308 + } 1.309 + } 1.310 + 1.311 + keyCount = attrs - keyTempl; 1.312 + PORT_Assert(keyCount <= sizeof(keyTempl)/sizeof(keyTempl[0])); 1.313 + 1.314 + 1.315 + slot = PK11_GetInternalSlot(); 1.316 + if (slot == NULL) { 1.317 + return SECFailure; 1.318 + } 1.319 + 1.320 + PK11_EnterSlotMonitor(slot); 1.321 + crv = PK11_GETTAB(slot)->C_CreateObject(slot->session, keyTempl, keyCount, 1.322 + &objectID); 1.323 + PK11_ExitSlotMonitor(slot); 1.324 + 1.325 + /* throw away the keys, we only wanted the return code */ 1.326 + PK11_DestroyObject(slot,objectID); 1.327 + PK11_FreeSlot(slot); 1.328 + 1.329 + *result = SECSuccess; 1.330 + if (crv == CKR_ATTRIBUTE_VALUE_INVALID) { 1.331 + *result = SECFailure; 1.332 + } else if (crv != CKR_OK) { 1.333 + PORT_SetError( PK11_MapError(crv) ); 1.334 + rv = SECFailure; 1.335 + } 1.336 + return rv; 1.337 + 1.338 +} 1.339 + 1.340 + 1.341 + 1.342 +/************************************************************************** 1.343 + * Free the PQGParams struct and the things it points to. * 1.344 + **************************************************************************/ 1.345 +extern void 1.346 +PK11_PQG_DestroyParams(PQGParams *params) { 1.347 + if (params == NULL) 1.348 + return; 1.349 + if (params->arena != NULL) { 1.350 + PORT_FreeArena(params->arena, PR_FALSE); /* don't zero it */ 1.351 + } else { 1.352 + SECITEM_FreeItem(¶ms->prime, PR_FALSE); /* don't free prime */ 1.353 + SECITEM_FreeItem(¶ms->subPrime, PR_FALSE); /* don't free subPrime */ 1.354 + SECITEM_FreeItem(¶ms->base, PR_FALSE); /* don't free base */ 1.355 + PORT_Free(params); 1.356 + } 1.357 +} 1.358 + 1.359 +/************************************************************************** 1.360 + * Free the PQGVerify struct and the things it points to. * 1.361 + **************************************************************************/ 1.362 +extern void 1.363 +PK11_PQG_DestroyVerify(PQGVerify *vfy) { 1.364 + if (vfy == NULL) 1.365 + return; 1.366 + if (vfy->arena != NULL) { 1.367 + PORT_FreeArena(vfy->arena, PR_FALSE); /* don't zero it */ 1.368 + } else { 1.369 + SECITEM_FreeItem(&vfy->seed, PR_FALSE); /* don't free seed */ 1.370 + SECITEM_FreeItem(&vfy->h, PR_FALSE); /* don't free h */ 1.371 + PORT_Free(vfy); 1.372 + } 1.373 +} 1.374 + 1.375 +#define PQG_DEFAULT_CHUNKSIZE 2048 /* bytes */ 1.376 + 1.377 +/************************************************************************** 1.378 + * Return a pointer to a new PQGParams struct that is constructed from * 1.379 + * copies of the arguments passed in. * 1.380 + * Return NULL on failure. * 1.381 + **************************************************************************/ 1.382 +extern PQGParams * 1.383 +PK11_PQG_NewParams(const SECItem * prime, const SECItem * subPrime, 1.384 + const SECItem * base) { 1.385 + PLArenaPool *arena; 1.386 + PQGParams *dest; 1.387 + SECStatus status; 1.388 + 1.389 + arena = PORT_NewArena(PQG_DEFAULT_CHUNKSIZE); 1.390 + if (arena == NULL) 1.391 + goto loser; 1.392 + 1.393 + dest = (PQGParams*)PORT_ArenaZAlloc(arena, sizeof(PQGParams)); 1.394 + if (dest == NULL) 1.395 + goto loser; 1.396 + 1.397 + dest->arena = arena; 1.398 + 1.399 + status = SECITEM_CopyItem(arena, &dest->prime, prime); 1.400 + if (status != SECSuccess) 1.401 + goto loser; 1.402 + 1.403 + status = SECITEM_CopyItem(arena, &dest->subPrime, subPrime); 1.404 + if (status != SECSuccess) 1.405 + goto loser; 1.406 + 1.407 + status = SECITEM_CopyItem(arena, &dest->base, base); 1.408 + if (status != SECSuccess) 1.409 + goto loser; 1.410 + 1.411 + return dest; 1.412 + 1.413 +loser: 1.414 + if (arena != NULL) 1.415 + PORT_FreeArena(arena, PR_FALSE); 1.416 + return NULL; 1.417 +} 1.418 + 1.419 + 1.420 +/************************************************************************** 1.421 + * Fills in caller's "prime" SECItem with the prime value in params. 1.422 + * Contents can be freed by calling SECITEM_FreeItem(prime, PR_FALSE); 1.423 + **************************************************************************/ 1.424 +extern SECStatus 1.425 +PK11_PQG_GetPrimeFromParams(const PQGParams *params, SECItem * prime) { 1.426 + return SECITEM_CopyItem(NULL, prime, ¶ms->prime); 1.427 +} 1.428 + 1.429 + 1.430 +/************************************************************************** 1.431 + * Fills in caller's "subPrime" SECItem with the prime value in params. 1.432 + * Contents can be freed by calling SECITEM_FreeItem(subPrime, PR_FALSE); 1.433 + **************************************************************************/ 1.434 +extern SECStatus 1.435 +PK11_PQG_GetSubPrimeFromParams(const PQGParams *params, SECItem * subPrime) { 1.436 + return SECITEM_CopyItem(NULL, subPrime, ¶ms->subPrime); 1.437 +} 1.438 + 1.439 + 1.440 +/************************************************************************** 1.441 + * Fills in caller's "base" SECItem with the base value in params. 1.442 + * Contents can be freed by calling SECITEM_FreeItem(base, PR_FALSE); 1.443 + **************************************************************************/ 1.444 +extern SECStatus 1.445 +PK11_PQG_GetBaseFromParams(const PQGParams *params, SECItem *base) { 1.446 + return SECITEM_CopyItem(NULL, base, ¶ms->base); 1.447 +} 1.448 + 1.449 + 1.450 +/************************************************************************** 1.451 + * Return a pointer to a new PQGVerify struct that is constructed from * 1.452 + * copies of the arguments passed in. * 1.453 + * Return NULL on failure. * 1.454 + **************************************************************************/ 1.455 +extern PQGVerify * 1.456 +PK11_PQG_NewVerify(unsigned int counter, const SECItem * seed, 1.457 + const SECItem * h) { 1.458 + PLArenaPool *arena; 1.459 + PQGVerify * dest; 1.460 + SECStatus status; 1.461 + 1.462 + arena = PORT_NewArena(PQG_DEFAULT_CHUNKSIZE); 1.463 + if (arena == NULL) 1.464 + goto loser; 1.465 + 1.466 + dest = (PQGVerify*)PORT_ArenaZAlloc(arena, sizeof(PQGVerify)); 1.467 + if (dest == NULL) 1.468 + goto loser; 1.469 + 1.470 + dest->arena = arena; 1.471 + dest->counter = counter; 1.472 + 1.473 + status = SECITEM_CopyItem(arena, &dest->seed, seed); 1.474 + if (status != SECSuccess) 1.475 + goto loser; 1.476 + 1.477 + status = SECITEM_CopyItem(arena, &dest->h, h); 1.478 + if (status != SECSuccess) 1.479 + goto loser; 1.480 + 1.481 + return dest; 1.482 + 1.483 +loser: 1.484 + if (arena != NULL) 1.485 + PORT_FreeArena(arena, PR_FALSE); 1.486 + return NULL; 1.487 +} 1.488 + 1.489 + 1.490 +/************************************************************************** 1.491 + * Returns "counter" value from the PQGVerify. 1.492 + **************************************************************************/ 1.493 +extern unsigned int 1.494 +PK11_PQG_GetCounterFromVerify(const PQGVerify *verify) { 1.495 + return verify->counter; 1.496 +} 1.497 + 1.498 +/************************************************************************** 1.499 + * Fills in caller's "seed" SECItem with the seed value in verify. 1.500 + * Contents can be freed by calling SECITEM_FreeItem(seed, PR_FALSE); 1.501 + **************************************************************************/ 1.502 +extern SECStatus 1.503 +PK11_PQG_GetSeedFromVerify(const PQGVerify *verify, SECItem *seed) { 1.504 + return SECITEM_CopyItem(NULL, seed, &verify->seed); 1.505 +} 1.506 + 1.507 + 1.508 +/************************************************************************** 1.509 + * Fills in caller's "h" SECItem with the h value in verify. 1.510 + * Contents can be freed by calling SECITEM_FreeItem(h, PR_FALSE); 1.511 + **************************************************************************/ 1.512 +extern SECStatus 1.513 +PK11_PQG_GetHFromVerify(const PQGVerify *verify, SECItem * h) { 1.514 + return SECITEM_CopyItem(NULL, h, &verify->h); 1.515 +}