X-Git-Url: https://git.openssl.org/gitweb/?p=openssl.git;a=blobdiff_plain;f=crypto%2Fsrp%2Fsrp_vfy.c;h=397d26c9d7e0b835f85b72678c69539895849351;hp=f99fa1b278702cb23d68d7b70fbf37e59caa47e0;hb=51f03f12270cdebf1dff140cc17925991520fb77;hpb=98370c2dd7dc32cecd7bb7d940383846fa435f25 diff --git a/crypto/srp/srp_vfy.c b/crypto/srp/srp_vfy.c index f99fa1b278..397d26c9d7 100644 --- a/crypto/srp/srp_vfy.c +++ b/crypto/srp/srp_vfy.c @@ -1,138 +1,176 @@ /* - * Copyright 2011-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2004-2018 The OpenSSL Project Authors. All Rights Reserved. + * Copyright (c) 2004, EdelKey Project. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html + * + * Originally written by Christophe Renou and Peter Sylvester, + * for the EdelKey project. */ #ifndef OPENSSL_NO_SRP # include "internal/cryptlib.h" +# include "internal/evp_int.h" # include # include # include # include # include # include +# include # define SRP_RANDOM_SALT_LEN 20 # define MAX_LEN 2500 -static char b64table[] = - "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./"; - /* - * the following two conversion routines have been inspired by code from - * Stanford + * Note that SRP uses its own variant of base 64 encoding. A different base64 + * alphabet is used and no padding '=' characters are added. Instead we pad to + * the front with 0 bytes and subsequently strip off leading encoded padding. + * This variant is used for compatibility with other SRP implementations - + * notably libsrp, but also others. It is also required for backwards + * compatibility in order to load verifier files from other OpenSSL versions. */ /* * Convert a base64 string into raw byte array representation. + * Returns the length of the decoded data, or -1 on error. */ -static int t_fromb64(unsigned char *a, const char *src) +static int t_fromb64(unsigned char *a, size_t alen, const char *src) { - char *loc; - int i, j; - int size; + EVP_ENCODE_CTX *ctx; + int outl = 0, outl2 = 0; + size_t size, padsize; + const unsigned char *pad = (const unsigned char *)"00"; - while (*src && (*src == ' ' || *src == '\t' || *src == '\n')) + while (*src == ' ' || *src == '\t' || *src == '\n') ++src; size = strlen(src); - i = 0; - while (i < size) { - loc = strchr(b64table, src[i]); - if (loc == (char *)0) - break; - else - a[i] = loc - b64table; - ++i; + padsize = 4 - (size & 3); + padsize &= 3; + + /* Four bytes in src become three bytes output. */ + if (size > INT_MAX || ((size + padsize) / 4) * 3 > alen) + return -1; + + ctx = EVP_ENCODE_CTX_new(); + if (ctx == NULL) + return -1; + + /* + * This should never occur because 1 byte of data always requires 2 bytes of + * encoding, i.e. + * 0 bytes unencoded = 0 bytes encoded + * 1 byte unencoded = 2 bytes encoded + * 2 bytes unencoded = 3 bytes encoded + * 3 bytes unencoded = 4 bytes encoded + * 4 bytes unencoded = 6 bytes encoded + * etc + */ + if (padsize == 3) { + outl = -1; + goto err; } - /* if nothing valid to process we have a zero length response */ - if (i == 0) - return 0; - size = i; - i = size - 1; - j = size; - while (1) { - a[j] = a[i]; - if (--i < 0) - break; - a[j] |= (a[i] & 3) << 6; - --j; - a[j] = (unsigned char)((a[i] & 0x3c) >> 2); - if (--i < 0) - break; - a[j] |= (a[i] & 0xf) << 4; - --j; - a[j] = (unsigned char)((a[i] & 0x30) >> 4); - if (--i < 0) - break; - a[j] |= (a[i] << 2); - - a[--j] = 0; - if (--i < 0) - break; + + /* Valid padsize values are now 0, 1 or 2 */ + + EVP_DecodeInit(ctx); + evp_encode_ctx_set_flags(ctx, EVP_ENCODE_CTX_USE_SRP_ALPHABET); + + /* Add any encoded padding that is required */ + if (padsize != 0 + && EVP_DecodeUpdate(ctx, a, &outl, pad, padsize) < 0) { + outl = -1; + goto err; } - while (a[j] == 0 && j <= size) - ++j; - i = 0; - while (j <= size) - a[i++] = a[j++]; - return i; + if (EVP_DecodeUpdate(ctx, a, &outl2, (const unsigned char *)src, size) < 0) { + outl = -1; + goto err; + } + outl += outl2; + EVP_DecodeFinal(ctx, a + outl, &outl2); + outl += outl2; + + /* Strip off the leading padding */ + if (padsize != 0) { + if ((int)padsize >= outl) { + outl = -1; + goto err; + } + + /* + * If we added 1 byte of padding prior to encoding then we have 2 bytes + * of "real" data which gets spread across 4 encoded bytes like this: + * (6 bits pad)(2 bits pad | 4 bits data)(6 bits data)(6 bits data) + * So 1 byte of pre-encoding padding results in 1 full byte of encoded + * padding. + * If we added 2 bytes of padding prior to encoding this gets encoded + * as: + * (6 bits pad)(6 bits pad)(4 bits pad | 2 bits data)(6 bits data) + * So 2 bytes of pre-encoding padding results in 2 full bytes of encoded + * padding, i.e. we have to strip the same number of bytes of padding + * from the encoded data as we added to the pre-encoded data. + */ + memmove(a, a + padsize, outl - padsize); + outl -= padsize; + } + + err: + EVP_ENCODE_CTX_free(ctx); + + return outl; } /* * Convert a raw byte string into a null-terminated base64 ASCII string. + * Returns 1 on success or 0 on error. */ -static char *t_tob64(char *dst, const unsigned char *src, int size) +static int t_tob64(char *dst, const unsigned char *src, int size) { - int c, pos = size % 3; - unsigned char b0 = 0, b1 = 0, b2 = 0, notleading = 0; - char *olddst = dst; - - switch (pos) { - case 1: - b2 = src[0]; - break; - case 2: - b1 = src[0]; - b2 = src[1]; - break; + EVP_ENCODE_CTX *ctx = EVP_ENCODE_CTX_new(); + int outl = 0, outl2 = 0; + unsigned char pad[2] = {0, 0}; + size_t leadz = 0; + + if (ctx == NULL) + return 0; + + EVP_EncodeInit(ctx); + evp_encode_ctx_set_flags(ctx, EVP_ENCODE_CTX_NO_NEWLINES + | EVP_ENCODE_CTX_USE_SRP_ALPHABET); + + /* + * We pad at the front with zero bytes until the length is a multiple of 3 + * so that EVP_EncodeUpdate/EVP_EncodeFinal does not add any of its own "=" + * padding + */ + leadz = 3 - (size % 3); + if (leadz != 3 + && !EVP_EncodeUpdate(ctx, (unsigned char *)dst, &outl, pad, + leadz)) { + EVP_ENCODE_CTX_free(ctx); + return 0; } - while (1) { - c = (b0 & 0xfc) >> 2; - if (notleading || c != 0) { - *dst++ = b64table[c]; - notleading = 1; - } - c = ((b0 & 3) << 4) | ((b1 & 0xf0) >> 4); - if (notleading || c != 0) { - *dst++ = b64table[c]; - notleading = 1; - } - c = ((b1 & 0xf) << 2) | ((b2 & 0xc0) >> 6); - if (notleading || c != 0) { - *dst++ = b64table[c]; - notleading = 1; - } - c = b2 & 0x3f; - if (notleading || c != 0) { - *dst++ = b64table[c]; - notleading = 1; - } - if (pos >= size) - break; - else { - b0 = src[pos++]; - b1 = src[pos++]; - b2 = src[pos++]; - } + if (!EVP_EncodeUpdate(ctx, (unsigned char *)dst + outl, &outl2, src, + size)) { + EVP_ENCODE_CTX_free(ctx); + return 0; + } + outl += outl2; + EVP_EncodeFinal(ctx, (unsigned char *)dst + outl, &outl2); + outl += outl2; + + /* Strip the encoded padding at the front */ + if (leadz != 3) { + memmove(dst, dst + leadz, outl - leadz); + dst[outl - leadz] = '\0'; } - *dst++ = '\0'; - return olddst; + EVP_ENCODE_CTX_free(ctx); + return 1; } void SRP_user_pwd_free(SRP_user_pwd *user_pwd) @@ -148,9 +186,12 @@ void SRP_user_pwd_free(SRP_user_pwd *user_pwd) static SRP_user_pwd *SRP_user_pwd_new(void) { - SRP_user_pwd *ret = OPENSSL_malloc(sizeof(*ret)); - if (ret == NULL) + SRP_user_pwd *ret; + + if ((ret = OPENSSL_malloc(sizeof(*ret))) == NULL) { + /* SRPerr(SRP_F_SRP_USER_PWD_NEW, ERR_R_MALLOC_FAILURE); */ /*ckerr_ignore*/ return NULL; + } ret->N = NULL; ret->g = NULL; ret->s = NULL; @@ -181,13 +222,25 @@ static int SRP_user_pwd_set_sv(SRP_user_pwd *vinfo, const char *s, unsigned char tmp[MAX_LEN]; int len; - if (strlen(s) > MAX_LEN || strlen(v) > MAX_LEN) + vinfo->v = NULL; + vinfo->s = NULL; + + len = t_fromb64(tmp, sizeof(tmp), v); + if (len < 0) return 0; - len = t_fromb64(tmp, v); if (NULL == (vinfo->v = BN_bin2bn(tmp, len, NULL))) return 0; - len = t_fromb64(tmp, s); - return ((vinfo->s = BN_bin2bn(tmp, len, NULL)) != NULL); + len = t_fromb64(tmp, sizeof(tmp), s); + if (len < 0) + goto err; + vinfo->s = BN_bin2bn(tmp, len, NULL); + if (vinfo->s == NULL) + goto err; + return 1; + err: + BN_free(vinfo->v); + vinfo->v = NULL; + return 0; } static int SRP_user_pwd_set_sv_BN(SRP_user_pwd *vinfo, BIGNUM *s, BIGNUM *v) @@ -257,10 +310,13 @@ static SRP_gN_cache *SRP_gN_new_init(const char *ch) if (newgN == NULL) return NULL; + len = t_fromb64(tmp, sizeof(tmp), ch); + if (len < 0) + goto err; + if ((newgN->b64_bn = OPENSSL_strdup(ch)) == NULL) goto err; - len = t_fromb64(tmp, ch); if ((newgN->bn = BN_bin2bn(tmp, len, NULL))) return newgN; @@ -284,12 +340,13 @@ static SRP_gN *SRP_get_gN_by_id(const char *id, STACK_OF(SRP_gN) *gN_tab) int i; SRP_gN *gN; - if (gN_tab != NULL) + if (gN_tab != NULL) { for (i = 0; i < sk_SRP_gN_num(gN_tab); i++) { gN = sk_SRP_gN_value(gN_tab, i); if (gN && (id == NULL || strcmp(gN->id, id) == 0)) return gN; } + } return SRP_get_default_gN(id); } @@ -318,9 +375,13 @@ static BIGNUM *SRP_gN_place_bn(STACK_OF(SRP_gN_cache) *gN_cache, char *ch) } /* - * this function parses verifier file. Format is: - * string(index):base64(N):base64(g):0 - * string(username):base64(v):base64(salt):int(index) + * This function parses the verifier file generated by the srp app. + * The format for each entry is: + * V base64(verifier) base64(salt) username gNid userinfo(optional) + * or + * I base64(N) base64(g) + * Note that base64 is the SRP variant of base64 encoding described + * in t_fromb64(). */ int SRP_VBASE_init(SRP_VBASE *vb, char *verifier_file) @@ -453,7 +514,14 @@ static SRP_user_pwd *find_user(SRP_VBASE *vb, char *username) return NULL; } - #if OPENSSL_API_COMPAT < 0x10100000L +int SRP_VBASE_add0_user(SRP_VBASE *vb, SRP_user_pwd *user_pwd) +{ + if (sk_SRP_user_pwd_push(vb->users_pwd, user_pwd) <= 0) + return 0; + return 1; +} + +# if OPENSSL_API_COMPAT < 0x10100000L /* * DEPRECATED: use SRP_VBASE_get1_by_user instead. * This method ignores the configured seed and fails for an unknown user. @@ -464,7 +532,7 @@ SRP_user_pwd *SRP_VBASE_get_by_user(SRP_VBASE *vb, char *username) { return find_user(vb, username); } -#endif +# endif /* * Ownership of the returned pointer is released to the caller. @@ -497,13 +565,15 @@ SRP_user_pwd *SRP_VBASE_get1_by_user(SRP_VBASE *vb, char *username) if (!SRP_user_pwd_set_ids(user, username, NULL)) goto err; - if (RAND_bytes(digv, SHA_DIGEST_LENGTH) <= 0) + if (RAND_priv_bytes(digv, SHA_DIGEST_LENGTH) <= 0) goto err; ctxt = EVP_MD_CTX_new(); - EVP_DigestInit_ex(ctxt, EVP_sha1(), NULL); - EVP_DigestUpdate(ctxt, vb->seed_key, strlen(vb->seed_key)); - EVP_DigestUpdate(ctxt, username, strlen(username)); - EVP_DigestFinal_ex(ctxt, digs, NULL); + if (ctxt == NULL + || !EVP_DigestInit_ex(ctxt, EVP_sha1(), NULL) + || !EVP_DigestUpdate(ctxt, vb->seed_key, strlen(vb->seed_key)) + || !EVP_DigestUpdate(ctxt, username, strlen(username)) + || !EVP_DigestFinal_ex(ctxt, digs, NULL)) + goto err; EVP_MD_CTX_free(ctxt); ctxt = NULL; if (SRP_user_pwd_set_sv_BN(user, @@ -537,17 +607,17 @@ char *SRP_create_verifier(const char *user, const char *pass, char **salt, goto err; if (N) { - if ((len = t_fromb64(tmp, N)) == 0) + if ((len = t_fromb64(tmp, sizeof(tmp), N)) <= 0) goto err; N_bn_alloc = BN_bin2bn(tmp, len, NULL); N_bn = N_bn_alloc; - if ((len = t_fromb64(tmp, g)) == 0) + if ((len = t_fromb64(tmp, sizeof(tmp) ,g)) <= 0) goto err; g_bn_alloc = BN_bin2bn(tmp, len, NULL); g_bn = g_bn_alloc; defgNid = "*"; } else { - SRP_gN *gN = SRP_get_gN_by_id(g, NULL); + SRP_gN *gN = SRP_get_default_gN(g); if (gN == NULL) goto err; N_bn = gN->N; @@ -561,7 +631,7 @@ char *SRP_create_verifier(const char *user, const char *pass, char **salt, s = BN_bin2bn(tmp2, SRP_RANDOM_SALT_LEN, NULL); } else { - if ((len = t_fromb64(tmp2, *salt)) == 0) + if ((len = t_fromb64(tmp2, sizeof(tmp2), *salt)) <= 0) goto err; s = BN_bin2bn(tmp2, len, NULL); }