[openssl-commits] [openssl] master update

Matt Caswell matt at openssl.org
Fri Apr 13 08:41:09 UTC 2018


The branch master has been updated
       via  76fd7a1d61924ba5ee45224454cc3754b672efbf (commit)
       via  3fd59700357072c567785a1fb1430a55ef7bd45b (commit)
       via  c080461448815dab809661080ee5e21417478fb4 (commit)
      from  0320e8e2869fb6cde4579375e65f6d576bbec95e (commit)


- Log -----------------------------------------------------------------
commit 76fd7a1d61924ba5ee45224454cc3754b672efbf
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Apr 10 14:51:12 2018 +0100

    Add a test for SRP
    
    Reviewed-by: Andy Polyakov <appro at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/5925)

commit 3fd59700357072c567785a1fb1430a55ef7bd45b
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Apr 9 15:50:20 2018 +0100

    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 at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/5925)

commit c080461448815dab809661080ee5e21417478fb4
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Apr 9 15:06:50 2018 +0100

    Change SRP functions to use EVP_EncodeUpdate/EVP_DecodeUpdate functions
    
    Previously they were using EVP_EncodeBlock/EVP_DecodeBlock. These are low
    level functions that do not handle padding characters. This was causing
    the SRP code to fail. One side effect of using EVP_EncodeUpdate is that
    it inserts newlines which is not what we need in SRP so we add a flag to
    avoid that.
    
    Reviewed-by: Andy Polyakov <appro at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/5925)

-----------------------------------------------------------------------

Summary of changes:
 crypto/evp/encode.c                         | 150 +++++++++++++-----
 crypto/evp/evp_locl.h                       |   2 +-
 crypto/include/internal/evp_int.h           |   9 ++
 crypto/srp/srp_vfy.c                        | 131 +++++++++++++++-
 test/recipes/90-test_sslapi.t               |  10 +-
 test/recipes/90-test_sslapi_data/passwd.txt |   1 +
 test/sslapitest.c                           | 230 +++++++++++++++++++++++++++-
 7 files changed, 487 insertions(+), 46 deletions(-)
 create mode 100644 test/recipes/90-test_sslapi_data/passwd.txt

diff --git a/crypto/evp/encode.c b/crypto/evp/encode.c
index 17198ff..88e5a17 100644
--- a/crypto/evp/encode.c
+++ b/crypto/evp/encode.c
@@ -12,10 +12,17 @@
 #include "internal/cryptlib.h"
 #include <openssl/evp.h>
 #include "evp_locl.h"
+#include "internal/evp_int.h"
+
+static unsigned char conv_ascii2bin(unsigned char a,
+                                    const unsigned char *table);
+static int evp_encodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
+                               const unsigned char *f, int dlen);
+static int evp_decodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
+                               const unsigned char *f, int n);
 
-static unsigned char conv_ascii2bin(unsigned char a);
 #ifndef CHARSET_EBCDIC
-# define conv_bin2ascii(a)       (data_bin2ascii[(a)&0x3f])
+# define conv_bin2ascii(a, table)       ((table)[(a)&0x3f])
 #else
 /*
  * We assume that PEM encoded files are EBCDIC files (i.e., printable text
@@ -23,7 +30,7 @@ static unsigned char conv_ascii2bin(unsigned char a);
  * (text) format again. (No need for conversion in the conv_bin2ascii macro,
  * as the underlying textstring data_bin2ascii[] is already EBCDIC)
  */
-# define conv_bin2ascii(a)       (data_bin2ascii[(a)&0x3f])
+# define conv_bin2ascii(a, table)       ((table)[(a)&0x3f])
 #endif
 
 /*-
@@ -38,8 +45,13 @@ static unsigned char conv_ascii2bin(unsigned char a);
 #define CHUNKS_PER_LINE (64/4)
 #define CHAR_PER_LINE   (64+1)
 
-static const unsigned char data_bin2ascii[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\
-abcdefghijklmnopqrstuvwxyz0123456789+/";
+static const unsigned char data_bin2ascii[65] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/* SRP uses a different base64 alphabet */
+static const unsigned char srpdata_bin2ascii[65] =
+    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./";
+
 
 /*-
  * 0xF0 is a EOLN
@@ -76,20 +88,39 @@ static const unsigned char data_ascii2bin[128] = {
     0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 };
 
+static const unsigned char srpdata_ascii2bin[128] = {
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xE0, 0xF0, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0x3E, 0x3F,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF,
+    0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+    0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+    0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
+    0x21, 0x22, 0x23, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,
+    0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32,
+    0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
+    0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
 #ifndef CHARSET_EBCDIC
-static unsigned char conv_ascii2bin(unsigned char a)
+static unsigned char conv_ascii2bin(unsigned char a, const unsigned char *table)
 {
     if (a & 0x80)
         return B64_ERROR;
-    return data_ascii2bin[a];
+    return table[a];
 }
 #else
-static unsigned char conv_ascii2bin(unsigned char a)
+static unsigned char conv_ascii2bin(unsigned char a, const unsigned char *table)
 {
     a = os_toascii[a];
     if (a & 0x80)
         return B64_ERROR;
-    return data_ascii2bin[a];
+    return table[a];
 }
 #endif
 
@@ -115,11 +146,17 @@ int EVP_ENCODE_CTX_num(EVP_ENCODE_CTX *ctx)
     return ctx->num;
 }
 
+void evp_encode_ctx_set_flags(EVP_ENCODE_CTX *ctx, unsigned int flags)
+{
+    ctx->flags = flags;
+}
+
 void EVP_EncodeInit(EVP_ENCODE_CTX *ctx)
 {
     ctx->length = 48;
     ctx->num = 0;
     ctx->line_num = 0;
+    ctx->flags = 0;
 }
 
 int EVP_EncodeUpdate(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl,
@@ -142,21 +179,27 @@ int EVP_EncodeUpdate(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl,
         memcpy(&(ctx->enc_data[ctx->num]), in, i);
         in += i;
         inl -= i;
-        j = EVP_EncodeBlock(out, ctx->enc_data, ctx->length);
+        j = evp_encodeblock_int(ctx, out, ctx->enc_data, ctx->length);
         ctx->num = 0;
         out += j;
-        *(out++) = '\n';
+        total = j;
+        if ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0) {
+            *(out++) = '\n';
+            total++;
+        }
         *out = '\0';
-        total = j + 1;
     }
     while (inl >= ctx->length && total <= INT_MAX) {
-        j = EVP_EncodeBlock(out, in, ctx->length);
+        j = evp_encodeblock_int(ctx, out, in, ctx->length);
         in += ctx->length;
         inl -= ctx->length;
         out += j;
-        *(out++) = '\n';
+        total += j;
+        if ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0) {
+            *(out++) = '\n';
+            total++;
+        }
         *out = '\0';
-        total += j + 1;
     }
     if (total > INT_MAX) {
         /* Too much output data! */
@@ -176,35 +219,43 @@ void EVP_EncodeFinal(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl)
     unsigned int ret = 0;
 
     if (ctx->num != 0) {
-        ret = EVP_EncodeBlock(out, ctx->enc_data, ctx->num);
-        out[ret++] = '\n';
+        ret = evp_encodeblock_int(ctx, out, ctx->enc_data, ctx->num);
+        if ((ctx->flags & EVP_ENCODE_CTX_NO_NEWLINES) == 0)
+            out[ret++] = '\n';
         out[ret] = '\0';
         ctx->num = 0;
     }
     *outl = ret;
 }
 
-int EVP_EncodeBlock(unsigned char *t, const unsigned char *f, int dlen)
+static int evp_encodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
+                               const unsigned char *f, int dlen)
 {
     int i, ret = 0;
     unsigned long l;
+    const unsigned char *table;
+
+    if (ctx != NULL && (ctx->flags & EVP_ENCODE_CTX_USE_SRP_ALPHABET) != 0)
+        table = srpdata_bin2ascii;
+    else
+        table = data_bin2ascii;
 
     for (i = dlen; i > 0; i -= 3) {
         if (i >= 3) {
             l = (((unsigned long)f[0]) << 16L) |
                 (((unsigned long)f[1]) << 8L) | f[2];
-            *(t++) = conv_bin2ascii(l >> 18L);
-            *(t++) = conv_bin2ascii(l >> 12L);
-            *(t++) = conv_bin2ascii(l >> 6L);
-            *(t++) = conv_bin2ascii(l);
+            *(t++) = conv_bin2ascii(l >> 18L, table);
+            *(t++) = conv_bin2ascii(l >> 12L, table);
+            *(t++) = conv_bin2ascii(l >> 6L, table);
+            *(t++) = conv_bin2ascii(l, table);
         } else {
             l = ((unsigned long)f[0]) << 16L;
             if (i == 2)
                 l |= ((unsigned long)f[1] << 8L);
 
-            *(t++) = conv_bin2ascii(l >> 18L);
-            *(t++) = conv_bin2ascii(l >> 12L);
-            *(t++) = (i == 1) ? '=' : conv_bin2ascii(l >> 6L);
+            *(t++) = conv_bin2ascii(l >> 18L, table);
+            *(t++) = conv_bin2ascii(l >> 12L, table);
+            *(t++) = (i == 1) ? '=' : conv_bin2ascii(l >> 6L, table);
             *(t++) = '=';
         }
         ret += 4;
@@ -215,13 +266,18 @@ int EVP_EncodeBlock(unsigned char *t, const unsigned char *f, int dlen)
     return ret;
 }
 
+int EVP_EncodeBlock(unsigned char *t, const unsigned char *f, int dlen)
+{
+    return evp_encodeblock_int(NULL, t, f, dlen);
+}
+
 void EVP_DecodeInit(EVP_ENCODE_CTX *ctx)
 {
-    /* Only ctx->num is used during decoding. */
+    /* Only ctx->num and ctx->flags are used during decoding. */
     ctx->num = 0;
     ctx->length = 0;
     ctx->line_num = 0;
-    ctx->expect_nl = 0;
+    ctx->flags = 0;
 }
 
 /*-
@@ -249,6 +305,7 @@ int EVP_DecodeUpdate(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl,
 {
     int seof = 0, eof = 0, rv = -1, ret = 0, i, v, tmp, n, decoded_len;
     unsigned char *d;
+    const unsigned char *table;
 
     n = ctx->num;
     d = ctx->enc_data;
@@ -265,9 +322,14 @@ int EVP_DecodeUpdate(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl,
         goto end;
     }
 
+    if ((ctx->flags & EVP_ENCODE_CTX_USE_SRP_ALPHABET) != 0)
+        table = srpdata_ascii2bin;
+    else
+        table = data_ascii2bin;
+
     for (i = 0; i < inl; i++) {
         tmp = *(in++);
-        v = conv_ascii2bin(tmp);
+        v = conv_ascii2bin(tmp, table);
         if (v == B64_ERROR) {
             rv = -1;
             goto end;
@@ -307,7 +369,7 @@ int EVP_DecodeUpdate(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl,
         }
 
         if (n == 64) {
-            decoded_len = EVP_DecodeBlock(out, d, n);
+            decoded_len = evp_decodeblock_int(ctx, out, d, n);
             n = 0;
             if (decoded_len < 0 || eof > decoded_len) {
                 rv = -1;
@@ -326,7 +388,7 @@ int EVP_DecodeUpdate(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl,
 tail:
     if (n > 0) {
         if ((n & 3) == 0) {
-            decoded_len = EVP_DecodeBlock(out, d, n);
+            decoded_len = evp_decodeblock_int(ctx, out, d, n);
             n = 0;
             if (decoded_len < 0 || eof > decoded_len) {
                 rv = -1;
@@ -348,13 +410,20 @@ end:
     return rv;
 }
 
-int EVP_DecodeBlock(unsigned char *t, const unsigned char *f, int n)
+static int evp_decodeblock_int(EVP_ENCODE_CTX *ctx, unsigned char *t,
+                               const unsigned char *f, int n)
 {
     int i, ret = 0, a, b, c, d;
     unsigned long l;
+    const unsigned char *table;
+
+    if (ctx != NULL && (ctx->flags & EVP_ENCODE_CTX_USE_SRP_ALPHABET) != 0)
+        table = srpdata_ascii2bin;
+    else
+        table = data_ascii2bin;
 
     /* trim white space from the start of the line. */
-    while ((conv_ascii2bin(*f) == B64_WS) && (n > 0)) {
+    while ((conv_ascii2bin(*f, table) == B64_WS) && (n > 0)) {
         f++;
         n--;
     }
@@ -363,17 +432,17 @@ int EVP_DecodeBlock(unsigned char *t, const unsigned char *f, int n)
      * strip off stuff at the end of the line ascii2bin values B64_WS,
      * B64_EOLN, B64_EOLN and B64_EOF
      */
-    while ((n > 3) && (B64_NOT_BASE64(conv_ascii2bin(f[n - 1]))))
+    while ((n > 3) && (B64_NOT_BASE64(conv_ascii2bin(f[n - 1], table))))
         n--;
 
     if (n % 4 != 0)
         return -1;
 
     for (i = 0; i < n; i += 4) {
-        a = conv_ascii2bin(*(f++));
-        b = conv_ascii2bin(*(f++));
-        c = conv_ascii2bin(*(f++));
-        d = conv_ascii2bin(*(f++));
+        a = conv_ascii2bin(*(f++), table);
+        b = conv_ascii2bin(*(f++), table);
+        c = conv_ascii2bin(*(f++), table);
+        d = conv_ascii2bin(*(f++), table);
         if ((a & 0x80) || (b & 0x80) || (c & 0x80) || (d & 0x80))
             return -1;
         l = ((((unsigned long)a) << 18L) |
@@ -387,13 +456,18 @@ int EVP_DecodeBlock(unsigned char *t, const unsigned char *f, int n)
     return ret;
 }
 
+int EVP_DecodeBlock(unsigned char *t, const unsigned char *f, int n)
+{
+    return evp_decodeblock_int(NULL, t, f, n);
+}
+
 int EVP_DecodeFinal(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl)
 {
     int i;
 
     *outl = 0;
     if (ctx->num != 0) {
-        i = EVP_DecodeBlock(out, ctx->enc_data, ctx->num);
+        i = evp_decodeblock_int(ctx, out, ctx->enc_data, ctx->num);
         if (i < 0)
             return -1;
         ctx->num = 0;
diff --git a/crypto/evp/evp_locl.h b/crypto/evp/evp_locl.h
index 209577b..d5dbbeb 100644
--- a/crypto/evp/evp_locl.h
+++ b/crypto/evp/evp_locl.h
@@ -59,7 +59,7 @@ struct evp_Encode_Ctx_st {
     unsigned char enc_data[80];
     /* number read on current line */
     int line_num;
-    int expect_nl;
+    unsigned int flags;
 };
 
 typedef struct evp_pbe_st EVP_PBE_CTL;
diff --git a/crypto/include/internal/evp_int.h b/crypto/include/internal/evp_int.h
index 77c8731..691b28b 100644
--- a/crypto/include/internal/evp_int.h
+++ b/crypto/include/internal/evp_int.h
@@ -7,6 +7,7 @@
  * https://www.openssl.org/source/license.html
  */
 
+#include <openssl/evp.h>
 #include "internal/refcount.h"
 
 struct evp_pkey_ctx_st {
@@ -422,3 +423,11 @@ void evp_app_cleanup_int(void);
 #ifndef TLS1_1_VERSION
 # define TLS1_1_VERSION   0x0302
 #endif
+
+void evp_encode_ctx_set_flags(EVP_ENCODE_CTX *ctx, unsigned int flags);
+
+/* EVP_ENCODE_CTX flags */
+/* Don't generate new lines when encoding */
+#define EVP_ENCODE_CTX_NO_NEWLINES          1
+/* Use the SRP base64 alphabet instead of the standard one */
+#define EVP_ENCODE_CTX_USE_SRP_ALPHABET     2
diff --git a/crypto/srp/srp_vfy.c b/crypto/srp/srp_vfy.c
index 38d1a0f..1bf2f26 100644
--- a/crypto/srp/srp_vfy.c
+++ b/crypto/srp/srp_vfy.c
@@ -13,6 +13,7 @@
 
 #ifndef OPENSSL_NO_SRP
 # include "internal/cryptlib.h"
+# include "internal/evp_int.h"
 # include <openssl/sha.h>
 # include <openssl/srp.h>
 # include <openssl/evp.h>
@@ -25,25 +26,145 @@
 # 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.
  */
 static int t_fromb64(unsigned char *a, size_t alen, const char *src)
 {
-    size_t size = strlen(src);
+    EVP_ENCODE_CTX *ctx;
+    int outl = 0, outl2 = 0;
+    size_t size, padsize;
+    const unsigned char *pad = (const unsigned char *)"00";
+
+    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 / 4) * 3 > alen)
+    if (size > INT_MAX || ((size + padsize) / 4) * 3 > alen)
         return -1;
 
-    return EVP_DecodeBlock(a, (unsigned char *)src, (int)size);
+    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);
+    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;
 }
 
 /*
  * Convert a raw byte string into a null-terminated base64 ASCII string.
+ * Returns 1 on success or 0 on error.
  */
-static void t_tob64(char *dst, const unsigned char *src, int size)
+static int t_tob64(char *dst, const unsigned char *src, int size)
 {
-    EVP_EncodeBlock((unsigned char *)dst, src, 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_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;
+    }
+
+    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;
 }
 
 void SRP_user_pwd_free(SRP_user_pwd *user_pwd)
diff --git a/test/recipes/90-test_sslapi.t b/test/recipes/90-test_sslapi.t
index efaae3b..08af52d 100644
--- a/test/recipes/90-test_sslapi.t
+++ b/test/recipes/90-test_sslapi.t
@@ -9,6 +9,7 @@
 
 use OpenSSL::Test::Utils;
 use OpenSSL::Test qw/:DEFAULT srctop_file/;
+use File::Temp qw(tempfile);
 
 setup("test_sslapi");
 
@@ -17,5 +18,12 @@ plan skip_all => "No TLS/SSL protocols are supported by this OpenSSL build"
 
 plan tests => 1;
 
+(undef, my $tmpfilename) = tempfile();
+
 ok(run(test(["sslapitest", srctop_file("apps", "server.pem"),
-             srctop_file("apps", "server.pem")])), "running sslapitest");
+             srctop_file("apps", "server.pem"),
+             srctop_file("test", "recipes", "90-test_sslapi_data",
+                         "passwd.txt"), $tmpfilename])),
+             "running sslapitest");
+
+unlink $tmpfilename;
diff --git a/test/recipes/90-test_sslapi_data/passwd.txt b/test/recipes/90-test_sslapi_data/passwd.txt
new file mode 100644
index 0000000..b611aed
--- /dev/null
+++ b/test/recipes/90-test_sslapi_data/passwd.txt
@@ -0,0 +1 @@
+V	1auIY/NQXwKWVeWaYg.YV0AaU.mpHSsZw8PWfrYT0oMTPYekTqGXu6ElyTN64DmK03V3P2yVRdhN0UBxMBujLnTauROkuEep/vp7S5xhW1VK8zg1gtJslTqOp4l.GTJF9x0WYmS6VNRnj5AVi3mgfVJ3nmzlMJUMm7niQxm5awLZZ8xykox1j6MFRa80y02Ub87A88DwqA5wrIM/Uojx9VBxUhTHC.353aBA/rL4O/179rgIBbhID08RA6uLv7pIJQVl5OjYsRu/XzQsgFFW6Wog7PaB.AATqArzXZieZxs/teOiFKPSgKI.76vvVEMQIifSj3hRuVK/immK.9hBCTHYjAv96MUmitb0ErPYJRl2MeBC8M6aHJ8FaMmak.Qv.bwyiqpEjlX1a9KjdBAKIaAswECjeP6G0Gk5v1g5D7ZmP5JUK7Wp/X9sKuZZYOsDwEGfXNmmJG6Y3TETx105HT2QMJ5ti5QCbrd71VWABmVWpHJc03YLUExw6WtYdUW0YHTbRKVntgVe2hOQD.XPtFPn2SwxbGonq1bwEvdCp22uTb5HFSC3I7amCUTZteVmMgqJAcx.x.2yfliESVvpmG.dnDFkp6vsQxch6Q1dV5rDmR4GGSy8FoPSFXc7NS0kCSs.qsTqLSmHN1XMzwrwYuVbItXBwetwxcIcdi.sFG6OLuwRUGaNOXiMwhlDHyQtVfEm3L/KIjPpzLlYRAJWF9M40FIcNsI6xiMNhvUGNO7LaBHKSV3oHlwUWWUnL7Uo/ePH8lBpGadYPxObXZ1/wOcWdJ1Rb5dB9orSSTSvoNrZyALKO.swl7pP7beYq6bUx8qtBJLaqI2zQzr1tnmJi8azVicuFtsDs363ntCRtd1LLT3CX3EBVXMbEy6xgAKWI2GL3HO6v8k3Gv96UeGFN/w5yAz61mbajDrSeJekUaKgfucV8h9tgHNlTA1kGowd2Yn/EQdVc/qSETddySqNC0mXlPW1tgb2ixV6sWbYrb5TLBUdztdw5L2D62Aal.9IjpTEKc4F/gMjYsazIX6nzpXZtWnYP7dIOpSi4c.48B2RIeDrZVMzUF.9QOF9Dk1fy5Z2X91z8J2I0GuqIWKKfwnx4xA3RbGUds1Cv2XvUA1tP7eqtvs/mTsC8KWApNSpL6K.U.Pt0ee6F76CV.ZcBXTbXl9zJZ0H1peiehzZpbuIPLZPtzIHClRQovjqdrlEUzS5VdSgCfNhEUr3ZOpG3cCKO4Lk25jZuQtoFmyxUuRAIXejLizCd727hO7rHZoD.GGm4HiNaH2jgZaftoFhfSBXvPRGYfcj.ZkiLyurNlumMXTduHImB1ZMkZ1af5dggKaQG4bJe9WbF6KYxmeRwV	1oFJIzMwXA0RFKXCGcSV0nAToL5	test	8192	A test user
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 876be31..74ee897 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -14,6 +14,8 @@
 #include <openssl/crypto.h>
 #include <openssl/ssl.h>
 #include <openssl/ocsp.h>
+#include <openssl/srp.h>
+#include <openssl/txt_db.h>
 
 #include "ssltestlib.h"
 #include "testutil.h"
@@ -23,6 +25,8 @@
 
 static char *cert = NULL;
 static char *privkey = NULL;
+static char *srpvfile = NULL;
+static char *tmpfilename = NULL;
 
 #define LOG_BUFFER_SIZE 1024
 static char server_log_buffer[LOG_BUFFER_SIZE + 1] = {0};
@@ -3786,10 +3790,231 @@ static int test_pha_key_update(void)
 }
 #endif
 
+#if !defined(OPENSSL_NO_SRP) && !defined(OPENSSL_NO_TLS1_2)
+
+static SRP_VBASE *vbase = NULL;
+
+static int ssl_srp_cb(SSL *s, int *ad, void *arg)
+{
+    int ret = SSL3_AL_FATAL;
+    char *username;
+    SRP_user_pwd *user = NULL;
+
+    username = SSL_get_srp_username(s);
+    if (username == NULL) {
+        *ad = SSL_AD_INTERNAL_ERROR;
+        goto err;
+    }
+
+    user = SRP_VBASE_get1_by_user(vbase, username);
+    if (user == NULL) {
+        *ad = SSL_AD_INTERNAL_ERROR;
+        goto err;
+    }
+
+    if (SSL_set_srp_server_param(s, user->N, user->g, user->s, user->v,
+                                 user->info) <= 0) {
+        *ad = SSL_AD_INTERNAL_ERROR;
+        goto err;
+    }
+
+    ret = 0;
+
+ err:
+    SRP_user_pwd_free(user);
+    return ret;
+}
+
+static int create_new_vfile(char *userid, char *password, const char *filename)
+{
+    char *gNid = NULL;
+    OPENSSL_STRING *row = OPENSSL_zalloc(sizeof(row) * (DB_NUMBER + 1));
+    TXT_DB *db = NULL;
+    int ret = 0;
+    BIO *out = NULL, *dummy = BIO_new_mem_buf("", 0);
+    size_t i;
+
+    if (!TEST_ptr(dummy) || !TEST_ptr(row))
+        goto end;
+
+    gNid = SRP_create_verifier(userid, password, &row[DB_srpsalt],
+                               &row[DB_srpverifier], NULL, NULL);
+    if (!TEST_ptr(gNid))
+        goto end;
+
+    /*
+     * The only way to create an empty TXT_DB is to provide a BIO with no data
+     * in it!
+     */
+    db = TXT_DB_read(dummy, DB_NUMBER);
+    if (!TEST_ptr(db))
+        goto end;
+
+    out = BIO_new_file(filename, "w");
+    if (!TEST_ptr(out))
+        goto end;
+
+    row[DB_srpid] = OPENSSL_strdup(userid);
+    row[DB_srptype] = OPENSSL_strdup("V");
+    row[DB_srpgN] = OPENSSL_strdup(gNid);
+
+    if (!TEST_ptr(row[DB_srpid])
+            || !TEST_ptr(row[DB_srptype])
+            || !TEST_ptr(row[DB_srpgN])
+            || !TEST_true(TXT_DB_insert(db, row)))
+        goto end;
+
+    row = NULL;
+
+    if (!TXT_DB_write(out, db))
+        goto end;
+
+    ret = 1;
+ end:
+    if (row != NULL) {
+        for (i = 0; i < DB_NUMBER; i++)
+            OPENSSL_free(row[i]);
+    }
+    OPENSSL_free(row);
+    BIO_free(dummy);
+    BIO_free(out);
+    TXT_DB_free(db);
+
+    return ret;
+}
+
+static int create_new_vbase(char *userid, char *password)
+{
+    BIGNUM *verifier = NULL, *salt = NULL;
+    const SRP_gN *lgN = NULL;
+    SRP_user_pwd *user_pwd = NULL;
+    int ret = 0;
+
+    lgN = SRP_get_default_gN(NULL);
+    if (!TEST_ptr(lgN))
+        goto end;
+
+    if (!TEST_true(SRP_create_verifier_BN(userid, password, &salt, &verifier,
+                                          lgN->N, lgN->g)))
+        goto end;
+
+    user_pwd = OPENSSL_zalloc(sizeof(*user_pwd));
+    if (!TEST_ptr(user_pwd))
+        goto end;
+
+    user_pwd->N = lgN->N;
+    user_pwd->g = lgN->g;
+    user_pwd->id = OPENSSL_strdup(userid);
+    if (!TEST_ptr(user_pwd->id))
+        goto end;
+
+    user_pwd->v = verifier;
+    user_pwd->s = salt;
+    verifier = salt = NULL;
+
+    if (sk_SRP_user_pwd_insert(vbase->users_pwd, user_pwd, 0) == 0)
+        goto end;
+    user_pwd = NULL;
+
+    ret = 1;
+end:
+    SRP_user_pwd_free(user_pwd);
+    BN_free(salt);
+    BN_free(verifier);
+
+    return ret;
+}
+
+/*
+ * SRP tests
+ *
+ * Test 0: Simple successful SRP connection, new vbase
+ * Test 1: Connection failure due to bad password, new vbase
+ * Test 2: Simple successful SRP connection, vbase loaded from existing file
+ * Test 3: Connection failure due to bad password, vbase loaded from existing
+ *         file
+ * Test 4: Simple successful SRP connection, vbase loaded from new file
+ * Test 5: Connection failure due to bad password, vbase loaded from new file
+ */
+static int test_srp(int tst)
+{
+    char *userid = "test", *password = "password", *tstsrpfile;
+    SSL_CTX *cctx = NULL, *sctx = NULL;
+    SSL *clientssl = NULL, *serverssl = NULL;
+    int ret, testresult = 0;
+
+    vbase = SRP_VBASE_new(NULL);
+    if (!TEST_ptr(vbase))
+        goto end;
+
+    if (tst == 0 || tst == 1) {
+        if (!TEST_true(create_new_vbase(userid, password)))
+            goto end;
+    } else {
+        if (tst == 4 || tst == 5) {
+            if (!TEST_true(create_new_vfile(userid, password, tmpfilename)))
+                goto end;
+            tstsrpfile = tmpfilename;
+        } else {
+            tstsrpfile = srpvfile;
+        }
+        if (!TEST_int_eq(SRP_VBASE_init(vbase, tstsrpfile), SRP_NO_ERROR))
+            goto end;
+    }
+
+    if (!TEST_true(create_ssl_ctx_pair(TLS_server_method(), TLS_client_method(),
+                                       TLS1_VERSION, TLS_MAX_VERSION,
+                                       &sctx, &cctx, cert, privkey)))
+        goto end;
+
+    if (!TEST_int_gt(SSL_CTX_set_srp_username_callback(sctx, ssl_srp_cb), 0)
+            || !TEST_true(SSL_CTX_set_cipher_list(cctx, "SRP-AES-128-CBC-SHA"))
+            || !TEST_true(SSL_CTX_set_max_proto_version(sctx, TLS1_2_VERSION))
+            || !TEST_true(SSL_CTX_set_max_proto_version(cctx, TLS1_2_VERSION))
+            || !TEST_int_gt(SSL_CTX_set_srp_username(cctx, userid), 0))
+        goto end;
+
+    if (tst % 2 == 1) {
+        if (!TEST_int_gt(SSL_CTX_set_srp_password(cctx, "badpass"), 0))
+            goto end;
+    } else {
+        if (!TEST_int_gt(SSL_CTX_set_srp_password(cctx, password), 0))
+            goto end;
+    }
+
+    if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl,
+                                      NULL, NULL)))
+        goto end;
+
+    ret = create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE);
+    if (ret) {
+        if (!TEST_true(tst % 2 == 0))
+            goto end;
+    } else {
+        if (!TEST_true(tst % 2 == 1))
+            goto end;
+    }
+
+    testresult = 1;
+
+ end:
+    SRP_VBASE_free(vbase);
+    vbase = NULL;
+    SSL_free(serverssl);
+    SSL_free(clientssl);
+    SSL_CTX_free(sctx);
+    SSL_CTX_free(cctx);
+
+    return testresult;
+}
+#endif
+
 int setup_tests(void)
 {
     if (!TEST_ptr(cert = test_get_argument(0))
-            || !TEST_ptr(privkey = test_get_argument(1)))
+            || !TEST_ptr(privkey = test_get_argument(1))
+            || !TEST_ptr(srpvfile = test_get_argument(2))
+            || !TEST_ptr(tmpfilename = test_get_argument(3)))
         return 0;
 
     if (getenv("OPENSSL_TEST_GETCOUNTS") != NULL) {
@@ -3871,6 +4096,9 @@ int setup_tests(void)
 #endif
     ADD_ALL_TESTS(test_ssl_clear, 2);
     ADD_ALL_TESTS(test_max_fragment_len_ext, OSSL_NELEM(max_fragment_len_test));
+#if !defined(OPENSSL_NO_SRP) && !defined(OPENSSL_NO_TLS1_2)
+    ADD_ALL_TESTS(test_srp, 6);
+#endif
     return 1;
 }
 


More information about the openssl-commits mailing list