1 /*
   2  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
   3  *
   4  * Redistribution and use in source and binary forms, with or without
   5  * modification, are permitted provided that the following conditions
   6  * are met:
   7  * 1. Redistributions of source code must retain the above copyright
   8  *    notice, this list of conditions and the following disclaimer.
   9  * 2. Redistributions in binary form must reproduce the above copyright
  10  *    notice, this list of conditions and the following disclaimer in the
  11  *    documentation and/or other materials provided with the distribution.
  12  *
  13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  23  */
  24 
  25 #include "includes.h"
  26 RCSID("$OpenBSD: mac.c,v 1.5 2002/05/16 22:02:50 markus Exp $");
  27 
  28 #include <openssl/hmac.h>
  29 
  30 #include "xmalloc.h"
  31 #include "getput.h"
  32 #include "log.h"
  33 #include "cipher.h"
  34 #include "kex.h"
  35 #include "mac.h"
  36 #include "misc.h"
  37 
  38 #define SSH_EVP         1       /* OpenSSL EVP-based MAC */
  39 
  40 struct {
  41         char            *name;
  42         int             type;
  43         const EVP_MD *  (*mdfunc)(void);
  44         int             truncatebits;   /* truncate digest if != 0 */
  45         int             key_len;        /* will be used if we have UMAC */
  46 } macs[] = {
  47         { "hmac-sha1",                  SSH_EVP, EVP_sha1,       0, -1 },
  48         { "hmac-sha1-96",               SSH_EVP, EVP_sha1,      96, -1 },
  49         { "hmac-md5",                   SSH_EVP, EVP_md5,        0, -1 },
  50         { "hmac-md5-96",                SSH_EVP, EVP_md5,       96, -1 },
  51 #ifdef SOLARIS_SSH_ENABLE_RIPEMD160
  52         { "hmac-ripemd160",             SSH_EVP, EVP_ripemd160,  0, -1 },
  53         { "hmac-ripemd160@openssh.com", SSH_EVP, EVP_ripemd160,  0, -1 },
  54 #endif /* SOLARIS_SSH_ENABLE_RIPEMD160 */
  55         { NULL,                         0,       NULL,           0, -1 }
  56 };
  57 
  58 static void
  59 mac_setup_by_id(Mac *mac, int which)
  60 {
  61         int evp_len;
  62         mac->type = macs[which].type;
  63         if (mac->type == SSH_EVP) {
  64                 mac->evp_md = (*macs[which].mdfunc)();
  65                 if ((evp_len = EVP_MD_size(mac->evp_md)) <= 0)
  66                         fatal("mac %s len %d", mac->name, evp_len);
  67                 mac->key_len = mac->mac_len = (u_int)evp_len;
  68         } else
  69                 fatal("wrong MAC type (%d)", mac->type);
  70         if (macs[which].truncatebits != 0)
  71                 mac->mac_len = macs[which].truncatebits / 8;
  72 }
  73 
  74 int
  75 mac_setup(Mac *mac, char *name)
  76 {
  77         int i;
  78 
  79         for (i = 0; macs[i].name; i++) {
  80                 if (strcmp(name, macs[i].name) == 0) {
  81                         if (mac != NULL)
  82                                 mac_setup_by_id(mac, i);
  83                         debug2("mac_setup: found %s", name);
  84                         return (0);
  85                 }
  86         }
  87         debug2("mac_setup: unknown %s", name);
  88         return (-1);
  89 }
  90 
  91 int
  92 mac_init(Mac *mac)
  93 {
  94         if (mac->key == NULL)
  95                 fatal("mac_init: no key");
  96         switch (mac->type) {
  97         case SSH_EVP:
  98                 if (mac->evp_md == NULL)
  99                         return -1;
 100                 HMAC_Init(&mac->evp_ctx, mac->key, mac->key_len, mac->evp_md);
 101                 return 0;
 102         default:
 103                 return -1;
 104         }
 105 }
 106 
 107 u_char *
 108 mac_compute(Mac *mac, u_int32_t seqno, u_char *data, int datalen)
 109 {
 110         static u_char m[EVP_MAX_MD_SIZE];
 111         u_char b[4];
 112 
 113         if (mac->mac_len > sizeof(m))
 114                 fatal("mac_compute: mac too long %u %lu",
 115                     mac->mac_len, (u_long)sizeof(m));
 116 
 117         switch (mac->type) {
 118         case SSH_EVP:
 119                 put_u32(b, seqno);
 120                 /* reset HMAC context */
 121                 HMAC_Init(&mac->evp_ctx, NULL, 0, NULL);
 122                 HMAC_Update(&mac->evp_ctx, b, sizeof(b));
 123                 HMAC_Update(&mac->evp_ctx, data, datalen);
 124                 HMAC_Final(&mac->evp_ctx, m, NULL);
 125                 break;
 126         default:
 127                 fatal("mac_compute: unknown MAC type");
 128         }
 129 
 130         return (m);
 131 }
 132 
 133 void
 134 mac_clear(Mac *mac)
 135 {
 136         if (mac->evp_md != NULL)
 137                 HMAC_cleanup(&mac->evp_ctx);
 138         mac->evp_md = NULL;
 139 }
 140 
 141 /* XXX copied from ciphers_valid */
 142 #define MAC_SEP ","
 143 int
 144 mac_valid(const char *names)
 145 {
 146         char *maclist, *cp, *p;
 147 
 148         if (names == NULL || strcmp(names, "") == 0)
 149                 return (0);
 150         maclist = cp = xstrdup(names);
 151         for ((p = strsep(&cp, MAC_SEP)); p && *p != '\0';
 152             (p = strsep(&cp, MAC_SEP))) {
 153                 if (mac_setup(NULL, p) < 0) {
 154                         debug("bad mac %s [%s]", p, names);
 155                         xfree(maclist);
 156                         return (0);
 157                 } else {
 158                         debug3("mac ok: %s [%s]", p, names);
 159                 }
 160         }
 161         debug3("macs ok: [%s]", names);
 162         xfree(maclist);
 163         return (1);
 164 }