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/opensslconf.h>
  29 #include <openssl/hmac.h>
  30 
  31 #include "xmalloc.h"
  32 #include "getput.h"
  33 #include "log.h"
  34 #include "cipher.h"
  35 #include "kex.h"
  36 #include "mac.h"
  37 #include "misc.h"
  38 
  39 #define SSH_EVP         1       /* OpenSSL EVP-based MAC */
  40 
  41 struct {
  42         char            *name;
  43         int             type;
  44         const EVP_MD *  (*mdfunc)(void);
  45         int             truncatebits;   /* truncate digest if != 0 */
  46         int             key_len;        /* will be used if we have UMAC */
  47 } macs[] = {
  48         { "hmac-sha1",                  SSH_EVP, EVP_sha1,       0, -1 },
  49         { "hmac-sha1-96",               SSH_EVP, EVP_sha1,      96, -1 },
  50         { "hmac-md5",                   SSH_EVP, EVP_md5,        0, -1 },
  51         { "hmac-md5-96",                SSH_EVP, EVP_md5,       96, -1 },
  52 #ifdef SOLARIS_SSH_ENABLE_RIPEMD160
  53         { "hmac-ripemd160",             SSH_EVP, EVP_ripemd160,  0, -1 },
  54         { "hmac-ripemd160@openssh.com", SSH_EVP, EVP_ripemd160,  0, -1 },
  55 #endif /* SOLARIS_SSH_ENABLE_RIPEMD160 */
  56         { NULL,                         0,       NULL,           0, -1 }
  57 };
  58 
  59 static void
  60 mac_setup_by_id(Mac *mac, int which)
  61 {
  62         int evp_len;
  63         mac->type = macs[which].type;
  64         if (mac->type == SSH_EVP) {
  65                 mac->evp_md = (*macs[which].mdfunc)();
  66                 if ((evp_len = EVP_MD_size(mac->evp_md)) <= 0)
  67                         fatal("mac %s len %d", mac->name, evp_len);
  68                 mac->key_len = mac->mac_len = (u_int)evp_len;
  69         } else
  70                 fatal("wrong MAC type (%d)", mac->type);
  71         if (macs[which].truncatebits != 0)
  72                 mac->mac_len = macs[which].truncatebits / 8;
  73 }
  74 
  75 int
  76 mac_setup(Mac *mac, char *name)
  77 {
  78         int i;
  79 
  80         for (i = 0; macs[i].name; i++) {
  81                 if (strcmp(name, macs[i].name) == 0) {
  82                         if (mac != NULL)
  83                                 mac_setup_by_id(mac, i);
  84                         debug2("mac_setup: found %s", name);
  85                         return (0);
  86                 }
  87         }
  88         debug2("mac_setup: unknown %s", name);
  89         return (-1);
  90 }
  91 
  92 int
  93 mac_init(Mac *mac)
  94 {
  95         if (mac->key == NULL)
  96                 fatal("mac_init: no key");
  97         switch (mac->type) {
  98         case SSH_EVP:
  99                 if (mac->evp_md == NULL)
 100                         return -1;
 101                 HMAC_Init(&mac->evp_ctx, mac->key, mac->key_len, mac->evp_md);
 102                 return 0;
 103         default:
 104                 return -1;
 105         }
 106 }
 107 
 108 u_char *
 109 mac_compute(Mac *mac, u_int32_t seqno, u_char *data, int datalen)
 110 {
 111         static u_char m[EVP_MAX_MD_SIZE];
 112         u_char b[4];
 113 
 114         if (mac->mac_len > sizeof(m))
 115                 fatal("mac_compute: mac too long %u %lu",
 116                     mac->mac_len, (u_long)sizeof(m));
 117 
 118         switch (mac->type) {
 119         case SSH_EVP:
 120                 put_u32(b, seqno);
 121                 /* reset HMAC context */
 122                 HMAC_Init(&mac->evp_ctx, NULL, 0, NULL);
 123                 HMAC_Update(&mac->evp_ctx, b, sizeof(b));
 124                 HMAC_Update(&mac->evp_ctx, data, datalen);
 125                 HMAC_Final(&mac->evp_ctx, m, NULL);
 126                 break;
 127         default:
 128                 fatal("mac_compute: unknown MAC type");
 129         }
 130 
 131         return (m);
 132 }
 133 
 134 void
 135 mac_clear(Mac *mac)
 136 {
 137         if (mac->evp_md != NULL)
 138                 HMAC_cleanup(&mac->evp_ctx);
 139         mac->evp_md = NULL;
 140 }
 141 
 142 /* XXX copied from ciphers_valid */
 143 #define MAC_SEP ","
 144 int
 145 mac_valid(const char *names)
 146 {
 147         char *maclist, *cp, *p;
 148 
 149         if (names == NULL || strcmp(names, "") == 0)
 150                 return (0);
 151         maclist = cp = xstrdup(names);
 152         for ((p = strsep(&cp, MAC_SEP)); p && *p != '\0';
 153             (p = strsep(&cp, MAC_SEP))) {
 154                 if (mac_setup(NULL, p) < 0) {
 155                         debug("bad mac %s [%s]", p, names);
 156                         xfree(maclist);
 157                         return (0);
 158                 } else {
 159                         debug3("mac ok: %s [%s]", p, names);
 160                 }
 161         }
 162         debug3("macs ok: [%s]", names);
 163         xfree(maclist);
 164         return (1);
 165 }