1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2014 Jason King.
  14  */
  15 #include <sys/debug.h>
  16 #include <security/cryptoki.h>
  17 #include <umem.h>
  18 #include <limits.h>
  19 #include <string.h>
  20 #include "defs.h"
  21 #include "ikev2.h"
  22 #include "prf.h"
  23 #include "pkcs11.h"
  24 
  25 typedef struct prf_alg_s {
  26         int                     i2alg;
  27         CK_MECHANISM_TYPE       hash;
  28         CK_MECHANISM_TYPE       hmac;
  29         size_t                  inlen;  /* in bytes */
  30         size_t                  outlen; /* in bytes */
  31         boolean_t               pad;
  32 } prf_alg_t;
  33 
  34 #define MECHPAD(_i2, _p11, _in, _out) {         \
  35         .i2alg = IKEV2_PRF_HMAC_ ## _i2,        \
  36         .hash = CKM_ ## _p11,                   \
  37         .hmac = CKM_ ## _p11 ## _HMAC,          \
  38         .inlen = _in,                           \
  39         .outlen = _out,                         \
  40         .pad = B_TRUE                           \
  41 }
  42 
  43 static const prf_alg_t prf_tbl[] = {
  44         MECHPAD(MD5, MD5, 64, 16),
  45         MECHPAD(SHA1, SHA_1, 64, 16),
  46         MECHPAD(SHA2_256, SHA256, 64, 32),
  47         MECHPAD(SHA2_384, SHA384, 128, 48),
  48         MECHPAD(SHA2_512, SHA512, 128, 64)
  49 };
  50 #define N_PRF (sizeof (prf_tbl) / sizeof (prf_alg_t))
  51 
  52 static const prf_alg_t  *get_alg(int);
  53 static CK_RV            prfplus_update(prfp_t *);
  54 
  55 /*
  56  * Create a PKCS11 key object that can be used in C_Sign* operations with
  57  * a scatter/gather-like listing of source input.
  58  *
  59  * Args:
  60  *      alg     The PRF algorithm this will be used with
  61  *      src     An array of buf_t's pointing to the source key
  62  *      n       The number of buf_t's we have
  63  *      kp      Pointer to CK_OBJECT_HANDLE to write the resulting key
  64  *              object.
  65  * Returns:
  66  *      CKR_OK  Success
  67  */
  68 CK_RV
  69 prf_genkey(int alg, buf_t *restrict src, size_t n,
  70     CK_OBJECT_HANDLE_PTR restrict kp)
  71 {
  72         const prf_alg_t *algp;
  73 
  74         CK_RV                   rc;
  75         CK_OBJECT_CLASS         cls = CKO_SECRET_KEY;
  76         CK_KEY_TYPE             kt = CKK_GENERIC_SECRET;
  77         CK_BBOOL                false_v = CK_FALSE;
  78         CK_MECHANISM_TYPE       hmac;
  79         CK_ATTRIBUTE            template[] = {
  80                 { CKA_VALUE, NULL_PTR, 0 },     /* filled in later */
  81                 { CKA_CLASS, &cls, sizeof (cls) },
  82                 { CKA_KEY_TYPE, &kt, sizeof (kt) },
  83                 /* XXX: is this actually needed? */
  84                 { CKA_ALLOWED_MECHANISMS, &hmac, sizeof (hmac) },
  85                 { CKA_MODIFIABLE, &false_v, sizeof (false_v) }
  86         };
  87         buf_t                   key = { 0 };
  88         size_t                  srclen;
  89         ulong_t                 keylen = 0; /* actual length */
  90 
  91         rc = CKR_OK;
  92 
  93         VERIFY((algp = get_alg(alg)) != NULL);
  94         hmac = algp->hmac;
  95 
  96         srclen = 0;
  97         for (size_t i = 0; i < n; i++)
  98                 srclen += buf_left(&src[i]);
  99 
 100         if (srclen < algp->inlen && algp->pad)
 101                 keylen = algp->inlen;
 102         else
 103                 keylen = srclen;
 104 
 105         if (!buf_alloc(&key, keylen)) {
 106                 rc = CKR_HOST_MEMORY;
 107                 goto done;
 108         }
 109         buf_set_write(&key);
 110 
 111         /*
 112          * The HMAC standards specify what an implementation should do when
 113          * the given key length doesn't match the preferred key length (either
 114          * pad or run the digest alg on the key to yield a value of the desired
 115          * length), so we should not need to worry about this.
 116          */
 117         VERIFY3U(buf_copy(&key, src, n), <=, keylen);
 118 
 119         template[0].pValue = key.b_buf;
 120         template[0].ulValueLen = keylen;
 121 
 122         rc = C_CreateObject(p11h, template,
 123             sizeof (template) / sizeof (CK_ATTRIBUTE), kp);
 124 
 125 done:
 126         buf_free(&key);
 127         return (rc);
 128 }
 129 
 130 /*
 131  * Run the given PRF algorithm for the given key and seed and place
 132  * result into out.
 133  */
 134 CK_RV
 135 prf(int alg, CK_OBJECT_HANDLE key, buf_t *restrict seed, size_t nseed,
 136     buf_t *restrict out)
 137 {
 138         const prf_alg_t         *algp;
 139         CK_MECHANISM            mech;
 140         CK_RV                   rc = CKR_OK;
 141         CK_ULONG                len;
 142 
 143         VERIFY3P((algp = get_alg(alg)), !=, NULL);
 144         VERIFY3U(out->b_len, >=, algp->outlen);
 145 
 146         mech.mechanism = algp->hmac;
 147         mech.pParameter = NULL;
 148         mech.ulParameterLen = 0;
 149 
 150         if ((rc = C_SignInit(p11h, &mech, key)) != CKR_OK)
 151                 return (rc);
 152 
 153         for (size_t i = 0; i < nseed; i++, seed) {
 154                 BUF_IS_READ(seed);
 155                 rc = C_SignUpdate(p11h, seed->b_ptr, buf_left(seed));
 156                 /* XXX: should we still call C_SignFinal? */
 157                 if (rc != CKR_OK)
 158                         return (rc);
 159         }
 160 
 161         BUF_IS_WRITE(out);
 162 
 163         len = buf_left(out);
 164         rc = C_SignFinal(p11h, out->b_ptr, &len);
 165         if (rc == CKR_OK)
 166                 VERIFY3U(len, ==, buf_left(out));
 167 
 168         buf_skip(out, len);
 169         return (rc);
 170 }
 171 
 172 /*
 173  * Inititalize a prf+ instance for the given algorithm, key, and seed.
 174  */
 175 CK_RV
 176 prfplus_init(prfp_t *restrict prfp, int alg, CK_OBJECT_HANDLE key,
 177     const buf_t *restrict seed)
 178 {
 179         const prf_alg_t *algp;
 180         CK_RV           rc = CKR_OK;
 181 
 182         (void) memset(prfp, 0, sizeof (*prfp));
 183 
 184         VERIFY((algp = get_alg(alg)) != NULL);
 185 
 186         if (!buf_alloc(&prfp->tbuf[0], algp->outlen) ||
 187             !buf_alloc(&prfp->tbuf[1], algp->outlen) ||
 188             !buf_alloc(&prfp->seed, buf_left(seed))) {
 189                 rc = CKR_HOST_MEMORY;
 190                 goto error;
 191         }
 192 
 193         /* stash our own copy of the seed */
 194         (void) buf_copy(&prfp->seed, seed, 1);
 195         VERIFY(!buf_eof(&prfp->seed));
 196 
 197         /*
 198          * Per RFC5996 2.13, prf+(K, S) = T1 | T2 | T3 | T4 | ...
 199          *
 200          * where:
 201          *      T1 = prf (K, S | 0x01)
 202          *      T2 = prf (K, T1 | S | 0x02)
 203          *      T3 = prf (K, T2 | S | 0x03)
 204          *      T4 = prf (K, T3 | S | 0x04)
 205          *
 206          * As such, we keep a list of buf_t's for each of the three components
 207          * Since the last two never change location, they are set now, while
 208          * the first points to either prfp->tbuf[0] or prfp->tbuf[1], based
 209          * on the value of prfp->n
 210          */
 211         prfp->prf_arg[1] = prfp->seed;
 212         prfp->prf_arg[2].b_ptr = prfp->prf_arg[2].b_buf = &prfp->n;
 213         prfp->prf_arg[2].b_len = sizeof (prfp->n);
 214         prfp->n = 1;
 215 
 216         buf_set_read(&prfp->prf_arg[1]);
 217         buf_set_read(&prfp->prf_arg[2]);
 218 
 219         /*
 220          * Fill prfp->tbuf[1] with T1. T1 is defined as:
 221          *      T1 = prf (K, S | 0x01)
 222          * Note that this is different from subsequent iterations, hence
 223          * starting at prfp->prf_arg[1], not prfp->arg[0]
 224          */
 225         rc = prf(alg, prfp->key, &prfp->prf_arg[1], 2, &prfp->tbuf[1]);
 226         return (rc);
 227 
 228 error:
 229         prfplus_fini(prfp);
 230         return (rc);
 231 }
 232 
 233 /*
 234  * Fill out with the result of the prf+ function.
 235  */
 236 CK_RV
 237 prfplus(prfp_t *restrict prfp, buf_t *restrict out)
 238 {
 239         const prf_alg_t *algp;
 240         buf_t           t;
 241         buf_t           outcopy;
 242         CK_RV           rc = CKR_OK;
 243 
 244         algp = get_alg(prfp->i2alg);
 245 
 246         /* generate a local cache of out so we can manipulate the ptr and len */
 247         outcopy = *out;
 248         buf_set_write(&outcopy);
 249 
 250         while (buf_left(&outcopy) > 0) {
 251                 size_t chunk;
 252 
 253                 chunk = buf_left(&outcopy);
 254 
 255                 if (prfp->n & 0x01)
 256                         t = prfp->tbuf[1];
 257                 else
 258                         t = prfp->tbuf[0];
 259 
 260                 t.b_ptr += prfp->pos;
 261                 t.b_len -= prfp->pos;
 262 
 263                 if (t.b_len == 0) {
 264                         if ((rc = prfplus_update(prfp)) != CKR_OK)
 265                                 goto done;
 266                         continue;
 267                 }
 268 
 269                 if (chunk > t.b_len)
 270                         chunk = t.b_len;
 271 
 272                 VERIFY(buf_copy(&outcopy, &t, chunk) == chunk);
 273                 buf_skip(&outcopy, chunk);
 274                 prfp->pos += chunk;
 275         }
 276 
 277 done:
 278         return (rc);
 279 }
 280 
 281 /*
 282  * Perform a prf+ iteration
 283  */
 284 static CK_RV
 285 prfplus_update(prfp_t *prfp)
 286 {
 287         buf_t   *dest;
 288         CK_RV   rc = CKR_OK;
 289 
 290         ASSERT(prfp->n >= 1);
 291 
 292         if (prfp->n == 0xff) {
 293                 /* XXX: log error */
 294                 return (CKR_GENERAL_ERROR);
 295         }
 296 
 297         if (++prfp->n & 0x01) {
 298                 prfp->prf_arg[1] = prfp->tbuf[1];
 299                 dest = &prfp->tbuf[0];
 300         } else {
 301                 prfp->prf_arg[0] = prfp->tbuf[0];
 302                 dest = &prfp->tbuf[1];
 303         }
 304 
 305         rc = prf(prfp->i2alg, prfp->key, prfp->prf_arg, 3, dest);
 306         prfp->pos = 0;
 307         return (rc);
 308 }
 309 
 310 void
 311 prfplus_fini(prfp_t *prfp)
 312 {
 313         if (prfp == NULL)
 314                 return;
 315 
 316         buf_free(&prfp->tbuf[0]);
 317         buf_free(&prfp->tbuf[1]);
 318         buf_free(&prfp->seed);
 319         (void) memset(prfp, 0, sizeof (*prfp));
 320 }
 321 
 322 CK_MECHANISM_TYPE
 323 ikev2_prf_to_p11(int prf)
 324 {
 325         switch (prf) {
 326         case IKEV2_PRF_HMAC_MD5:
 327                 return (CKM_MD5_HMAC);
 328         case IKEV2_PRF_HMAC_SHA1:
 329                 return (CKM_SHA_1_HMAC);
 330         case IKEV2_PRF_HMAC_SHA2_256:
 331                 return (CKM_SHA256_HMAC);
 332         case IKEV2_PRF_HMAC_SHA2_384:
 333                 return (CKM_SHA384_HMAC);
 334         case IKEV2_PRF_HMAC_SHA2_512:
 335                 return (CKM_SHA512_HMAC);
 336         }
 337 
 338         INVALID("invalid hmac value");
 339 
 340         /*NOTREACHED*/
 341         return (0);
 342 }
 343 
 344 size_t
 345 ikev2_prf_keylen(int prf)
 346 {
 347         return (get_alg(prf)->inlen);
 348 }
 349 
 350 /*
 351  * Get the information for a given algorithm, or NULL of not found
 352  */
 353 static const prf_alg_t *
 354 get_alg(int i2alg)
 355 {
 356         for (int i = 0; i < N_PRF; i++) {
 357                 if (prf_tbl[i].i2alg == i2alg)
 358                         return (&prf_tbl[i]);
 359         }
 360         return (NULL);
 361 }