crypto/rand: restore the generic DRBG implementation
authorDr. Matthias St. Pierre <Matthias.St.Pierre@ncp-e.com>
Thu, 28 Dec 2017 20:42:14 +0000 (21:42 +0100)
committerPauli <paul.dale@oracle.com>
Thu, 4 Jan 2018 01:47:31 +0000 (11:47 +1000)
The DRGB concept described in NIST SP 800-90A provides for having different
algorithms to generate random output. In fact, the FIPS object module used to
implement three of them, CTR DRBG, HASH DRBG and HMAC DRBG.

When the FIPS code was ported to master in #4019, two of the three algorithms
were dropped, and together with those the entire code that made RAND_DRBG
generic was removed, since only one concrete implementation was left.

This commit restores the original generic implementation of the DRBG, making it
possible again to add additional implementations using different algorithms
(like RAND_DRBG_CHACHA20) in the future.

Reviewed-by: Paul Dale <paul.dale@oracle.com>
Reviewed-by: Tim Hudson <tjh@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/4998)

crypto/err/openssl.txt
crypto/rand/drbg_ctr.c
crypto/rand/drbg_lib.c
crypto/rand/rand_err.c
crypto/rand/rand_lcl.h
include/openssl/randerr.h
test/drbgtest.c

index 4357cfb..9ec0009 100644 (file)
@@ -893,6 +893,7 @@ RAND_F_RAND_DRBG_NEW:109:RAND_DRBG_new
 RAND_F_RAND_DRBG_RESEED:110:RAND_DRBG_reseed
 RAND_F_RAND_DRBG_RESTART:102:rand_drbg_restart
 RAND_F_RAND_DRBG_SET:104:RAND_DRBG_set
+RAND_F_RAND_DRBG_UNINSTANTIATE:118:RAND_DRBG_uninstantiate
 RAND_F_RAND_LOAD_FILE:111:RAND_load_file
 RAND_F_RAND_POOL_ADD:103:RAND_POOL_add
 RAND_F_RAND_POOL_ADD_BEGIN:113:RAND_POOL_add_begin
@@ -2258,6 +2259,7 @@ RAND_R_INTERNAL_ERROR:113:internal error
 RAND_R_IN_ERROR_STATE:114:in error state
 RAND_R_NOT_A_REGULAR_FILE:122:Not a regular file
 RAND_R_NOT_INSTANTIATED:115:not instantiated
+RAND_R_NO_DRBG_IMPLEMENTATION_SELECTED:128:no drbg implementation selected
 RAND_R_PERSONALISATION_STRING_TOO_LONG:116:personalisation string too long
 RAND_R_PRNG_NOT_SEEDED:100:PRNG not seeded
 RAND_R_RANDOM_POOL_OVERFLOW:125:random pool overflow
index bb3acc8..3130e49 100644 (file)
@@ -201,7 +201,7 @@ static void ctr_update(RAND_DRBG *drbg,
                        const unsigned char *in2, size_t in2len,
                        const unsigned char *nonce, size_t noncelen)
 {
-    RAND_DRBG_CTR *ctr = &drbg->ctr;
+    RAND_DRBG_CTR *ctr = &drbg->data.ctr;
 
     /* ks is already setup for correct key */
     inc_128(ctr);
@@ -236,12 +236,12 @@ static void ctr_update(RAND_DRBG *drbg,
     AES_set_encrypt_key(ctr->K, drbg->strength, &ctr->ks);
 }
 
-int ctr_instantiate(RAND_DRBG *drbg,
-                    const unsigned char *entropy, size_t entropylen,
-                    const unsigned char *nonce, size_t noncelen,
-                    const unsigned char *pers, size_t perslen)
+static int drbg_ctr_instantiate(RAND_DRBG *drbg,
+                                const unsigned char *entropy, size_t entropylen,
+                                const unsigned char *nonce, size_t noncelen,
+                                const unsigned char *pers, size_t perslen)
 {
-    RAND_DRBG_CTR *ctr = &drbg->ctr;
+    RAND_DRBG_CTR *ctr = &drbg->data.ctr;
 
     if (entropy == NULL)
         return 0;
@@ -253,9 +253,9 @@ int ctr_instantiate(RAND_DRBG *drbg,
     return 1;
 }
 
-int ctr_reseed(RAND_DRBG *drbg,
-               const unsigned char *entropy, size_t entropylen,
-               const unsigned char *adin, size_t adinlen)
+static int drbg_ctr_reseed(RAND_DRBG *drbg,
+                           const unsigned char *entropy, size_t entropylen,
+                           const unsigned char *adin, size_t adinlen)
 {
     if (entropy == NULL)
         return 0;
@@ -263,11 +263,11 @@ int ctr_reseed(RAND_DRBG *drbg,
     return 1;
 }
 
-int ctr_generate(RAND_DRBG *drbg,
-                 unsigned char *out, size_t outlen,
-                 const unsigned char *adin, size_t adinlen)
+static int drbg_ctr_generate(RAND_DRBG *drbg,
+                             unsigned char *out, size_t outlen,
+                             const unsigned char *adin, size_t adinlen)
 {
-    RAND_DRBG_CTR *ctr = &drbg->ctr;
+    RAND_DRBG_CTR *ctr = &drbg->data.ctr;
 
     if (adin != NULL && adinlen != 0) {
         ctr_update(drbg, adin, adinlen, NULL, 0, NULL, 0);
@@ -299,15 +299,22 @@ int ctr_generate(RAND_DRBG *drbg,
     return 1;
 }
 
-int ctr_uninstantiate(RAND_DRBG *drbg)
+static int drbg_ctr_uninstantiate(RAND_DRBG *drbg)
 {
-    OPENSSL_cleanse(&drbg->ctr, sizeof(drbg->ctr));
+    OPENSSL_cleanse(&drbg->data.ctr, sizeof(drbg->data.ctr));
     return 1;
 }
 
-int ctr_init(RAND_DRBG *drbg)
+static RAND_DRBG_METHOD drbg_ctr_meth = {
+    drbg_ctr_instantiate,
+    drbg_ctr_reseed,
+    drbg_ctr_generate,
+    drbg_ctr_uninstantiate
+};
+
+int drbg_ctr_init(RAND_DRBG *drbg)
 {
-    RAND_DRBG_CTR *ctr = &drbg->ctr;
+    RAND_DRBG_CTR *ctr = &drbg->data.ctr;
     size_t keylen;
 
     switch (drbg->nid) {
@@ -325,6 +332,8 @@ int ctr_init(RAND_DRBG *drbg)
         break;
     }
 
+    drbg->meth = &drbg_ctr_meth;
+
     ctr->keylen = keylen;
     drbg->strength = keylen * 8;
     drbg->seedlen = keylen + 16;
index e33877e..795b098 100644 (file)
@@ -124,7 +124,7 @@ int RAND_DRBG_set(RAND_DRBG *drbg, int nid, unsigned int flags)
     case NID_aes_128_ctr:
     case NID_aes_192_ctr:
     case NID_aes_256_ctr:
-        ret = ctr_init(drbg);
+        ret = drbg_ctr_init(drbg);
         break;
     }
 
@@ -172,7 +172,8 @@ void RAND_DRBG_free(RAND_DRBG *drbg)
     if (drbg == NULL)
         return;
 
-    ctr_uninstantiate(drbg);
+    if (drbg->meth != NULL)
+        drbg->meth->uninstantiate(drbg);
     CRYPTO_free_ex_data(CRYPTO_EX_INDEX_DRBG, drbg, &drbg->ex_data);
     OPENSSL_clear_free(drbg, sizeof(*drbg));
 }
@@ -196,6 +197,14 @@ int RAND_DRBG_instantiate(RAND_DRBG *drbg,
                 RAND_R_PERSONALISATION_STRING_TOO_LONG);
         goto end;
     }
+
+    if (drbg->meth == NULL)
+    {
+        RANDerr(RAND_F_RAND_DRBG_INSTANTIATE,
+                RAND_R_NO_DRBG_IMPLEMENTATION_SELECTED);
+        goto end;
+    }
+
     if (drbg->state != DRBG_UNINITIALISED) {
         RANDerr(RAND_F_RAND_DRBG_INSTANTIATE,
                 drbg->state == DRBG_ERROR ? RAND_R_IN_ERROR_STATE
@@ -223,7 +232,7 @@ int RAND_DRBG_instantiate(RAND_DRBG *drbg,
         }
     }
 
-    if (!ctr_instantiate(drbg, entropy, entropylen,
+    if (!drbg->meth->instantiate(drbg, entropy, entropylen,
                          nonce, noncelen, pers, perslen)) {
         RANDerr(RAND_F_RAND_DRBG_INSTANTIATE, RAND_R_ERROR_INSTANTIATING_DRBG);
         goto end;
@@ -267,11 +276,18 @@ end:
  */
 int RAND_DRBG_uninstantiate(RAND_DRBG *drbg)
 {
+    if (drbg->meth == NULL)
+    {
+        RANDerr(RAND_F_RAND_DRBG_UNINSTANTIATE,
+                RAND_R_NO_DRBG_IMPLEMENTATION_SELECTED);
+        return 0;
+    }
+
     /* Clear the entire drbg->ctr struct, then reset some important
      * members of the drbg->ctr struct (e.g. keysize, df_ks) to their
      * initial values.
      */
-    ctr_uninstantiate(drbg);
+    drbg->meth->uninstantiate(drbg);
     return RAND_DRBG_set(drbg, drbg->nid, drbg->flags);
 }
 
@@ -314,7 +330,7 @@ int RAND_DRBG_reseed(RAND_DRBG *drbg,
         goto end;
     }
 
-    if (!ctr_reseed(drbg, entropy, entropylen, adin, adinlen))
+    if (!drbg->meth->reseed(drbg, entropy, entropylen, adin, adinlen))
         goto end;
 
     drbg->state = DRBG_READY;
@@ -420,7 +436,7 @@ int rand_drbg_restart(RAND_DRBG *drbg,
              * entropy from the trusted entropy source using get_entropy().
              * This is not a reseeding in the strict sense of NIST SP 800-90A.
              */
-            ctr_reseed(drbg, adin, adinlen, NULL, 0);
+            drbg->meth->reseed(drbg, adin, adinlen, NULL, 0);
         } else if (reseeded == 0) {
             /* do a full reseeding if it has not been done yet above */
             RAND_DRBG_reseed(drbg, NULL, 0);
@@ -507,7 +523,7 @@ int RAND_DRBG_generate(RAND_DRBG *drbg, unsigned char *out, size_t outlen,
         adinlen = 0;
     }
 
-    if (!ctr_generate(drbg, out, outlen, adin, adinlen)) {
+    if (!drbg->meth->generate(drbg, out, outlen, adin, adinlen)) {
         drbg->state = DRBG_ERROR;
         RANDerr(RAND_F_RAND_DRBG_GENERATE, RAND_R_GENERATE_ERROR);
         return 0;
index dc6140c..ac70b4a 100644 (file)
@@ -27,6 +27,8 @@ static const ERR_STRING_DATA RAND_str_functs[] = {
     {ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_DRBG_RESEED, 0), "RAND_DRBG_reseed"},
     {ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_DRBG_RESTART, 0), "rand_drbg_restart"},
     {ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_DRBG_SET, 0), "RAND_DRBG_set"},
+    {ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_DRBG_UNINSTANTIATE, 0),
+     "RAND_DRBG_uninstantiate"},
     {ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_LOAD_FILE, 0), "RAND_load_file"},
     {ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_POOL_ADD, 0), "RAND_POOL_add"},
     {ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_POOL_ADD_BEGIN, 0),
@@ -76,6 +78,8 @@ static const ERR_STRING_DATA RAND_str_reasons[] = {
     {ERR_PACK(ERR_LIB_RAND, 0, RAND_R_NOT_A_REGULAR_FILE),
     "Not a regular file"},
     {ERR_PACK(ERR_LIB_RAND, 0, RAND_R_NOT_INSTANTIATED), "not instantiated"},
+    {ERR_PACK(ERR_LIB_RAND, 0, RAND_R_NO_DRBG_IMPLEMENTATION_SELECTED),
+    "no drbg implementation selected"},
     {ERR_PACK(ERR_LIB_RAND, 0, RAND_R_PERSONALISATION_STRING_TOO_LONG),
     "personalisation string too long"},
     {ERR_PACK(ERR_LIB_RAND, 0, RAND_R_PRNG_NOT_SEEDED), "PRNG not seeded"},
index 9044981..1a09118 100644 (file)
@@ -54,6 +54,42 @@ typedef enum drbg_status_e {
 } DRBG_STATUS;
 
 
+/* intantiate */
+typedef int (*RAND_DRBG_instantiate_fn)(RAND_DRBG *ctx,
+                                        const unsigned char *ent,
+                                        size_t entlen,
+                                        const unsigned char *nonce,
+                                        size_t noncelen,
+                                        const unsigned char *pers,
+                                        size_t perslen);
+/* reseed */
+typedef int (*RAND_DRBG_reseed_fn)(RAND_DRBG *ctx,
+                                   const unsigned char *ent,
+                                   size_t entlen,
+                                   const unsigned char *adin,
+                                   size_t adinlen);
+/* generat output */
+typedef int (*RAND_DRBG_generate_fn)(RAND_DRBG *ctx,
+                                     unsigned char *out,
+                                     size_t outlen,
+                                     const unsigned char *adin,
+                                     size_t adinlen);
+/* uninstantiate */
+typedef int (*RAND_DRBG_uninstantiate_fn)(RAND_DRBG *ctx);
+
+
+/*
+ * The DRBG methods
+ */
+
+typedef struct rand_drbg_method_st {
+    RAND_DRBG_instantiate_fn instantiate;
+    RAND_DRBG_reseed_fn reseed;
+    RAND_DRBG_generate_fn generate;
+    RAND_DRBG_uninstantiate_fn uninstantiate;
+} RAND_DRBG_METHOD;
+
+
 /*
  * The state of a DRBG AES-CTR.
  */
@@ -96,7 +132,7 @@ struct rand_drbg_st {
     /*
      * The following parameters are setup by the per-type "init" function.
      *
-     * Currently the only type is CTR_DRBG, its init function is ctr_init().
+     * Currently the only type is CTR_DRBG, its init function is drbg_ctr_init().
      *
      * The parameters are closely related to the ones described in
      * section '10.2.1 CTR_DRBG' of [NIST SP 800-90Ar1], with one
@@ -148,8 +184,13 @@ struct rand_drbg_st {
     /* Application data, mainly used in the KATs. */
     CRYPTO_EX_DATA ex_data;
 
-    /* Implementation specific structures; was a union, but inline for now */
-    RAND_DRBG_CTR ctr;
+    /* Implementation specific data (currently only one implementation) */
+    union {
+        RAND_DRBG_CTR ctr;
+    } data;
+
+    /* Implementation specific methods */
+    RAND_DRBG_METHOD *meth;
 
     /* Callback functions.  See comments in rand_lib.c */
     RAND_DRBG_get_entropy_fn get_entropy;
@@ -179,18 +220,7 @@ void rand_drbg_cleanup_entropy(RAND_DRBG *drbg,
 int rand_drbg_restart(RAND_DRBG *drbg,
                       const unsigned char *buffer, size_t len, size_t entropy);
 
-/* DRBG functions implementing AES-CTR */
-int ctr_init(RAND_DRBG *drbg);
-int ctr_uninstantiate(RAND_DRBG *drbg);
-int ctr_instantiate(RAND_DRBG *drbg,
-                    const unsigned char *entropy, size_t entropylen,
-                    const unsigned char *nonce, size_t noncelen,
-                    const unsigned char *pers, size_t perslen);
-int ctr_reseed(RAND_DRBG *drbg,
-               const unsigned char *entropy, size_t entropylen,
-               const unsigned char *adin, size_t adinlen);
-int ctr_generate(RAND_DRBG *drbg,
-                 unsigned char *out, size_t outlen,
-                 const unsigned char *adin, size_t adinlen);
+/* initializes the AES-CTR DRBG implementation */
+int drbg_ctr_init(RAND_DRBG *drbg);
 
 #endif
index b07ea23..b136ce8 100644 (file)
@@ -33,6 +33,7 @@ int ERR_load_RAND_strings(void);
 # define RAND_F_RAND_DRBG_RESEED                          110
 # define RAND_F_RAND_DRBG_RESTART                         102
 # define RAND_F_RAND_DRBG_SET                             104
+# define RAND_F_RAND_DRBG_UNINSTANTIATE                   118
 # define RAND_F_RAND_LOAD_FILE                            111
 # define RAND_F_RAND_POOL_ADD                             103
 # define RAND_F_RAND_POOL_ADD_BEGIN                       113
@@ -65,6 +66,7 @@ int ERR_load_RAND_strings(void);
 # define RAND_R_IN_ERROR_STATE                            114
 # define RAND_R_NOT_A_REGULAR_FILE                        122
 # define RAND_R_NOT_INSTANTIATED                          115
+# define RAND_R_NO_DRBG_IMPLEMENTATION_SELECTED           128
 # define RAND_R_PERSONALISATION_STRING_TOO_LONG           116
 # define RAND_R_PRNG_NOT_SEEDED                           100
 # define RAND_R_RANDOM_POOL_OVERFLOW                      125
index 68c1697..fcb4b28 100644 (file)
@@ -438,7 +438,7 @@ static int error_check(DRBG_SELFTEST_DATA *td)
         goto err;
 
     /* Standard says we have to check uninstantiate really zeroes */
-    if (!TEST_mem_eq(zero, sizeof(drbg->ctr), &drbg->ctr, sizeof(drbg->ctr)))
+    if (!TEST_mem_eq(zero, sizeof(drbg->data), &drbg->data, sizeof(drbg->data)))
         goto err;
 
     ret = 1;