From e41dbc3ddbe30a414e73fa25d9c7c304ffe6989e Mon Sep 17 00:00:00 2001 From: Nikias Bassen Date: Wed, 9 Feb 2022 04:04:36 +0100 Subject: Add support for wireless pairing --- 3rd_party/libsrp6a-sha512/srp6a_sha512_client.c | 363 ++++++++++++++++++++++++ 1 file changed, 363 insertions(+) create mode 100644 3rd_party/libsrp6a-sha512/srp6a_sha512_client.c (limited to '3rd_party/libsrp6a-sha512/srp6a_sha512_client.c') diff --git a/3rd_party/libsrp6a-sha512/srp6a_sha512_client.c b/3rd_party/libsrp6a-sha512/srp6a_sha512_client.c new file mode 100644 index 0000000..db59fe8 --- /dev/null +++ b/3rd_party/libsrp6a-sha512/srp6a_sha512_client.c @@ -0,0 +1,363 @@ +/* + * Copyright (c) 1997-2007 The Stanford SRP Authentication Project + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INCIDENTAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF + * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Redistributions in source or binary form must retain an intact copy + * of this copyright notice. + */ +#include "t_defines.h" +#include "srp.h" +#include "t_sha.h" + +/* + * SRP-6/6a has two minor refinements relative to SRP-3/RFC2945: + * 1. The "g^x" value is multipled by three in the client's + * calculation of its session key. + * SRP-6a: The "g^x" value is multiplied by the hash of + * N and g in the client's session key calculation. + * 2. The value of u is taken as the hash of A and B, + * instead of the top 32 bits of the hash of B. + * This eliminates the old restriction where the + * server had to receive A before it could send B. + */ + +/****************************/ +#define SHA512_DIGESTSIZE 64 +#define SRP6_SHA512_KEY_LEN 64 + +/* + * The client keeps track of the running hash + * state via SHA512_CTX structures pointed to by the + * meth_data pointer. The "hash" member is the hash value that + * will be sent to the other side; the "ckhash" member is the + * hash value expected from the other side. + */ +struct sha512_client_meth_st { + SHA512_CTX hash; + SHA512_CTX ckhash; + unsigned char k[SRP6_SHA512_KEY_LEN]; +}; + +#define SHA512_CLIENT_CTXP(srp) ((struct sha512_client_meth_st *)(srp)->meth_data) + +static SRP_RESULT +srp6a_sha512_client_init(SRP * srp) +{ + srp->magic = SRP_MAGIC_CLIENT; + srp->flags = SRP_FLAG_MOD_ACCEL | SRP_FLAG_LEFT_PAD; + srp->meth_data = malloc(sizeof(struct sha512_client_meth_st)); + SHA512Init(&SHA512_CLIENT_CTXP(srp)->hash); + SHA512Init(&SHA512_CLIENT_CTXP(srp)->ckhash); + return SRP_SUCCESS; +} + +static SRP_RESULT +srp6_sha512_client_finish(SRP * srp) +{ + if(srp->meth_data) { + memset(srp->meth_data, 0, sizeof(struct sha512_client_meth_st)); + free(srp->meth_data); + } + return SRP_SUCCESS; +} + +static SRP_RESULT +srp6_sha512_client_params(SRP * srp, const unsigned char * modulus, int modlen, + const unsigned char * generator, int genlen, + const unsigned char * salt, int saltlen) +{ + int i; + unsigned char buf1[SHA512_DIGESTSIZE], buf2[SHA512_DIGESTSIZE]; + SHA512_CTX ctxt; + + /* Fields set by SRP_set_params */ + + /* Update hash state */ + SHA512Init(&ctxt); + SHA512Update(&ctxt, modulus, modlen); + SHA512Final(buf1, &ctxt); /* buf1 = H(modulus) */ + + SHA512Init(&ctxt); + SHA512Update(&ctxt, generator, genlen); + SHA512Final(buf2, &ctxt); /* buf2 = H(generator) */ + + for(i = 0; i < sizeof(buf1); ++i) + buf1[i] ^= buf2[i]; /* buf1 = H(modulus) xor H(generator) */ + + /* hash: H(N) xor H(g) */ + SHA512Update(&SHA512_CLIENT_CTXP(srp)->hash, buf1, sizeof(buf1)); + + SHA512Init(&ctxt); + SHA512Update(&ctxt, srp->username->data, srp->username->length); + SHA512Final(buf1, &ctxt); /* buf1 = H(user) */ + + /* hash: (H(N) xor H(g)) | H(U) */ + SHA512Update(&SHA512_CLIENT_CTXP(srp)->hash, buf1, sizeof(buf1)); + + /* hash: (H(N) xor H(g)) | H(U) | s */ + SHA512Update(&SHA512_CLIENT_CTXP(srp)->hash, salt, saltlen); + + return SRP_SUCCESS; +} + +static SRP_RESULT +srp6_sha512_client_auth(SRP * srp, const unsigned char * a, int alen) +{ + /* On the client, the authenticator is the raw password-derived hash */ + srp->password = BigIntegerFromBytes(a, alen); + + /* verifier = g^x mod N */ + srp->verifier = BigIntegerFromInt(0); + BigIntegerModExp(srp->verifier, srp->generator, srp->password, srp->modulus, srp->bctx, srp->accel); + + return SRP_SUCCESS; +} + +static SRP_RESULT +srp6_sha512_client_passwd(SRP * srp, const unsigned char * p, int plen) +{ + SHA512_CTX ctxt; + unsigned char dig[SHA512_DIGESTSIZE]; + int r; + + SHA512Init(&ctxt); + SHA512Update(&ctxt, srp->username->data, srp->username->length); + SHA512Update(&ctxt, ":", 1); + SHA512Update(&ctxt, p, plen); + SHA512Final(dig, &ctxt); /* dig = H(U | ":" | P) */ + + SHA512Init(&ctxt); + SHA512Update(&ctxt, srp->salt->data, srp->salt->length); + SHA512Update(&ctxt, dig, sizeof(dig)); + SHA512Final(dig, &ctxt); /* dig = H(s | H(U | ":" | P)) */ + memset(&ctxt, 0, sizeof(ctxt)); + + r = SRP_set_authenticator(srp, dig, sizeof(dig)); + memset(dig, 0, sizeof(dig)); + + return r; +} + +static SRP_RESULT +srp6_sha512_client_genpub(SRP * srp, cstr ** result) +{ + cstr * astr; + int slen = (SRP_get_secret_bits(BigIntegerBitLen(srp->modulus)) + 7) / 8; + + if(result == NULL) + astr = cstr_new(); + else { + if(*result == NULL) + *result = cstr_new(); + astr = *result; + } + + cstr_set_length(astr, BigIntegerByteLen(srp->modulus)); + t_random((unsigned char*)astr->data, slen); + srp->secret = BigIntegerFromBytes((const unsigned char*)astr->data, slen); + /* Force g^a mod n to "wrap around" by adding log[2](n) to "a". */ + BigIntegerAddInt(srp->secret, srp->secret, BigIntegerBitLen(srp->modulus)); + /* A = g^a mod n */ + srp->pubkey = BigIntegerFromInt(0); + BigIntegerModExp(srp->pubkey, srp->generator, srp->secret, srp->modulus, srp->bctx, srp->accel); + BigIntegerToCstr(srp->pubkey, astr); + + /* hash: (H(N) xor H(g)) | H(U) | s | A */ + SHA512Update(&SHA512_CLIENT_CTXP(srp)->hash, astr->data, astr->length); + /* ckhash: A */ + SHA512Update(&SHA512_CLIENT_CTXP(srp)->ckhash, astr->data, astr->length); + + if(result == NULL) /* astr was a temporary */ + cstr_clear_free(astr); + + return SRP_SUCCESS; +} + +static SRP_RESULT +srp6_sha512_client_key_ex(SRP * srp, cstr ** result, + const unsigned char * pubkey, int pubkeylen, BigInteger k) +{ + SHA512_CTX ctxt; + unsigned char dig[SHA512_DIGESTSIZE]; + BigInteger gb, e; + cstr * s; + int modlen; + + modlen = BigIntegerByteLen(srp->modulus); + if(pubkeylen > modlen) + return SRP_ERROR; + + /* Compute u from client's and server's values */ + SHA512Init(&ctxt); + /* Use s as a temporary to store client's value */ + s = cstr_new(); + if(srp->flags & SRP_FLAG_LEFT_PAD) { + BigIntegerToCstrEx(srp->pubkey, s, modlen); + SHA512Update(&ctxt, s->data, s->length); + if(pubkeylen < modlen) { + memcpy(s->data + (modlen - pubkeylen), pubkey, pubkeylen); + memset(s->data, 0, modlen - pubkeylen); + SHA512Update(&ctxt, s->data, modlen); + } + else + SHA512Update(&ctxt, pubkey, pubkeylen); + } + else { + BigIntegerToCstr(srp->pubkey, s); + SHA512Update(&ctxt, s->data, s->length); + SHA512Update(&ctxt, pubkey, pubkeylen); + } + SHA512Final(dig, &ctxt); + srp->u = BigIntegerFromBytes(dig, SHA512_DIGESTSIZE); + + /* hash: (H(N) xor H(g)) | H(U) | s | A | B */ + SHA512Update(&SHA512_CLIENT_CTXP(srp)->hash, pubkey, pubkeylen); + + gb = BigIntegerFromBytes(pubkey, pubkeylen); + /* reject B == 0, B >= modulus */ + if(BigIntegerCmp(gb, srp->modulus) >= 0 || BigIntegerCmpInt(gb, 0) == 0) { + BigIntegerFree(gb); + cstr_clear_free(s); + return SRP_ERROR; + } + e = BigIntegerFromInt(0); + srp->key = BigIntegerFromInt(0); + /* unblind g^b (mod N) */ + BigIntegerSub(srp->key, srp->modulus, srp->verifier); + /* use e as temporary, e == -k*v (mod N) */ + BigIntegerMul(e, k, srp->key, srp->bctx); + BigIntegerAdd(e, e, gb); + BigIntegerMod(gb, e, srp->modulus, srp->bctx); + + /* compute gb^(a + ux) (mod N) */ + BigIntegerMul(e, srp->password, srp->u, srp->bctx); + BigIntegerAdd(e, e, srp->secret); /* e = a + ux */ + + BigIntegerModExp(srp->key, gb, e, srp->modulus, srp->bctx, srp->accel); + BigIntegerClearFree(e); + BigIntegerClearFree(gb); + + /* convert srp->key into a session key, update hash states */ + BigIntegerToCstr(srp->key, s); + SHA512Init(&ctxt); + SHA512Update(&ctxt, s->data, s->length); + SHA512Final((unsigned char*)&SHA512_CLIENT_CTXP(srp)->k, &ctxt); + cstr_clear_free(s); + + /* hash: (H(N) xor H(g)) | H(U) | s | A | B | K */ + SHA512Update(&SHA512_CLIENT_CTXP(srp)->hash, SHA512_CLIENT_CTXP(srp)->k, SRP6_SHA512_KEY_LEN); + /* hash: (H(N) xor H(g)) | H(U) | s | A | B | K | ex_data */ + if(srp->ex_data->length > 0) + SHA512Update(&SHA512_CLIENT_CTXP(srp)->hash, + srp->ex_data->data, srp->ex_data->length); + if(result) { + if(*result == NULL) + *result = cstr_new(); + cstr_setn(*result, (const char*)SHA512_CLIENT_CTXP(srp)->k, SRP6_SHA512_KEY_LEN); + } + + return SRP_SUCCESS; +} + +static SRP_RESULT +srp6a_sha512_client_key(SRP * srp, cstr ** result, + const unsigned char * pubkey, int pubkeylen) +{ + SRP_RESULT ret; + BigInteger k; + cstr * s; + SHA512_CTX ctxt; + unsigned char dig[SHA512_DIGESTSIZE]; + + SHA512Init(&ctxt); + s = cstr_new(); + BigIntegerToCstr(srp->modulus, s); + SHA512Update(&ctxt, s->data, s->length); + if(srp->flags & SRP_FLAG_LEFT_PAD) + BigIntegerToCstrEx(srp->generator, s, s->length); + else + BigIntegerToCstr(srp->generator, s); + SHA512Update(&ctxt, s->data, s->length); + SHA512Final(dig, &ctxt); + cstr_free(s); + + k = BigIntegerFromBytes(dig, SHA512_DIGESTSIZE); + if(BigIntegerCmpInt(k, 0) == 0) + ret = SRP_ERROR; + else + ret = srp6_sha512_client_key_ex(srp, result, pubkey, pubkeylen, k); + BigIntegerClearFree(k); + return ret; +} + +static SRP_RESULT +srp6_sha512_client_verify(SRP * srp, const unsigned char * proof, int prooflen) +{ + unsigned char expected[SHA512_DIGESTSIZE]; + + SHA512Final(expected, &SHA512_CLIENT_CTXP(srp)->ckhash); + if(prooflen == SHA512_DIGESTSIZE && memcmp(expected, proof, prooflen) == 0) + return SRP_SUCCESS; + else + return SRP_ERROR; +} + +static SRP_RESULT +srp6_sha512_client_respond(SRP * srp, cstr ** proof) +{ + if(proof == NULL) + return SRP_ERROR; + + if(*proof == NULL) + *proof = cstr_new(); + + /* proof contains client's response */ + cstr_set_length(*proof, SHA512_DIGESTSIZE); + SHA512Final((unsigned char*)(*proof)->data, &SHA512_CLIENT_CTXP(srp)->hash); + + /* ckhash: A | M | K */ + SHA512Update(&SHA512_CLIENT_CTXP(srp)->ckhash, (*proof)->data, (*proof)->length); + SHA512Update(&SHA512_CLIENT_CTXP(srp)->ckhash, SHA512_CLIENT_CTXP(srp)->k, SRP6_SHA512_KEY_LEN); + return SRP_SUCCESS; +} + +static SRP_METHOD srp6a_sha512_client_meth = { + "SRP-6a sha512 client (tjw)", + srp6a_sha512_client_init, + srp6_sha512_client_finish, + srp6_sha512_client_params, + srp6_sha512_client_auth, + srp6_sha512_client_passwd, + srp6_sha512_client_genpub, + srp6a_sha512_client_key, + srp6_sha512_client_verify, + srp6_sha512_client_respond, + NULL +}; + +_TYPE( SRP_METHOD * ) +SRP6a_sha512_client_method() +{ + return &srp6a_sha512_client_meth; +} -- cgit v1.1-32-gdbae