1323124Sdes/* $OpenBSD: mac.c,v 1.33 2016/07/08 03:44:42 djm Exp $ */ 276259Sgreen/* 376259Sgreen * Copyright (c) 2001 Markus Friedl. All rights reserved. 476259Sgreen * 576259Sgreen * Redistribution and use in source and binary forms, with or without 676259Sgreen * modification, are permitted provided that the following conditions 776259Sgreen * are met: 876259Sgreen * 1. Redistributions of source code must retain the above copyright 976259Sgreen * notice, this list of conditions and the following disclaimer. 1076259Sgreen * 2. Redistributions in binary form must reproduce the above copyright 1176259Sgreen * notice, this list of conditions and the following disclaimer in the 1276259Sgreen * documentation and/or other materials provided with the distribution. 1376259Sgreen * 1476259Sgreen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1576259Sgreen * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1676259Sgreen * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1776259Sgreen * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1876259Sgreen * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1976259Sgreen * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2076259Sgreen * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2176259Sgreen * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2276259Sgreen * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2376259Sgreen * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2476259Sgreen */ 2576259Sgreen 2676259Sgreen#include "includes.h" 2776259Sgreen 28162852Sdes#include <sys/types.h> 29162852Sdes 30162852Sdes#include <string.h> 31295367Sdes#include <stdio.h> 32162852Sdes 33264377Sdes#include "digest.h" 34264377Sdes#include "hmac.h" 35181111Sdes#include "umac.h" 36295367Sdes#include "mac.h" 37295367Sdes#include "misc.h" 38295367Sdes#include "ssherr.h" 39295367Sdes#include "sshbuf.h" 40181111Sdes 41240075Sdes#include "openbsd-compat/openssl-compat.h" 42240075Sdes 43264377Sdes#define SSH_DIGEST 1 /* SSH_DIGEST_XXX */ 44181111Sdes#define SSH_UMAC 2 /* UMAC (not integrated with OpenSSL) */ 45248619Sdes#define SSH_UMAC128 3 46181111Sdes 47255767Sdesstruct macalg { 4876259Sgreen char *name; 49181111Sdes int type; 50264377Sdes int alg; 5176259Sgreen int truncatebits; /* truncate digest if != 0 */ 52181111Sdes int key_len; /* just for UMAC */ 53181111Sdes int len; /* just for UMAC */ 54248619Sdes int etm; /* Encrypt-then-MAC */ 55255767Sdes}; 56255767Sdes 57255767Sdesstatic const struct macalg macs[] = { 58248619Sdes /* Encrypt-and-MAC (encrypt-and-authenticate) variants */ 59264377Sdes { "hmac-sha1", SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 0 }, 60264377Sdes { "hmac-sha1-96", SSH_DIGEST, SSH_DIGEST_SHA1, 96, 0, 0, 0 }, 61226046Sdes#ifdef HAVE_EVP_SHA256 62264377Sdes { "hmac-sha2-256", SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 0 }, 63264377Sdes { "hmac-sha2-512", SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 0 }, 64226046Sdes#endif 65264377Sdes { "hmac-md5", SSH_DIGEST, SSH_DIGEST_MD5, 0, 0, 0, 0 }, 66264377Sdes { "hmac-md5-96", SSH_DIGEST, SSH_DIGEST_MD5, 96, 0, 0, 0 }, 67264377Sdes { "hmac-ripemd160", SSH_DIGEST, SSH_DIGEST_RIPEMD160, 0, 0, 0, 0 }, 68264377Sdes { "hmac-ripemd160@openssh.com", SSH_DIGEST, SSH_DIGEST_RIPEMD160, 0, 0, 0, 0 }, 69264377Sdes { "umac-64@openssh.com", SSH_UMAC, 0, 0, 128, 64, 0 }, 70264377Sdes { "umac-128@openssh.com", SSH_UMAC128, 0, 0, 128, 128, 0 }, 71248619Sdes 72248619Sdes /* Encrypt-then-MAC variants */ 73264377Sdes { "hmac-sha1-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 1 }, 74264377Sdes { "hmac-sha1-96-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA1, 96, 0, 0, 1 }, 75248619Sdes#ifdef HAVE_EVP_SHA256 76264377Sdes { "hmac-sha2-256-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 1 }, 77264377Sdes { "hmac-sha2-512-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 1 }, 78248619Sdes#endif 79264377Sdes { "hmac-md5-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_MD5, 0, 0, 0, 1 }, 80264377Sdes { "hmac-md5-96-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_MD5, 96, 0, 0, 1 }, 81264377Sdes { "hmac-ripemd160-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_RIPEMD160, 0, 0, 0, 1 }, 82264377Sdes { "umac-64-etm@openssh.com", SSH_UMAC, 0, 0, 128, 64, 1 }, 83264377Sdes { "umac-128-etm@openssh.com", SSH_UMAC128, 0, 0, 128, 128, 1 }, 84248619Sdes 85264377Sdes { NULL, 0, 0, 0, 0, 0, 0 } 8676259Sgreen}; 8776259Sgreen 88262566Sdes/* Returns a list of supported MACs separated by the specified char. */ 89255767Sdeschar * 90262566Sdesmac_alg_list(char sep) 91255767Sdes{ 92295367Sdes char *ret = NULL, *tmp; 93255767Sdes size_t nlen, rlen = 0; 94255767Sdes const struct macalg *m; 95255767Sdes 96255767Sdes for (m = macs; m->name != NULL; m++) { 97255767Sdes if (ret != NULL) 98262566Sdes ret[rlen++] = sep; 99255767Sdes nlen = strlen(m->name); 100295367Sdes if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) { 101295367Sdes free(ret); 102295367Sdes return NULL; 103295367Sdes } 104295367Sdes ret = tmp; 105255767Sdes memcpy(ret + rlen, m->name, nlen + 1); 106255767Sdes rlen += nlen; 107255767Sdes } 108255767Sdes return ret; 109255767Sdes} 110255767Sdes 111295367Sdesstatic int 112295367Sdesmac_setup_by_alg(struct sshmac *mac, const struct macalg *macalg) 113181111Sdes{ 114255767Sdes mac->type = macalg->type; 115264377Sdes if (mac->type == SSH_DIGEST) { 116264377Sdes if ((mac->hmac_ctx = ssh_hmac_start(macalg->alg)) == NULL) 117295367Sdes return SSH_ERR_ALLOC_FAIL; 118264377Sdes mac->key_len = mac->mac_len = ssh_hmac_bytes(macalg->alg); 119181111Sdes } else { 120255767Sdes mac->mac_len = macalg->len / 8; 121255767Sdes mac->key_len = macalg->key_len / 8; 122181111Sdes mac->umac_ctx = NULL; 123181111Sdes } 124255767Sdes if (macalg->truncatebits != 0) 125255767Sdes mac->mac_len = macalg->truncatebits / 8; 126255767Sdes mac->etm = macalg->etm; 127295367Sdes return 0; 128181111Sdes} 129181111Sdes 13076259Sgreenint 131295367Sdesmac_setup(struct sshmac *mac, char *name) 13276259Sgreen{ 133255767Sdes const struct macalg *m; 134149749Sdes 135255767Sdes for (m = macs; m->name != NULL; m++) { 136255767Sdes if (strcmp(name, m->name) != 0) 137255767Sdes continue; 138295367Sdes if (mac != NULL) 139295367Sdes return mac_setup_by_alg(mac, m); 140295367Sdes return 0; 14176259Sgreen } 142295367Sdes return SSH_ERR_INVALID_ARGUMENT; 14376259Sgreen} 14476259Sgreen 145181111Sdesint 146295367Sdesmac_init(struct sshmac *mac) 147181111Sdes{ 148181111Sdes if (mac->key == NULL) 149295367Sdes return SSH_ERR_INVALID_ARGUMENT; 150181111Sdes switch (mac->type) { 151264377Sdes case SSH_DIGEST: 152264377Sdes if (mac->hmac_ctx == NULL || 153264377Sdes ssh_hmac_init(mac->hmac_ctx, mac->key, mac->key_len) < 0) 154295367Sdes return SSH_ERR_INVALID_ARGUMENT; 155181111Sdes return 0; 156181111Sdes case SSH_UMAC: 157295367Sdes if ((mac->umac_ctx = umac_new(mac->key)) == NULL) 158295367Sdes return SSH_ERR_ALLOC_FAIL; 159181111Sdes return 0; 160248619Sdes case SSH_UMAC128: 161295367Sdes if ((mac->umac_ctx = umac128_new(mac->key)) == NULL) 162295367Sdes return SSH_ERR_ALLOC_FAIL; 163248619Sdes return 0; 164181111Sdes default: 165295367Sdes return SSH_ERR_INVALID_ARGUMENT; 166181111Sdes } 167181111Sdes} 168181111Sdes 169295367Sdesint 170323124Sdesmac_compute(struct sshmac *mac, u_int32_t seqno, 171323124Sdes const u_char *data, int datalen, 172295367Sdes u_char *digest, size_t dlen) 17376259Sgreen{ 174255767Sdes static union { 175295367Sdes u_char m[SSH_DIGEST_MAX_LENGTH]; 176255767Sdes u_int64_t for_align; 177255767Sdes } u; 178295367Sdes u_char b[4]; 179295367Sdes u_char nonce[8]; 18076259Sgreen 181255767Sdes if (mac->mac_len > sizeof(u)) 182295367Sdes return SSH_ERR_INTERNAL_ERROR; 183181111Sdes 184181111Sdes switch (mac->type) { 185264377Sdes case SSH_DIGEST: 186181111Sdes put_u32(b, seqno); 187181111Sdes /* reset HMAC context */ 188264377Sdes if (ssh_hmac_init(mac->hmac_ctx, NULL, 0) < 0 || 189264377Sdes ssh_hmac_update(mac->hmac_ctx, b, sizeof(b)) < 0 || 190264377Sdes ssh_hmac_update(mac->hmac_ctx, data, datalen) < 0 || 191264377Sdes ssh_hmac_final(mac->hmac_ctx, u.m, sizeof(u.m)) < 0) 192295367Sdes return SSH_ERR_LIBCRYPTO_ERROR; 193181111Sdes break; 194181111Sdes case SSH_UMAC: 195295367Sdes POKE_U64(nonce, seqno); 196181111Sdes umac_update(mac->umac_ctx, data, datalen); 197255767Sdes umac_final(mac->umac_ctx, u.m, nonce); 198181111Sdes break; 199248619Sdes case SSH_UMAC128: 200248619Sdes put_u64(nonce, seqno); 201248619Sdes umac128_update(mac->umac_ctx, data, datalen); 202255767Sdes umac128_final(mac->umac_ctx, u.m, nonce); 203248619Sdes break; 204181111Sdes default: 205295367Sdes return SSH_ERR_INVALID_ARGUMENT; 206181111Sdes } 207295367Sdes if (digest != NULL) { 208295367Sdes if (dlen > mac->mac_len) 209295367Sdes dlen = mac->mac_len; 210295367Sdes memcpy(digest, u.m, dlen); 211295367Sdes } 212295367Sdes return 0; 21376259Sgreen} 21476259Sgreen 215323124Sdesint 216323124Sdesmac_check(struct sshmac *mac, u_int32_t seqno, 217323124Sdes const u_char *data, size_t dlen, 218323124Sdes const u_char *theirmac, size_t mlen) 219323124Sdes{ 220323124Sdes u_char ourmac[SSH_DIGEST_MAX_LENGTH]; 221323124Sdes int r; 222323124Sdes 223323124Sdes if (mac->mac_len > mlen) 224323124Sdes return SSH_ERR_INVALID_ARGUMENT; 225323124Sdes if ((r = mac_compute(mac, seqno, data, dlen, 226323124Sdes ourmac, sizeof(ourmac))) != 0) 227323124Sdes return r; 228323124Sdes if (timingsafe_bcmp(ourmac, theirmac, mac->mac_len) != 0) 229323124Sdes return SSH_ERR_MAC_INVALID; 230323124Sdes return 0; 231323124Sdes} 232323124Sdes 233181111Sdesvoid 234295367Sdesmac_clear(struct sshmac *mac) 235181111Sdes{ 236181111Sdes if (mac->type == SSH_UMAC) { 237181111Sdes if (mac->umac_ctx != NULL) 238181111Sdes umac_delete(mac->umac_ctx); 239248619Sdes } else if (mac->type == SSH_UMAC128) { 240248619Sdes if (mac->umac_ctx != NULL) 241248619Sdes umac128_delete(mac->umac_ctx); 242264377Sdes } else if (mac->hmac_ctx != NULL) 243264377Sdes ssh_hmac_free(mac->hmac_ctx); 244264377Sdes mac->hmac_ctx = NULL; 245181111Sdes mac->umac_ctx = NULL; 246181111Sdes} 247181111Sdes 24876259Sgreen/* XXX copied from ciphers_valid */ 24976259Sgreen#define MAC_SEP "," 25076259Sgreenint 25176259Sgreenmac_valid(const char *names) 25276259Sgreen{ 25376259Sgreen char *maclist, *cp, *p; 25476259Sgreen 25576259Sgreen if (names == NULL || strcmp(names, "") == 0) 256295367Sdes return 0; 257295367Sdes if ((maclist = cp = strdup(names)) == NULL) 258295367Sdes return 0; 25976259Sgreen for ((p = strsep(&cp, MAC_SEP)); p && *p != '\0'; 26092555Sdes (p = strsep(&cp, MAC_SEP))) { 261181111Sdes if (mac_setup(NULL, p) < 0) { 262255767Sdes free(maclist); 263295367Sdes return 0; 26476259Sgreen } 26576259Sgreen } 266255767Sdes free(maclist); 267295367Sdes return 1; 26876259Sgreen} 269