LCOV - code coverage report
Current view: top level - src/eap_common - eap_pwd_common.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1393793999 Lines: 130 174 74.7 %
Date: 2014-03-02 Functions: 6 6 100.0 %
Branches: 43 72 59.7 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * EAP server/peer: EAP-pwd shared routines
       3                 :            :  * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
       4                 :            :  *
       5                 :            :  * This software may be distributed under the terms of the BSD license.
       6                 :            :  * See README for more details.
       7                 :            :  */
       8                 :            : 
       9                 :            : #include "includes.h"
      10                 :            : #include "common.h"
      11                 :            : #include "crypto/sha256.h"
      12                 :            : #include "crypto/crypto.h"
      13                 :            : #include "eap_defs.h"
      14                 :            : #include "eap_pwd_common.h"
      15                 :            : 
      16                 :            : /* The random function H(x) = HMAC-SHA256(0^32, x) */
      17                 :        108 : struct crypto_hash * eap_pwd_h_init(void)
      18                 :            : {
      19                 :            :         u8 allzero[SHA256_MAC_LEN];
      20                 :        108 :         os_memset(allzero, 0, SHA256_MAC_LEN);
      21                 :        108 :         return crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256, allzero,
      22                 :            :                                 SHA256_MAC_LEN);
      23                 :            : }
      24                 :            : 
      25                 :            : 
      26                 :        578 : void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len)
      27                 :            : {
      28                 :        578 :         crypto_hash_update(hash, data, len);
      29                 :        578 : }
      30                 :            : 
      31                 :            : 
      32                 :        108 : void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest)
      33                 :            : {
      34                 :        108 :         size_t len = SHA256_MAC_LEN;
      35                 :        108 :         crypto_hash_finish(hash, digest, &len);
      36                 :        108 : }
      37                 :            : 
      38                 :            : 
      39                 :            : /* a counter-based KDF based on NIST SP800-108 */
      40                 :         58 : static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label,
      41                 :            :                        size_t labellen, u8 *result, size_t resultbitlen)
      42                 :            : {
      43                 :            :         struct crypto_hash *hash;
      44                 :            :         u8 digest[SHA256_MAC_LEN];
      45                 :            :         u16 i, ctr, L;
      46                 :         58 :         size_t resultbytelen, len = 0, mdlen;
      47                 :            : 
      48                 :         58 :         resultbytelen = (resultbitlen + 7) / 8;
      49                 :         58 :         ctr = 0;
      50                 :         58 :         L = htons(resultbitlen);
      51         [ +  + ]:        188 :         while (len < resultbytelen) {
      52                 :        130 :                 ctr++;
      53                 :        130 :                 i = htons(ctr);
      54                 :        130 :                 hash = crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256,
      55                 :            :                                         key, keylen);
      56         [ -  + ]:        130 :                 if (hash == NULL)
      57                 :          0 :                         return -1;
      58         [ +  + ]:        130 :                 if (ctr > 1)
      59                 :         72 :                         crypto_hash_update(hash, digest, SHA256_MAC_LEN);
      60                 :        130 :                 crypto_hash_update(hash, (u8 *) &i, sizeof(u16));
      61                 :        130 :                 crypto_hash_update(hash, label, labellen);
      62                 :        130 :                 crypto_hash_update(hash, (u8 *) &L, sizeof(u16));
      63                 :        130 :                 mdlen = SHA256_MAC_LEN;
      64         [ -  + ]:        130 :                 if (crypto_hash_finish(hash, digest, &mdlen) < 0)
      65                 :          0 :                         return -1;
      66         [ +  + ]:        130 :                 if ((len + mdlen) > resultbytelen)
      67                 :         24 :                         os_memcpy(result + len, digest, resultbytelen - len);
      68                 :            :                 else
      69                 :        106 :                         os_memcpy(result + len, digest, mdlen);
      70                 :        130 :                 len += mdlen;
      71                 :            :         }
      72                 :            : 
      73                 :            :         /* since we're expanding to a bit length, mask off the excess */
      74         [ +  + ]:         58 :         if (resultbitlen % 8) {
      75                 :         10 :                 u8 mask = 0xff;
      76                 :         10 :                 mask <<= (8 - (resultbitlen % 8));
      77                 :         10 :                 result[resultbytelen - 1] &= mask;
      78                 :            :         }
      79                 :            : 
      80                 :         58 :         return 0;
      81                 :            : }
      82                 :            : 
      83                 :            : 
      84                 :            : /*
      85                 :            :  * compute a "random" secret point on an elliptic curve based
      86                 :            :  * on the password and identities.
      87                 :            :  */
      88                 :         18 : int compute_password_element(EAP_PWD_group *grp, u16 num,
      89                 :            :                              u8 *password, int password_len,
      90                 :            :                              u8 *id_server, int id_server_len,
      91                 :            :                              u8 *id_peer, int id_peer_len, u8 *token)
      92                 :            : {
      93                 :         18 :         BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
      94                 :            :         struct crypto_hash *hash;
      95                 :         18 :         unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr;
      96                 :         18 :         int nid, is_odd, ret = 0;
      97                 :            :         size_t primebytelen, primebitlen;
      98                 :            : 
      99   [ +  +  +  +  :         18 :         switch (num) { /* from IANA registry for IKE D-H groups */
                   +  - ]
     100                 :            :         case 19:
     101                 :         10 :                 nid = NID_X9_62_prime256v1;
     102                 :         10 :                 break;
     103                 :            :         case 20:
     104                 :          2 :                 nid = NID_secp384r1;
     105                 :          2 :                 break;
     106                 :            :         case 21:
     107                 :          2 :                 nid = NID_secp521r1;
     108                 :          2 :                 break;
     109                 :            :         case 25:
     110                 :          2 :                 nid = NID_X9_62_prime192v1;
     111                 :          2 :                 break;
     112                 :            :         case 26:
     113                 :          2 :                 nid = NID_secp224r1;
     114                 :          2 :                 break;
     115                 :            :         default:
     116                 :          0 :                 wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num);
     117                 :          0 :                 return -1;
     118                 :            :         }
     119                 :            : 
     120                 :         18 :         grp->pwe = NULL;
     121                 :         18 :         grp->order = NULL;
     122                 :         18 :         grp->prime = NULL;
     123                 :            : 
     124         [ -  + ]:         18 :         if ((grp->group = EC_GROUP_new_by_curve_name(nid)) == NULL) {
     125                 :          0 :                 wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC_GROUP");
     126                 :          0 :                 goto fail;
     127                 :            :         }
     128                 :            : 
     129 [ +  - ][ +  - ]:         18 :         if (((rnd = BN_new()) == NULL) ||
     130         [ +  - ]:         18 :             ((cofactor = BN_new()) == NULL) ||
     131         [ +  - ]:         36 :             ((grp->pwe = EC_POINT_new(grp->group)) == NULL) ||
     132         [ +  - ]:         36 :             ((grp->order = BN_new()) == NULL) ||
     133         [ -  + ]:         36 :             ((grp->prime = BN_new()) == NULL) ||
     134                 :            :             ((x_candidate = BN_new()) == NULL)) {
     135                 :          0 :                 wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums");
     136                 :          0 :                 goto fail;
     137                 :            :         }
     138                 :            : 
     139         [ -  + ]:         18 :         if (!EC_GROUP_get_curve_GFp(grp->group, grp->prime, NULL, NULL, NULL))
     140                 :            :         {
     141                 :          0 :                 wpa_printf(MSG_INFO, "EAP-pwd: unable to get prime for GFp "
     142                 :            :                            "curve");
     143                 :          0 :                 goto fail;
     144                 :            :         }
     145         [ -  + ]:         18 :         if (!EC_GROUP_get_order(grp->group, grp->order, NULL)) {
     146                 :          0 :                 wpa_printf(MSG_INFO, "EAP-pwd: unable to get order for curve");
     147                 :          0 :                 goto fail;
     148                 :            :         }
     149         [ -  + ]:         18 :         if (!EC_GROUP_get_cofactor(grp->group, cofactor, NULL)) {
     150                 :          0 :                 wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for "
     151                 :            :                            "curve");
     152                 :          0 :                 goto fail;
     153                 :            :         }
     154                 :         18 :         primebitlen = BN_num_bits(grp->prime);
     155                 :         18 :         primebytelen = BN_num_bytes(grp->prime);
     156         [ -  + ]:         18 :         if ((prfbuf = os_malloc(primebytelen)) == NULL) {
     157                 :          0 :                 wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf "
     158                 :            :                            "buffer");
     159                 :          0 :                 goto fail;
     160                 :            :         }
     161                 :         18 :         os_memset(prfbuf, 0, primebytelen);
     162                 :         18 :         ctr = 0;
     163                 :            :         while (1) {
     164         [ -  + ]:         42 :                 if (ctr > 30) {
     165                 :          0 :                         wpa_printf(MSG_INFO, "EAP-pwd: unable to find random "
     166                 :            :                                    "point on curve for group %d, something's "
     167                 :            :                                    "fishy", num);
     168                 :          0 :                         goto fail;
     169                 :            :                 }
     170                 :         42 :                 ctr++;
     171                 :            : 
     172                 :            :                 /*
     173                 :            :                  * compute counter-mode password value and stretch to prime
     174                 :            :                  *    pwd-seed = H(token | peer-id | server-id | password |
     175                 :            :                  *                 counter)
     176                 :            :                  */
     177                 :         42 :                 hash = eap_pwd_h_init();
     178         [ -  + ]:         42 :                 if (hash == NULL)
     179                 :          0 :                         goto fail;
     180                 :         42 :                 eap_pwd_h_update(hash, token, sizeof(u32));
     181                 :         42 :                 eap_pwd_h_update(hash, id_peer, id_peer_len);
     182                 :         42 :                 eap_pwd_h_update(hash, id_server, id_server_len);
     183                 :         42 :                 eap_pwd_h_update(hash, password, password_len);
     184                 :         42 :                 eap_pwd_h_update(hash, &ctr, sizeof(ctr));
     185                 :         42 :                 eap_pwd_h_final(hash, pwe_digest);
     186                 :            : 
     187                 :         42 :                 BN_bin2bn(pwe_digest, SHA256_MAC_LEN, rnd);
     188                 :            : 
     189         [ -  + ]:         42 :                 if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN,
     190                 :            :                                 (u8 *) "EAP-pwd Hunting And Pecking",
     191                 :            :                                 os_strlen("EAP-pwd Hunting And Pecking"),
     192                 :            :                                 prfbuf, primebitlen) < 0)
     193                 :          0 :                         goto fail;
     194                 :            : 
     195                 :         42 :                 BN_bin2bn(prfbuf, primebytelen, x_candidate);
     196                 :            : 
     197                 :            :                 /*
     198                 :            :                  * eap_pwd_kdf() returns a string of bits 0..primebitlen but
     199                 :            :                  * BN_bin2bn will treat that string of bits as a big endian
     200                 :            :                  * number. If the primebitlen is not an even multiple of 8
     201                 :            :                  * then excessive bits-- those _after_ primebitlen-- so now
     202                 :            :                  * we have to shift right the amount we masked off.
     203                 :            :                  */
     204         [ +  + ]:         42 :                 if (primebitlen % 8)
     205                 :         10 :                         BN_rshift(x_candidate, x_candidate,
     206                 :            :                                   (8 - (primebitlen % 8)));
     207                 :            : 
     208         [ -  + ]:         42 :                 if (BN_ucmp(x_candidate, grp->prime) >= 0)
     209                 :          0 :                         continue;
     210                 :            : 
     211                 :         42 :                 wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate",
     212                 :            :                             prfbuf, primebytelen);
     213                 :            : 
     214                 :            :                 /*
     215                 :            :                  * need to unambiguously identify the solution, if there is
     216                 :            :                  * one...
     217                 :            :                  */
     218 [ +  + ][ +  - ]:         42 :                 if (BN_is_odd(rnd))
     219                 :         25 :                         is_odd = 1;
     220                 :            :                 else
     221                 :         17 :                         is_odd = 0;
     222                 :            : 
     223                 :            :                 /*
     224                 :            :                  * solve the quadratic equation, if it's not solvable then we
     225                 :            :                  * don't have a point
     226                 :            :                  */
     227         [ +  + ]:         42 :                 if (!EC_POINT_set_compressed_coordinates_GFp(grp->group,
     228                 :            :                                                              grp->pwe,
     229                 :            :                                                              x_candidate,
     230                 :            :                                                              is_odd, NULL))
     231                 :         24 :                         continue;
     232                 :            :                 /*
     233                 :            :                  * If there's a solution to the equation then the point must be
     234                 :            :                  * on the curve so why check again explicitly? OpenSSL code
     235                 :            :                  * says this is required by X9.62. We're not X9.62 but it can't
     236                 :            :                  * hurt just to be sure.
     237                 :            :                  */
     238         [ -  + ]:         18 :                 if (!EC_POINT_is_on_curve(grp->group, grp->pwe, NULL)) {
     239                 :          0 :                         wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve");
     240                 :          0 :                         continue;
     241                 :            :                 }
     242                 :            : 
     243         [ -  + ]:         18 :                 if (BN_cmp(cofactor, BN_value_one())) {
     244                 :            :                         /* make sure the point is not in a small sub-group */
     245         [ #  # ]:          0 :                         if (!EC_POINT_mul(grp->group, grp->pwe, NULL, grp->pwe,
     246                 :            :                                           cofactor, NULL)) {
     247                 :          0 :                                 wpa_printf(MSG_INFO, "EAP-pwd: cannot "
     248                 :            :                                            "multiply generator by order");
     249                 :          0 :                                 continue;
     250                 :            :                         }
     251         [ #  # ]:          0 :                         if (EC_POINT_is_at_infinity(grp->group, grp->pwe)) {
     252                 :          0 :                                 wpa_printf(MSG_INFO, "EAP-pwd: point is at "
     253                 :            :                                            "infinity");
     254                 :          0 :                                 continue;
     255                 :            :                         }
     256                 :            :                 }
     257                 :            :                 /* if we got here then we have a new generator. */
     258                 :         18 :                 break;
     259                 :         24 :         }
     260                 :         18 :         wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr);
     261                 :         18 :         grp->group_num = num;
     262                 :            :         if (0) {
     263                 :            :  fail:
     264                 :          0 :                 EC_GROUP_free(grp->group);
     265                 :          0 :                 grp->group = NULL;
     266                 :          0 :                 EC_POINT_free(grp->pwe);
     267                 :          0 :                 grp->pwe = NULL;
     268                 :          0 :                 BN_free(grp->order);
     269                 :          0 :                 grp->order = NULL;
     270                 :          0 :                 BN_free(grp->prime);
     271                 :          0 :                 grp->prime = NULL;
     272                 :          0 :                 ret = 1;
     273                 :            :         }
     274                 :            :         /* cleanliness and order.... */
     275                 :         18 :         BN_free(cofactor);
     276                 :         18 :         BN_free(x_candidate);
     277                 :         18 :         BN_free(rnd);
     278                 :         18 :         os_free(prfbuf);
     279                 :            : 
     280                 :         18 :         return ret;
     281                 :            : }
     282                 :            : 
     283                 :            : 
     284                 :         16 : int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k,
     285                 :            :                  BIGNUM *peer_scalar, BIGNUM *server_scalar,
     286                 :            :                  u8 *confirm_peer, u8 *confirm_server,
     287                 :            :                  u32 *ciphersuite, u8 *msk, u8 *emsk)
     288                 :            : {
     289                 :            :         struct crypto_hash *hash;
     290                 :            :         u8 mk[SHA256_MAC_LEN], *cruft;
     291                 :            :         u8 session_id[SHA256_MAC_LEN + 1];
     292                 :            :         u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN];
     293                 :            :         int offset;
     294                 :            : 
     295         [ -  + ]:         16 :         if ((cruft = os_malloc(BN_num_bytes(grp->prime))) == NULL)
     296                 :          0 :                 return -1;
     297                 :            : 
     298                 :            :         /*
     299                 :            :          * first compute the session-id = TypeCode | H(ciphersuite | scal_p |
     300                 :            :          *      scal_s)
     301                 :            :          */
     302                 :         16 :         session_id[0] = EAP_TYPE_PWD;
     303                 :         16 :         hash = eap_pwd_h_init();
     304         [ -  + ]:         16 :         if (hash == NULL) {
     305                 :          0 :                 os_free(cruft);
     306                 :          0 :                 return -1;
     307                 :            :         }
     308                 :         16 :         eap_pwd_h_update(hash, (u8 *) ciphersuite, sizeof(u32));
     309                 :         16 :         offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar);
     310                 :         16 :         os_memset(cruft, 0, BN_num_bytes(grp->prime));
     311                 :         16 :         BN_bn2bin(peer_scalar, cruft + offset);
     312                 :         16 :         eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order));
     313                 :         16 :         offset = BN_num_bytes(grp->order) - BN_num_bytes(server_scalar);
     314                 :         16 :         os_memset(cruft, 0, BN_num_bytes(grp->prime));
     315                 :         16 :         BN_bn2bin(server_scalar, cruft + offset);
     316                 :         16 :         eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order));
     317                 :         16 :         eap_pwd_h_final(hash, &session_id[1]);
     318                 :            : 
     319                 :            :         /* then compute MK = H(k | confirm-peer | confirm-server) */
     320                 :         16 :         hash = eap_pwd_h_init();
     321         [ -  + ]:         16 :         if (hash == NULL) {
     322                 :          0 :                 os_free(cruft);
     323                 :          0 :                 return -1;
     324                 :            :         }
     325                 :         16 :         offset = BN_num_bytes(grp->prime) - BN_num_bytes(k);
     326                 :         16 :         os_memset(cruft, 0, BN_num_bytes(grp->prime));
     327                 :         16 :         BN_bn2bin(k, cruft + offset);
     328                 :         16 :         eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->prime));
     329                 :         16 :         os_free(cruft);
     330                 :         16 :         eap_pwd_h_update(hash, confirm_peer, SHA256_MAC_LEN);
     331                 :         16 :         eap_pwd_h_update(hash, confirm_server, SHA256_MAC_LEN);
     332                 :         16 :         eap_pwd_h_final(hash, mk);
     333                 :            : 
     334                 :            :         /* stretch the mk with the session-id to get MSK | EMSK */
     335         [ -  + ]:         16 :         if (eap_pwd_kdf(mk, SHA256_MAC_LEN,
     336                 :            :                         session_id, SHA256_MAC_LEN + 1,
     337                 :            :                         msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8) < 0) {
     338                 :          0 :                 return -1;
     339                 :            :         }
     340                 :            : 
     341                 :         16 :         os_memcpy(msk, msk_emsk, EAP_MSK_LEN);
     342                 :         16 :         os_memcpy(emsk, msk_emsk + EAP_MSK_LEN, EAP_EMSK_LEN);
     343                 :            : 
     344                 :         16 :         return 1;
     345                 :            : }

Generated by: LCOV version 1.9