diff options
author | Matt Caswell <matt@openssl.org> | 2018-04-09 16:50:20 +0200 |
---|---|---|
committer | Matt Caswell <matt@openssl.org> | 2018-04-13 10:37:38 +0200 |
commit | 3fd59700357072c567785a1fb1430a55ef7bd45b (patch) | |
tree | 500ba38bfa6a14315b0832c8bb263e7cf31c4d85 /crypto/srp/srp_vfy.c | |
parent | Change SRP functions to use EVP_EncodeUpdate/EVP_DecodeUpdate functions (diff) | |
download | openssl-3fd59700357072c567785a1fb1430a55ef7bd45b.tar.xz openssl-3fd59700357072c567785a1fb1430a55ef7bd45b.zip |
Add support for the SRP base64 alphabet
Historically we used to implement standalone base64 code for SRP. This
was replaced by commit 3d3f21aa with the standard base64 processing code.
However, the SRP base64 code was designed to be compatible with other SRP
libraries (notably libsrp, but also others) that use a variant of standard
base64. Specifically a different alphabet is used and no padding '='
characters are used. Instead 0 padding is added to the front of the string.
By changing to standard base64 we change the behaviour of the API which may
impact interoperability. It also means that SRP verifier files created prior
to 1.1.1 would not be readable in 1.1.1 and vice versa.
Instead we expand our standard base64 processing with the capability to be
able to read and generate the SRP base64 variant.
Reviewed-by: Andy Polyakov <appro@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/5925)
Diffstat (limited to '')
-rw-r--r-- | crypto/srp/srp_vfy.c | 101 |
1 files changed, 95 insertions, 6 deletions
diff --git a/crypto/srp/srp_vfy.c b/crypto/srp/srp_vfy.c index 1eba5a7cb1..1bf2f267ed 100644 --- a/crypto/srp/srp_vfy.c +++ b/crypto/srp/srp_vfy.c @@ -26,6 +26,15 @@ # define MAX_LEN 2500 /* + * 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. */ @@ -33,24 +42,79 @@ static int t_fromb64(unsigned char *a, size_t alen, const char *src) { EVP_ENCODE_CTX *ctx; int outl = 0, outl2 = 0; - size_t size = strlen(src); + size_t size, padsize; + const unsigned char *pad = (const unsigned char *)"00"; - if (size > INT_MAX) + while (*src == ' ' || *src == '\t' || *src == '\n') + ++src; + size = strlen(src); + 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) + return -1; + + /* Valid padsize values are now 0, 1 or 2 */ + EVP_DecodeInit(ctx); - if (EVP_DecodeUpdate(ctx, a, &outl, (const unsigned char *)src, size) < 0) { + 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) { EVP_ENCODE_CTX_free(ctx); return -1; } + if (EVP_DecodeUpdate(ctx, a, &outl2, (const unsigned char *)src, size) < 0) { + EVP_ENCODE_CTX_free(ctx); + return -1; + } + outl += outl2; EVP_DecodeFinal(ctx, a + outl, &outl2); + outl += outl2; + + /* Strip off the leading padding */ + if (padsize != 0) { + if ((int)padsize >= outl) + return -1; + /* + * 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; + } EVP_ENCODE_CTX_free(ctx); - return outl + outl2; + + return outl; } /* @@ -61,18 +125,43 @@ static int t_tob64(char *dst, const unsigned char *src, int size) { 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_set_flags(ctx, EVP_ENCODE_CTX_NO_NEWLINES + | EVP_ENCODE_CTX_USE_SRP_ALPHABET); - if (!EVP_EncodeUpdate(ctx, (unsigned char *)dst, &outl, src, size)) { + /* + * 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; } + + 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'; + } EVP_ENCODE_CTX_free(ctx); return 1; |