dsa.c, srp.c, wolfcrypt/test/test.c: smallstack refactors: wc_DsaExportKeyRaw(), wc_DsaSign(), wc_SrpSetKey(), ecc_test_cdh_vectors(), ecc_test_custom_curves().

This commit is contained in:
Daniel Pouzzner
2020-12-01 18:35:57 -06:00
parent 1fc2c7714c
commit f49e9bf954
3 changed files with 584 additions and 210 deletions

View File

@ -654,32 +654,67 @@ int wc_DsaExportKeyRaw(DsaKey* dsa, byte* x, word32* xSz, byte* y, word32* ySz)
int wc_DsaSign(const byte* digest, byte* out, DsaKey* key, WC_RNG* rng) int wc_DsaSign(const byte* digest, byte* out, DsaKey* key, WC_RNG* rng)
{ {
mp_int k, kInv, r, s, H; #ifdef WOLFSSL_SMALL_STACK
mp_int *k = (mp_int *)XMALLOC(sizeof *k,
key->heap, DYNAMIC_TYPE_TMP_BUFFER);
mp_int *kInv = (mp_int *)XMALLOC(sizeof *kInv,
key->heap, DYNAMIC_TYPE_TMP_BUFFER);
mp_int *r = (mp_int *)XMALLOC(sizeof *r,
key->heap, DYNAMIC_TYPE_TMP_BUFFER);
mp_int *s = (mp_int *)XMALLOC(sizeof *s,
key->heap, DYNAMIC_TYPE_TMP_BUFFER);
mp_int *H = (mp_int *)XMALLOC(sizeof *H,
key->heap, DYNAMIC_TYPE_TMP_BUFFER);
#ifndef WOLFSSL_MP_INVMOD_CONSTANT_TIME #ifndef WOLFSSL_MP_INVMOD_CONSTANT_TIME
mp_int b; mp_int *b = (mp_int *)XMALLOC(sizeof *b,
key->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif
byte *buffer = (byte *)XMALLOC(DSA_HALF_SIZE, key->heap,
DYNAMIC_TYPE_TMP_BUFFER);
#else
mp_int k[1], kInv[1], r[1], s[1], H[1];
#ifndef WOLFSSL_MP_INVMOD_CONSTANT_TIME
mp_int b[1];
#endif
byte buffer[DSA_HALF_SIZE];
#endif #endif
mp_int* qMinus1; mp_int* qMinus1;
int ret = 0, sz; int ret = 0, sz = 0;
byte buffer[DSA_HALF_SIZE];
byte* tmp; /* initial output pointer */ byte* tmp; /* initial output pointer */
if (digest == NULL || out == NULL || key == NULL || rng == NULL) {
return BAD_FUNC_ARG;
}
tmp = out;
sz = min((int)sizeof(buffer), mp_unsigned_bin_size(&key->q));
#ifdef WOLFSSL_MP_INVMOD_CONSTANT_TIME #ifdef WOLFSSL_MP_INVMOD_CONSTANT_TIME
if (mp_init_multi(&k, &kInv, &r, &s, &H, 0) != MP_OKAY) if (mp_init_multi(k, kInv, r, s, H, 0) != MP_OKAY)
#else #else
if (mp_init_multi(&k, &kInv, &r, &s, &H, &b) != MP_OKAY) if (mp_init_multi(k, kInv, r, s, H, b) != MP_OKAY)
#endif #endif
{ {
return MP_INIT_E; ret = MP_INIT_E;
goto out;
} }
qMinus1 = &kInv;
#ifdef WOLFSSL_SMALL_STACK
if ((k == NULL) ||
(kInv == NULL) ||
(r == NULL) ||
(s == NULL) ||
(H == NULL)
#ifndef WOLFSSL_MP_INVMOD_CONSTANT_TIME
|| (b == NULL)
#endif
|| (buffer == NULL)) {
ret = MEMORY_E;
goto out;
}
#endif
if (digest == NULL || out == NULL || key == NULL || rng == NULL) {
ret = BAD_FUNC_ARG;
goto out;
}
sz = min(DSA_HALF_SIZE, mp_unsigned_bin_size(&key->q));
tmp = out;
qMinus1 = kInv;
/* NIST FIPS 186-4: B.2.2 /* NIST FIPS 186-4: B.2.2
* Per-Message Secret Number Generation by Testing Candidates * Per-Message Secret Number Generation by Testing Candidates
@ -687,159 +722,248 @@ int wc_DsaSign(const byte* digest, byte* out, DsaKey* key, WC_RNG* rng)
* Check that k is less than q-1: range [0, q-2]. * Check that k is less than q-1: range [0, q-2].
* Add 1 to k: range [1, q-1]. * Add 1 to k: range [1, q-1].
*/ */
if (mp_sub_d(&key->q, 1, qMinus1)) if (mp_sub_d(&key->q, 1, qMinus1)) {
ret = MP_SUB_E; ret = MP_SUB_E;
goto out;
if (ret == 0) {
do {
/* Step 4: generate k */
ret = wc_RNG_GenerateBlock(rng, buffer, sz);
/* Step 5 */
if (ret == 0 && mp_read_unsigned_bin(&k, buffer, sz) != MP_OKAY)
ret = MP_READ_E;
/* k is a random numnber and it should be less than q-1
* if k greater than repeat
*/
/* Step 6 */
} while (ret == 0 && mp_cmp(&k, qMinus1) != MP_LT);
} }
do {
/* Step 4: generate k */
if ((ret = wc_RNG_GenerateBlock(rng, buffer, sz))) {
goto out;
}
/* Step 5 */
if (mp_read_unsigned_bin(k, buffer, sz) != MP_OKAY) {
ret = MP_READ_E;
goto out;
}
/* k is a random numnber and it should be less than q-1
* if k greater than repeat
*/
/* Step 6 */
} while (mp_cmp(k, qMinus1) != MP_LT);
/* Step 7 */ /* Step 7 */
if (ret == 0 && mp_add_d(&k, 1, &k) != MP_OKAY) if (mp_add_d(k, 1, k) != MP_OKAY) {
ret = MP_MOD_E; ret = MP_MOD_E;
goto out;
}
#ifdef WOLFSSL_MP_INVMOD_CONSTANT_TIME #ifdef WOLFSSL_MP_INVMOD_CONSTANT_TIME
/* inverse k mod q */ /* inverse k mod q */
if (ret == 0 && mp_invmod(&k, &key->q, &kInv) != MP_OKAY) if (mp_invmod(k, &key->q, kInv) != MP_OKAY) {
ret = MP_INVMOD_E; ret = MP_INVMOD_E;
goto out;
/* generate r, r = (g exp k mod p) mod q */
if (ret == 0 && mp_exptmod_ex(&key->g, &k, key->q.used, &key->p,
&r) != MP_OKAY) {
ret = MP_EXPTMOD_E;
} }
if (ret == 0 && mp_mod(&r, &key->q, &r) != MP_OKAY) /* generate r, r = (g exp k mod p) mod q */
if (mp_exptmod_ex(&key->g, k, key->q.used, &key->p, r) != MP_OKAY) {
ret = MP_EXPTMOD_E;
goto out;
}
if (mp_mod(r, &key->q, r) != MP_OKAY) {
ret = MP_MOD_E; ret = MP_MOD_E;
goto out;
}
/* generate H from sha digest */ /* generate H from sha digest */
if (ret == 0 && mp_read_unsigned_bin(&H, digest,WC_SHA_DIGEST_SIZE) != MP_OKAY) if (mp_read_unsigned_bin(H, digest,WC_SHA_DIGEST_SIZE) != MP_OKAY) {
ret = MP_READ_E; ret = MP_READ_E;
goto out;
}
/* generate s, s = (kInv * (H + x*r)) % q */ /* generate s, s = (kInv * (H + x*r)) % q */
if (ret == 0 && mp_mul(&key->x, &r, &s) != MP_OKAY) if (mp_mul(&key->x, r, s) != MP_OKAY) {
ret = MP_MUL_E; ret = MP_MUL_E;
goto out;
}
if (ret == 0 && mp_add(&s, &H, &s) != MP_OKAY) if (mp_add(s, H, s) != MP_OKAY) {
ret = MP_ADD_E; ret = MP_ADD_E;
goto out;
}
if (ret == 0 && mp_mulmod(&s, &kInv, &key->q, &s) != MP_OKAY) if (mp_mulmod(s, kInv, &key->q, s) != MP_OKAY) {
ret = MP_MULMOD_E; ret = MP_MULMOD_E;
goto out;
}
#else #else
/* Blinding value /* Blinding value
* Generate b in range [1, q-1]. * Generate b in range [1, q-1].
*/ */
if (ret == 0) { do {
do { if ((ret = wc_RNG_GenerateBlock(rng, buffer, sz))) {
ret = wc_RNG_GenerateBlock(rng, buffer, sz); goto out;
if (ret == 0 && mp_read_unsigned_bin(&b, buffer, sz) != MP_OKAY) }
ret = MP_READ_E; if (mp_read_unsigned_bin(b, buffer, sz) != MP_OKAY) {
} while (ret == 0 && mp_cmp(&b, qMinus1) != MP_LT); ret = MP_READ_E;
} goto out;
if (ret == 0 && mp_add_d(&b, 1, &b) != MP_OKAY) }
} while (mp_cmp(b, qMinus1) != MP_LT);
if (mp_add_d(b, 1, b) != MP_OKAY) {
ret = MP_MOD_E; ret = MP_MOD_E;
goto out;
}
/* set H from sha digest */ /* set H from sha digest */
if (ret == 0 && mp_read_unsigned_bin(&H, digest, if (mp_read_unsigned_bin(H, digest, WC_SHA_DIGEST_SIZE) != MP_OKAY) {
WC_SHA_DIGEST_SIZE) != MP_OKAY) {
ret = MP_READ_E; ret = MP_READ_E;
goto out;
} }
/* generate r, r = (g exp k mod p) mod q */ /* generate r, r = (g exp k mod p) mod q */
if (ret == 0 && mp_exptmod_ex(&key->g, &k, key->q.used, &key->p, if (mp_exptmod_ex(&key->g, k, key->q.used, &key->p, r) != MP_OKAY) {
&r) != MP_OKAY) {
ret = MP_EXPTMOD_E; ret = MP_EXPTMOD_E;
goto out;
} }
/* calculate s = (H + xr)/k /* calculate s = (H + xr)/k = b.(H/k.b + x.r/k.b) */
= b.(H/k.b + x.r/k.b) */
/* k = k.b */ /* k = k.b */
if (ret == 0 && mp_mulmod(&k, &b, &key->q, &k) != MP_OKAY) if (mp_mulmod(k, b, &key->q, k) != MP_OKAY) {
ret = MP_MULMOD_E; ret = MP_MULMOD_E;
goto out;
}
/* kInv = 1/k.b mod q */ /* kInv = 1/k.b mod q */
if (ret == 0 && mp_invmod(&k, &key->q, &kInv) != MP_OKAY) if (mp_invmod(k, &key->q, kInv) != MP_OKAY) {
ret = MP_INVMOD_E; ret = MP_INVMOD_E;
goto out;
}
if (ret == 0 && mp_mod(&r, &key->q, &r) != MP_OKAY) if (mp_mod(r, &key->q, r) != MP_OKAY) {
ret = MP_MOD_E; ret = MP_MOD_E;
goto out;
}
/* s = x.r */ /* s = x.r */
if (ret == 0 && mp_mul(&key->x, &r, &s) != MP_OKAY) if (mp_mul(&key->x, r, s) != MP_OKAY) {
ret = MP_MUL_E; ret = MP_MUL_E;
goto out;
}
/* s = x.r/k.b */ /* s = x.r/k.b */
if (ret == 0 && mp_mulmod(&s, &kInv, &key->q, &s) != MP_OKAY) if (mp_mulmod(s, kInv, &key->q, s) != MP_OKAY) {
ret = MP_MULMOD_E; ret = MP_MULMOD_E;
goto out;
}
/* H = H/k.b */ /* H = H/k.b */
if (ret == 0 && mp_mulmod(&H, &kInv, &key->q, &H) != MP_OKAY) if (mp_mulmod(H, kInv, &key->q, H) != MP_OKAY) {
ret = MP_MULMOD_E; ret = MP_MULMOD_E;
goto out;
}
/* s = H/k.b + x.r/k.b /* s = H/k.b + x.r/k.b = (H + x.r)/k.b */
= (H + x.r)/k.b */ if (mp_add(s, H, s) != MP_OKAY) {
if (ret == 0 && mp_add(&s, &H, &s) != MP_OKAY)
ret = MP_ADD_E; ret = MP_ADD_E;
goto out;
}
/* s = b.(e + x.r)/k.b /* s = b.(e + x.r)/k.b = (e + x.r)/k */
= (e + x.r)/k */ if (mp_mulmod(s, b, &key->q, s) != MP_OKAY) {
if (ret == 0 && mp_mulmod(&s, &b, &key->q, &s) != MP_OKAY)
ret = MP_MULMOD_E; ret = MP_MULMOD_E;
goto out;
}
/* s = (e + x.r)/k */ /* s = (e + x.r)/k */
if (ret == 0 && mp_mod(&s, &key->q, &s) != MP_OKAY) if (mp_mod(s, &key->q, s) != MP_OKAY) {
ret = MP_MOD_E; ret = MP_MOD_E;
goto out;
}
#endif #endif
/* detect zero r or s */ /* detect zero r or s */
if (ret == 0 && (mp_iszero(&r) == MP_YES || mp_iszero(&s) == MP_YES)) if ((mp_iszero(r) == MP_YES) || (mp_iszero(s) == MP_YES)) {
ret = MP_ZERO_E; ret = MP_ZERO_E;
goto out;
}
/* write out */ /* write out */
if (ret == 0) { {
int rSz = mp_unsigned_bin_size(&r); int rSz = mp_unsigned_bin_size(r);
int sSz = mp_unsigned_bin_size(&s); int sSz = mp_unsigned_bin_size(s);
while (rSz++ < DSA_HALF_SIZE) { while (rSz++ < DSA_HALF_SIZE) {
*out++ = 0x00; /* pad front with zeros */ *out++ = 0x00; /* pad front with zeros */
} }
if (mp_to_unsigned_bin(&r, out) != MP_OKAY) if (mp_to_unsigned_bin(r, out) != MP_OKAY)
ret = MP_TO_E; ret = MP_TO_E;
else { else {
out = tmp + DSA_HALF_SIZE; /* advance to s in output */ out = tmp + DSA_HALF_SIZE; /* advance to s in output */
while (sSz++ < DSA_HALF_SIZE) { while (sSz++ < DSA_HALF_SIZE) {
*out++ = 0x00; /* pad front with zeros */ *out++ = 0x00; /* pad front with zeros */
} }
ret = mp_to_unsigned_bin(&s, out); ret = mp_to_unsigned_bin(s, out);
} }
} }
ForceZero(buffer, sz); out:
mp_forcezero(&kInv);
mp_forcezero(&k);
#ifndef WOLFSSL_MP_INVMOD_CONSTANT_TIME
mp_forcezero(&b);
mp_clear(&b); #ifdef WOLFSSL_SMALL_STACK
if (k) {
if (ret != MP_INIT_E) {
mp_forcezero(k);
mp_clear(k);
}
XFREE(k, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
}
if (kInv) {
if (ret != MP_INIT_E) {
mp_forcezero(kInv);
mp_clear(kInv);
}
XFREE(kInv, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
}
if (r) {
if (ret != MP_INIT_E)
mp_clear(r);
XFREE(r, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
}
if (s) {
if (ret != MP_INIT_E)
mp_clear(s);
XFREE(s, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
}
if (H) {
if (ret != MP_INIT_E)
mp_clear(H);
XFREE(H, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
}
#ifndef WOLFSSL_MP_INVMOD_CONSTANT_TIME
if (b) {
if (ret != MP_INIT_E) {
mp_forcezero(b);
mp_clear(b);
}
XFREE(b, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
}
#endif
if (buffer) {
ForceZero(buffer, sz);
XFREE(buffer, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
}
#else /* !WOLFSSL_SMALL_STACK */
if (ret != MP_INIT_E) {
ForceZero(buffer, sz);
mp_forcezero(kInv);
mp_forcezero(k);
#ifndef WOLFSSL_MP_INVMOD_CONSTANT_TIME
mp_forcezero(b);
mp_clear(b);
#endif
mp_clear(H);
mp_clear(s);
mp_clear(r);
mp_clear(kInv);
mp_clear(k);
}
#endif #endif
mp_clear(&H);
mp_clear(&s);
mp_clear(&r);
mp_clear(&kInv);
mp_clear(&k);
return ret; return ret;
} }
@ -847,70 +971,154 @@ int wc_DsaSign(const byte* digest, byte* out, DsaKey* key, WC_RNG* rng)
int wc_DsaVerify(const byte* digest, const byte* sig, DsaKey* key, int* answer) int wc_DsaVerify(const byte* digest, const byte* sig, DsaKey* key, int* answer)
{ {
mp_int w, u1, u2, v, r, s; #ifdef WOLFSSL_SMALL_STACK
mp_int *w = (mp_int *)XMALLOC(sizeof *w,
key->heap, DYNAMIC_TYPE_TMP_BUFFER);
mp_int *u1 = (mp_int *)XMALLOC(sizeof *u1,
key->heap, DYNAMIC_TYPE_TMP_BUFFER);
mp_int *u2 = (mp_int *)XMALLOC(sizeof *u2,
key->heap, DYNAMIC_TYPE_TMP_BUFFER);
mp_int *v = (mp_int *)XMALLOC(sizeof *v,
key->heap, DYNAMIC_TYPE_TMP_BUFFER);
mp_int *r = (mp_int *)XMALLOC(sizeof *r,
key->heap, DYNAMIC_TYPE_TMP_BUFFER);
mp_int *s = (mp_int *)XMALLOC(sizeof *s,
key->heap, DYNAMIC_TYPE_TMP_BUFFER);
#else
mp_int w[1], u1[1], u2[1], v[1], r[1], s[1];
#endif
int ret = 0; int ret = 0;
if (digest == NULL || sig == NULL || key == NULL || answer == NULL) { if (mp_init_multi(w, u1, u2, v, r, s) != MP_OKAY) {
return BAD_FUNC_ARG; ret = MP_INIT_E;
goto out;
} }
if (mp_init_multi(&w, &u1, &u2, &v, &r, &s) != MP_OKAY) if (digest == NULL || sig == NULL || key == NULL || answer == NULL) {
return MP_INIT_E; ret = BAD_FUNC_ARG;
goto out;
}
#ifdef WOLFSSL_SMALL_STACK
if ((w == NULL) ||
(u1 == NULL) ||
(u2 == NULL) ||
(v == NULL) ||
(r == NULL) ||
(s == NULL)) {
ret = MEMORY_E;
goto out;
}
#endif
/* set r and s from signature */ /* set r and s from signature */
if (mp_read_unsigned_bin(&r, sig, DSA_HALF_SIZE) != MP_OKAY || if (mp_read_unsigned_bin(r, sig, DSA_HALF_SIZE) != MP_OKAY ||
mp_read_unsigned_bin(&s, sig + DSA_HALF_SIZE, DSA_HALF_SIZE) != MP_OKAY) mp_read_unsigned_bin(s, sig + DSA_HALF_SIZE, DSA_HALF_SIZE) != MP_OKAY) {
ret = MP_READ_E; ret = MP_READ_E;
goto out;
}
/* sanity checks */ /* sanity checks */
if (ret == 0) { if (mp_iszero(r) == MP_YES || mp_iszero(s) == MP_YES ||
if (mp_iszero(&r) == MP_YES || mp_iszero(&s) == MP_YES || mp_cmp(r, &key->q) != MP_LT || mp_cmp(s, &key->q) != MP_LT) {
mp_cmp(&r, &key->q) != MP_LT || mp_cmp(&s, &key->q) != MP_LT) { ret = MP_ZERO_E;
ret = MP_ZERO_E; goto out;
}
} }
/* put H into u1 from sha digest */ /* put H into u1 from sha digest */
if (ret == 0 && mp_read_unsigned_bin(&u1,digest,WC_SHA_DIGEST_SIZE) != MP_OKAY) if (mp_read_unsigned_bin(u1,digest,WC_SHA_DIGEST_SIZE) != MP_OKAY) {
ret = MP_READ_E; ret = MP_READ_E;
goto out;
}
/* w = s invmod q */ /* w = s invmod q */
if (ret == 0 && mp_invmod(&s, &key->q, &w) != MP_OKAY) if (mp_invmod(s, &key->q, w) != MP_OKAY) {
ret = MP_INVMOD_E; ret = MP_INVMOD_E;
goto out;
}
/* u1 = (H * w) % q */ /* u1 = (H * w) % q */
if (ret == 0 && mp_mulmod(&u1, &w, &key->q, &u1) != MP_OKAY) if (mp_mulmod(u1, w, &key->q, u1) != MP_OKAY) {
ret = MP_MULMOD_E; ret = MP_MULMOD_E;
goto out;
}
/* u2 = (r * w) % q */ /* u2 = (r * w) % q */
if (ret == 0 && mp_mulmod(&r, &w, &key->q, &u2) != MP_OKAY) if (mp_mulmod(r, w, &key->q, u2) != MP_OKAY) {
ret = MP_MULMOD_E; ret = MP_MULMOD_E;
goto out;
}
/* verify v = ((g^u1 * y^u2) mod p) mod q */ /* verify v = ((g^u1 * y^u2) mod p) mod q */
if (ret == 0 && mp_exptmod(&key->g, &u1, &key->p, &u1) != MP_OKAY) if (mp_exptmod(&key->g, u1, &key->p, u1) != MP_OKAY) {
ret = MP_EXPTMOD_E; ret = MP_EXPTMOD_E;
goto out;
}
if (ret == 0 && mp_exptmod(&key->y, &u2, &key->p, &u2) != MP_OKAY) if (mp_exptmod(&key->y, u2, &key->p, u2) != MP_OKAY) {
ret = MP_EXPTMOD_E; ret = MP_EXPTMOD_E;
goto out;
}
if (ret == 0 && mp_mulmod(&u1, &u2, &key->p, &v) != MP_OKAY) if (mp_mulmod(u1, u2, &key->p, v) != MP_OKAY) {
ret = MP_MULMOD_E; ret = MP_MULMOD_E;
goto out;
}
if (ret == 0 && mp_mod(&v, &key->q, &v) != MP_OKAY) if (mp_mod(v, &key->q, v) != MP_OKAY) {
ret = MP_MULMOD_E; ret = MP_MULMOD_E;
goto out;
}
/* do they match */ /* do they match */
if (ret == 0 && mp_cmp(&r, &v) == MP_EQ) if (mp_cmp(r, v) == MP_EQ)
*answer = 1; *answer = 1;
else else
*answer = 0; *answer = 0;
mp_clear(&s); out:
mp_clear(&r);
mp_clear(&u1); #ifdef WOLFSSL_SMALL_STACK
mp_clear(&u2); if (s) {
mp_clear(&w); if (ret != MP_INIT_E)
mp_clear(&v); mp_clear(s);
XFREE(s, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
}
if (r) {
if (ret != MP_INIT_E)
mp_clear(r);
XFREE(r, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
}
if (u1) {
if (ret != MP_INIT_E)
mp_clear(u1);
XFREE(u1, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
}
if (u2) {
if (ret != MP_INIT_E)
mp_clear(u2);
XFREE(u2, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
}
if (w) {
if (ret != MP_INIT_E)
mp_clear(w);
XFREE(w, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
}
if (v) {
if (ret != MP_INIT_E)
mp_clear(v);
XFREE(v, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
}
#else
if (ret != MP_INIT_E) {
mp_clear(s);
mp_clear(r);
mp_clear(u1);
mp_clear(u2);
mp_clear(w);
mp_clear(v);
}
#endif
return ret; return ret;
} }

View File

@ -631,117 +631,233 @@ static int wc_SrpSetKey(Srp* srp, byte* secret, word32 size)
int wc_SrpComputeKey(Srp* srp, byte* clientPubKey, word32 clientPubKeySz, int wc_SrpComputeKey(Srp* srp, byte* clientPubKey, word32 clientPubKeySz,
byte* serverPubKey, word32 serverPubKeySz) byte* serverPubKey, word32 serverPubKeySz)
{ {
SrpHash hash; #ifdef WOLFSSL_SMALL_STACK
byte *secret; SrpHash *hash = (SrpHash *)XMALLOC(sizeof *hash, srp->heap, DYNAMIC_TYPE_SRP);
byte *digest = (byte *)XMALLOC(SRP_MAX_DIGEST_SIZE, srp->heap, DYNAMIC_TYPE_SRP);
mp_int *u = (mp_int *)XMALLOC(sizeof *u, srp->heap, DYNAMIC_TYPE_SRP);
mp_int *s = (mp_int *)XMALLOC(sizeof *s, srp->heap, DYNAMIC_TYPE_SRP);
mp_int *temp1 = (mp_int *)XMALLOC(sizeof *temp1, srp->heap, DYNAMIC_TYPE_SRP);
mp_int *temp2 = (mp_int *)XMALLOC(sizeof *temp2, srp->heap, DYNAMIC_TYPE_SRP);
#else
SrpHash hash[1];
byte digest[SRP_MAX_DIGEST_SIZE]; byte digest[SRP_MAX_DIGEST_SIZE];
mp_int u[1], s[1], temp1[1], temp2[1];
#endif
byte *secret = NULL;
word32 i, secretSz, digestSz; word32 i, secretSz, digestSz;
mp_int u, s, temp1, temp2;
byte pad = 0; byte pad = 0;
int r; int r;
/* validating params */ /* validating params */
if (!srp || !clientPubKey || clientPubKeySz == 0 if ((r = mp_init_multi(u, s, temp1, temp2, 0, 0)) != MP_OKAY) {
|| !serverPubKey || serverPubKeySz == 0) r = MP_INIT_E;
return BAD_FUNC_ARG; goto out;
}
if (mp_iszero(&srp->priv) == MP_YES) if (!srp || !clientPubKey || clientPubKeySz == 0
return SRP_CALL_ORDER_E; || !serverPubKey || serverPubKeySz == 0) {
r = BAD_FUNC_ARG;
goto out;
}
#ifdef WOLFSSL_SMALL_STACK
if ((hash == NULL) ||
(digest == NULL) ||
(u == NULL) ||
(s == NULL) ||
(temp1 == NULL) ||
(temp2 == NULL)) {
r = MEMORY_E;
goto out;
}
#endif
if (mp_iszero(&srp->priv) == MP_YES) {
r = SRP_CALL_ORDER_E;
goto out;
}
/* initializing variables */ /* initializing variables */
if ((r = SrpHashInit(&hash, srp->type)) != 0) if ((r = SrpHashInit(hash, srp->type)) != 0)
return r; goto out;
digestSz = SrpHashSize(srp->type); digestSz = SrpHashSize(srp->type);
secretSz = mp_unsigned_bin_size(&srp->N); secretSz = mp_unsigned_bin_size(&srp->N);
if ((secretSz < clientPubKeySz) || (secretSz < serverPubKeySz)) if ((secretSz < clientPubKeySz) || (secretSz < serverPubKeySz)) {
return BAD_FUNC_ARG; r = BAD_FUNC_ARG;
goto out;
}
if ((secret = (byte*)XMALLOC(secretSz, srp->heap, DYNAMIC_TYPE_SRP)) ==NULL) if ((secret = (byte*)XMALLOC(secretSz, srp->heap, DYNAMIC_TYPE_SRP)) == NULL) {
return MEMORY_E; r = MEMORY_E;
goto out;
if ((r = mp_init_multi(&u, &s, &temp1, &temp2, 0, 0)) != MP_OKAY) {
XFREE(secret, srp->heap, DYNAMIC_TYPE_SRP);
return r;
} }
/* building u (random scrambling parameter) */ /* building u (random scrambling parameter) */
/* H(A) */ /* H(A) */
for (i = 0; !r && i < secretSz - clientPubKeySz; i++) for (i = 0; i < secretSz - clientPubKeySz; i++) {
r = SrpHashUpdate(&hash, &pad, 1); if ((r = SrpHashUpdate(hash, &pad, 1)))
if (!r) r = SrpHashUpdate(&hash, clientPubKey, clientPubKeySz); goto out;
}
if ((r = SrpHashUpdate(hash, clientPubKey, clientPubKeySz)))
goto out;
/* H(A | B) */ /* H(A | B) */
for (i = 0; !r && i < secretSz - serverPubKeySz; i++) for (i = 0; i < secretSz - serverPubKeySz; i++) {
r = SrpHashUpdate(&hash, &pad, 1); if ((r = SrpHashUpdate(hash, &pad, 1)))
if (!r) r = SrpHashUpdate(&hash, serverPubKey, serverPubKeySz); goto out;
}
if ((r = SrpHashUpdate(hash, serverPubKey, serverPubKeySz)))
goto out;
/* set u */ /* set u */
if (!r) r = SrpHashFinal(&hash, digest); if ((r = SrpHashFinal(hash, digest)))
if (!r) r = mp_read_unsigned_bin(&u, digest, SrpHashSize(srp->type)); goto out;
SrpHashFree(&hash); if ((r = mp_read_unsigned_bin(u, digest, SrpHashSize(srp->type))))
goto out;
SrpHashFree(hash);
/* building s (secret) */ /* building s (secret) */
if (!r && srp->side == SRP_CLIENT_SIDE) { if (srp->side == SRP_CLIENT_SIDE) {
/* temp1 = B - k * v; rejects k == 0, B == 0 and B >= N. */ /* temp1 = B - k * v; rejects k == 0, B == 0 and B >= N. */
r = mp_read_unsigned_bin(&temp1, srp->k, digestSz); if ((r = mp_read_unsigned_bin(temp1, srp->k, digestSz)))
if (!r) r = mp_iszero(&temp1) == MP_YES ? SRP_BAD_KEY_E : 0; goto out;
if (!r) r = mp_exptmod(&srp->g, &srp->auth, &srp->N, &temp2); if (mp_iszero(temp1) == MP_YES) {
if (!r) r = mp_mulmod(&temp1, &temp2, &srp->N, &s); r = SRP_BAD_KEY_E;
if (!r) r = mp_read_unsigned_bin(&temp2, serverPubKey, serverPubKeySz); goto out;
if (!r) r = mp_iszero(&temp2) == MP_YES ? SRP_BAD_KEY_E : 0; }
if (!r) r = mp_cmp(&temp2, &srp->N) != MP_LT ? SRP_BAD_KEY_E : 0; if ((r = mp_exptmod(&srp->g, &srp->auth, &srp->N, temp2)))
if (!r) r = mp_submod(&temp2, &s, &srp->N, &temp1); goto out;
if ((r = mp_mulmod(temp1, temp2, &srp->N, s)))
goto out;
if ((r = mp_read_unsigned_bin(temp2, serverPubKey, serverPubKeySz)))
goto out;
if (mp_iszero(temp2) == MP_YES) {
r = SRP_BAD_KEY_E;
goto out;
}
if (mp_cmp(temp2, &srp->N) != MP_LT) {
r = SRP_BAD_KEY_E;
goto out;
}
if ((r = mp_submod(temp2, s, &srp->N, temp1)))
goto out;
/* temp2 = a + u * x */ /* temp2 = a + u * x */
if (!r) r = mp_mulmod(&u, &srp->auth, &srp->N, &s); if ((r = mp_mulmod(u, &srp->auth, &srp->N, s)))
if (!r) r = mp_add(&srp->priv, &s, &temp2); goto out;
if ((r = mp_add(&srp->priv, s, temp2)))
goto out;
/* secret = temp1 ^ temp2 % N */ /* secret = temp1 ^ temp2 % N */
if (!r) r = mp_exptmod(&temp1, &temp2, &srp->N, &s); if ((r = mp_exptmod(temp1, temp2, &srp->N, s)))
goto out;
} else if (!r && srp->side == SRP_SERVER_SIDE) { } else if (srp->side == SRP_SERVER_SIDE) {
/* temp1 = v ^ u % N */ /* temp1 = v ^ u % N */
r = mp_exptmod(&srp->auth, &u, &srp->N, &temp1); if ((r = mp_exptmod(&srp->auth, u, &srp->N, temp1)))
goto out;
/* temp2 = A * temp1 % N; rejects A == 0, A >= N */ /* temp2 = A * temp1 % N; rejects A == 0, A >= N */
if (!r) r = mp_read_unsigned_bin(&s, clientPubKey, clientPubKeySz); if ((r = mp_read_unsigned_bin(s, clientPubKey, clientPubKeySz)))
if (!r) r = mp_iszero(&s) == MP_YES ? SRP_BAD_KEY_E : 0; goto out;
if (!r) r = mp_cmp(&s, &srp->N) != MP_LT ? SRP_BAD_KEY_E : 0; if (mp_iszero(s) == MP_YES) {
if (!r) r = mp_mulmod(&s, &temp1, &srp->N, &temp2); r = SRP_BAD_KEY_E;
goto out;
}
if (mp_cmp(s, &srp->N) != MP_LT) {
r = SRP_BAD_KEY_E;
goto out;
}
if ((r = mp_mulmod(s, temp1, &srp->N, temp2)))
goto out;
/* rejects A * v ^ u % N >= 1, A * v ^ u % N == -1 % N */ /* rejects A * v ^ u % N >= 1, A * v ^ u % N == -1 % N */
if (!r) r = mp_read_unsigned_bin(&temp1, (const byte*)"\001", 1); if ((r = mp_read_unsigned_bin(temp1, (const byte*)"\001", 1)))
if (!r) r = mp_cmp(&temp2, &temp1) != MP_GT ? SRP_BAD_KEY_E : 0; goto out;
if (!r) r = mp_sub(&srp->N, &temp1, &s); if (mp_cmp(temp2, temp1) != MP_GT) {
if (!r) r = mp_cmp(&temp2, &s) == MP_EQ ? SRP_BAD_KEY_E : 0; r = SRP_BAD_KEY_E;
goto out;
}
if ((r = mp_sub(&srp->N, temp1, s)))
goto out;
if (mp_cmp(temp2, s) == MP_EQ) {
r = SRP_BAD_KEY_E;
goto out;
}
/* secret = temp2 * b % N */ /* secret = temp2 * b % N */
if (!r) r = mp_exptmod(&temp2, &srp->priv, &srp->N, &s); if ((r = mp_exptmod(temp2, &srp->priv, &srp->N, s)))
goto out;
} }
/* building session key from secret */ /* building session key from secret */
if (!r) r = mp_to_unsigned_bin(&s, secret); if ((r = mp_to_unsigned_bin(s, secret)))
if (!r) r = srp->keyGenFunc_cb(srp, secret, mp_unsigned_bin_size(&s)); goto out;
if ((r = srp->keyGenFunc_cb(srp, secret, mp_unsigned_bin_size(s))))
goto out;
/* updating client proof = H( H(N) ^ H(g) | H(user) | salt | A | B | K) */ /* updating client proof = H( H(N) ^ H(g) | H(user) | salt | A | B | K) */
if (!r) r = SrpHashUpdate(&srp->client_proof, clientPubKey, clientPubKeySz); if ((r = SrpHashUpdate(&srp->client_proof, clientPubKey, clientPubKeySz)))
if (!r) r = SrpHashUpdate(&srp->client_proof, serverPubKey, serverPubKeySz); goto out;
if (!r) r = SrpHashUpdate(&srp->client_proof, srp->key, srp->keySz); if ((r = SrpHashUpdate(&srp->client_proof, serverPubKey, serverPubKeySz)))
goto out;
if ((r = SrpHashUpdate(&srp->client_proof, srp->key, srp->keySz)))
goto out;
/* updating server proof = H(A) */ /* updating server proof = H(A) */
if (!r) r = SrpHashUpdate(&srp->server_proof, clientPubKey, clientPubKeySz); r = SrpHashUpdate(&srp->server_proof, clientPubKey, clientPubKeySz);
XFREE(secret, srp->heap, DYNAMIC_TYPE_SRP); out:
mp_clear(&u); mp_clear(&s); mp_clear(&temp1); mp_clear(&temp2);
if (secret) {
ForceZero(secret, secretSz);
XFREE(secret, srp->heap, DYNAMIC_TYPE_SRP);
}
#ifdef WOLFSSL_SMALL_STACK
if (hash)
XFREE(hash, srp->heap, DYNAMIC_TYPE_SRP);
if (digest)
XFREE(digest, srp->heap, DYNAMIC_TYPE_SRP);
if (u) {
if (r != MP_INIT_E)
mp_clear(u);
XFREE(u, srp->heap, DYNAMIC_TYPE_SRP);
}
if (s) {
if (r != MP_INIT_E)
mp_clear(s);
XFREE(s, srp->heap, DYNAMIC_TYPE_SRP);
}
if (temp1) {
if (r != MP_INIT_E)
mp_clear(temp1);
XFREE(temp1, srp->heap, DYNAMIC_TYPE_SRP);
}
if (temp2) {
if (r != MP_INIT_E)
mp_clear(temp2);
XFREE(temp2, srp->heap, DYNAMIC_TYPE_SRP);
}
#else
if (r != MP_INIT_E) {
mp_clear(u);
mp_clear(s);
mp_clear(temp1);
mp_clear(temp2);
}
#endif
return r; return r;
} }

View File

@ -19408,39 +19408,53 @@ done:
static int ecc_test_cdh_vectors(WC_RNG* rng) static int ecc_test_cdh_vectors(WC_RNG* rng)
{ {
int ret; int ret;
ecc_key pub_key, priv_key; #ifdef WOLFSSL_SMALL_STACK
ecc_key *pub_key = (ecc_key *)XMALLOC(sizeof *pub_key, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
ecc_key *priv_key = (ecc_key *)XMALLOC(sizeof *priv_key, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
#else
ecc_key pub_key[1], priv_key[1];
#endif
byte sharedA[32] = {0}, sharedB[32] = {0}; byte sharedA[32] = {0}, sharedB[32] = {0};
word32 x, z; word32 x, z;
const char* QCAVSx = "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287"; static const char* QCAVSx = "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287";
const char* QCAVSy = "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac"; static const char* QCAVSy = "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac";
const char* dIUT = "7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534"; static const char* dIUT = "7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534";
const char* QIUTx = "ead218590119e8876b29146ff89ca61770c4edbbf97d38ce385ed281d8a6b230"; static const char* QIUTx = "ead218590119e8876b29146ff89ca61770c4edbbf97d38ce385ed281d8a6b230";
const char* QIUTy = "28af61281fd35e2fa7002523acc85a429cb06ee6648325389f59edfce1405141"; static const char* QIUTy = "28af61281fd35e2fa7002523acc85a429cb06ee6648325389f59edfce1405141";
const char* ZIUT = "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b"; static const char* ZIUT = "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b";
#ifdef WOLFSSL_SMALL_STACK
if ((pub_key == NULL) ||
(priv_key == NULL)) {
ret = MEMORY_E;
goto done;
}
#endif
XMEMSET(pub_key, 0, sizeof *pub_key);
XMEMSET(priv_key, 0, sizeof *priv_key);
/* setup private and public keys */ /* setup private and public keys */
ret = wc_ecc_init_ex(&pub_key, HEAP_HINT, devId); ret = wc_ecc_init_ex(pub_key, HEAP_HINT, devId);
if (ret != 0)
return ret;
ret = wc_ecc_init_ex(&priv_key, HEAP_HINT, devId);
if (ret != 0) {
wc_ecc_free(&pub_key);
return ret;
}
wc_ecc_set_flags(&pub_key, WC_ECC_FLAG_COFACTOR);
wc_ecc_set_flags(&priv_key, WC_ECC_FLAG_COFACTOR);
ret = wc_ecc_import_raw(&pub_key, QCAVSx, QCAVSy, NULL, "SECP256R1");
if (ret != 0) if (ret != 0)
goto done; goto done;
ret = wc_ecc_import_raw(&priv_key, QIUTx, QIUTy, dIUT, "SECP256R1"); ret = wc_ecc_init_ex(priv_key, HEAP_HINT, devId);
if (ret != 0)
goto done;
wc_ecc_set_flags(pub_key, WC_ECC_FLAG_COFACTOR);
wc_ecc_set_flags(priv_key, WC_ECC_FLAG_COFACTOR);
ret = wc_ecc_import_raw(pub_key, QCAVSx, QCAVSy, NULL, "SECP256R1");
if (ret != 0)
goto done;
ret = wc_ecc_import_raw(priv_key, QIUTx, QIUTy, dIUT, "SECP256R1");
if (ret != 0) if (ret != 0)
goto done; goto done;
#if defined(ECC_TIMING_RESISTANT) && (!defined(HAVE_FIPS) || \ #if defined(ECC_TIMING_RESISTANT) && (!defined(HAVE_FIPS) || \
(!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION != 2))) && \ (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION != 2))) && \
!defined(HAVE_SELFTEST) !defined(HAVE_SELFTEST)
ret = wc_ecc_set_rng(&priv_key, rng); ret = wc_ecc_set_rng(priv_key, rng);
if (ret != 0) if (ret != 0)
goto done; goto done;
#else #else
@ -19451,10 +19465,10 @@ static int ecc_test_cdh_vectors(WC_RNG* rng)
x = sizeof(sharedA); x = sizeof(sharedA);
do { do {
#if defined(WOLFSSL_ASYNC_CRYPT) #if defined(WOLFSSL_ASYNC_CRYPT)
ret = wc_AsyncWait(ret, &priv_key.asyncDev, WC_ASYNC_FLAG_CALL_AGAIN); ret = wc_AsyncWait(ret, priv_key.asyncDev, WC_ASYNC_FLAG_CALL_AGAIN);
#endif #endif
if (ret == 0) if (ret == 0)
ret = wc_ecc_shared_secret(&priv_key, &pub_key, sharedA, &x); ret = wc_ecc_shared_secret(priv_key, pub_key, sharedA, &x);
} while (ret == WC_PENDING_E); } while (ret == WC_PENDING_E);
if (ret != 0) { if (ret != 0) {
goto done; goto done;
@ -19473,8 +19487,21 @@ static int ecc_test_cdh_vectors(WC_RNG* rng)
} }
done: done:
wc_ecc_free(&priv_key);
wc_ecc_free(&pub_key); #ifdef WOLFSSL_SMALL_STACK
if (priv_key) {
wc_ecc_free(priv_key);
XFREE(priv_key, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
}
if (pub_key) {
wc_ecc_free(pub_key);
XFREE(pub_key, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
}
#else
wc_ecc_free(priv_key);
wc_ecc_free(pub_key);
#endif
return ret; return ret;
} }
#endif /* HAVE_ECC_CDH */ #endif /* HAVE_ECC_CDH */
@ -21230,24 +21257,28 @@ static int ecc_test_custom_curves(WC_RNG* rng)
{ {
int ret; int ret;
word32 inOutIdx; word32 inOutIdx;
ecc_key key; #ifdef WOLFSSL_SMALL_STACK
ecc_key *key = (ecc_key *)XMALLOC(sizeof *key, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
#else
ecc_key key[1];
#endif
/* test use of custom curve - using BRAINPOOLP256R1 for test */ /* test use of custom curve - using BRAINPOOLP256R1 for test */
#ifndef WOLFSSL_ECC_CURVE_STATIC #ifndef WOLFSSL_ECC_CURVE_STATIC
const ecc_oid_t ecc_oid_brainpoolp256r1[] = { static const ecc_oid_t ecc_oid_brainpoolp256r1[] = {
0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x07 0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x07
}; };
const word32 ecc_oid_brainpoolp256r1_sz = #define ecc_oid_brainpoolp256r1_sz \
sizeof(ecc_oid_brainpoolp256r1) / sizeof(ecc_oid_t); (sizeof(ecc_oid_brainpoolp256r1) / sizeof(ecc_oid_t))
#else #else
#define ecc_oid_brainpoolp256r1 { \ #define ecc_oid_brainpoolp256r1 { \
0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x07 \ 0x2B,0x24,0x03,0x03,0x02,0x08,0x01,0x01,0x07 \
} }
#define ecc_oid_brainpoolp256r1_sz 9 #define ecc_oid_brainpoolp256r1_sz 9
#endif #endif
const word32 ecc_oid_brainpoolp256r1_sum = 104; #define ecc_oid_brainpoolp256r1_sum 104
const ecc_set_type ecc_dp_brainpool256r1 = { static const ecc_set_type ecc_dp_brainpool256r1 = {
32, /* size/bytes */ 32, /* size/bytes */
ECC_CURVE_CUSTOM, /* ID */ ECC_CURVE_CUSTOM, /* ID */
"BRAINPOOLP256R1", /* curve name */ "BRAINPOOLP256R1", /* curve name */
@ -21263,11 +21294,20 @@ static int ecc_test_custom_curves(WC_RNG* rng)
1, /* cofactor */ 1, /* cofactor */
}; };
#ifdef WOLFSSL_SMALL_STACK
if (! key) {
ret = MEMORY_E;
goto done;
}
#endif
XMEMSET(key, 0, sizeof *key);
ret = ecc_test_curve_size(rng, 0, ECC_TEST_VERIFY_COUNT, ECC_CURVE_DEF, ret = ecc_test_curve_size(rng, 0, ECC_TEST_VERIFY_COUNT, ECC_CURVE_DEF,
&ecc_dp_brainpool256r1); &ecc_dp_brainpool256r1);
if (ret != 0) { if (ret != 0) {
printf("ECC test for custom curve failed! %d\n", ret); printf("ECC test for custom curve failed! %d\n", ret);
return ret; goto done;
} }
#if defined(HAVE_ECC_BRAINPOOL) || defined(HAVE_ECC_KOBLITZ) #if defined(HAVE_ECC_BRAINPOOL) || defined(HAVE_ECC_KOBLITZ)
@ -21282,23 +21322,33 @@ static int ecc_test_custom_curves(WC_RNG* rng)
ret = ecc_test_curve_size(rng, 0, ECC_TEST_VERIFY_COUNT, curve_id, NULL); ret = ecc_test_curve_size(rng, 0, ECC_TEST_VERIFY_COUNT, curve_id, NULL);
if (ret < 0) { if (ret < 0) {
printf("ECC test for curve_id %d failed! %d\n", curve_id, ret); printf("ECC test for curve_id %d failed! %d\n", curve_id, ret);
return ret; goto done;
} }
} }
#endif #endif
ret = wc_ecc_init_ex(&key, HEAP_HINT, devId); ret = wc_ecc_init_ex(key, HEAP_HINT, devId);
if (ret != 0) { if (ret != 0) {
return -10120; ret = -10120;
goto done;
} }
inOutIdx = 0; inOutIdx = 0;
ret = wc_EccPublicKeyDecode(eccKeyExplicitCurve, &inOutIdx, &key, ret = wc_EccPublicKeyDecode(eccKeyExplicitCurve, &inOutIdx, key,
sizeof(eccKeyExplicitCurve)); sizeof(eccKeyExplicitCurve));
if (ret != 0) if (ret != 0)
return -10121; ret = -10121;
wc_ecc_free(&key); done:
#ifdef WOLFSSL_SMALL_STACK
if (key) {
wc_ecc_free(key);
XFREE(key, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
}
#else
wc_ecc_free(key);
#endif
return ret; return ret;
} }