1/* 2 * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 6 * as published by the Free Software Foundation 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 */ 13 14#include <stddef.h> 15#include <stdint.h> 16#include <stdlib.h> 17#include <stdbool.h> 18#include <string.h> 19#include <unistd.h> 20#include <stdio.h> 21#include "ead.h" 22 23#include "sha1.c" 24#include "aes.c" 25 26#if EAD_DEBUGLEVEL >= 1 27#define DEBUG(n, format, ...) do { \ 28 if (EAD_DEBUGLEVEL >= n) \ 29 fprintf(stderr, format, ##__VA_ARGS__); \ 30} while (0); 31 32#else 33#define DEBUG(n, format, ...) do {} while(0) 34#endif 35 36 37static uint32_t aes_enc_ctx[AES_PRIV_SIZE]; 38static uint32_t aes_dec_ctx[AES_PRIV_SIZE]; 39static uint32_t ead_rx_iv; 40static uint32_t ead_tx_iv; 41static uint32_t ivofs_vec; 42static unsigned int ivofs_idx = 0; 43static uint32_t W[80]; /* work space for sha1 */ 44 45#define EAD_ENC_PAD 64 46 47void 48ead_set_key(unsigned char *skey) 49{ 50 uint32_t *ivp = (uint32_t *)skey; 51 52 memset(aes_enc_ctx, 0, sizeof(aes_enc_ctx)); 53 memset(aes_dec_ctx, 0, sizeof(aes_dec_ctx)); 54 55 /* first 32 bytes of skey are used as aes key for 56 * encryption and decryption */ 57 rijndaelKeySetupEnc(aes_enc_ctx, skey); 58 rijndaelKeySetupDec(aes_dec_ctx, skey); 59 60 /* the following bytes are used as initialization vector for messages 61 * (highest byte cleared to avoid overflow) */ 62 ivp += 8; 63 ead_rx_iv = ntohl(*ivp) & 0x00ffffff; 64 ead_tx_iv = ead_rx_iv; 65 66 /* the last bytes are used to feed the random iv increment */ 67 ivp++; 68 ivofs_vec = *ivp; 69} 70 71 72static bool 73ead_check_rx_iv(uint32_t iv) 74{ 75 if (iv <= ead_rx_iv) 76 return false; 77 78 if (iv > ead_rx_iv + EAD_MAX_IV_INCR) 79 return false; 80 81 ead_rx_iv = iv; 82 return true; 83} 84 85 86static uint32_t 87ead_get_tx_iv(void) 88{ 89 unsigned int ofs; 90 91 ofs = 1 + ((ivofs_vec >> 2 * ivofs_idx) & 0x3); 92 ivofs_idx = (ivofs_idx + 1) % 16; 93 ead_tx_iv += ofs; 94 95 return ead_tx_iv; 96} 97 98static void 99ead_hash_message(struct ead_msg_encrypted *enc, uint32_t *hash, int len) 100{ 101 unsigned char *data = (unsigned char *) enc; 102 103 /* hash the packet with the stored hash part initialized to zero */ 104 sha_init(hash); 105 memset(enc->hash, 0, sizeof(enc->hash)); 106 while (len > 0) { 107 sha_transform(hash, data, W); 108 len -= 64; 109 data += 64; 110 } 111} 112 113void 114ead_encrypt_message(struct ead_msg *msg, unsigned int len) 115{ 116 struct ead_msg_encrypted *enc = EAD_DATA(msg, enc); 117 unsigned char *data = (unsigned char *) enc; 118 uint32_t hash[5]; 119 int enclen, i; 120 121 len += sizeof(struct ead_msg_encrypted); 122 enc->pad = (EAD_ENC_PAD - (len % EAD_ENC_PAD)) % EAD_ENC_PAD; 123 enclen = len + enc->pad; 124 msg->len = htonl(enclen); 125 enc->iv = htonl(ead_get_tx_iv()); 126 127 ead_hash_message(enc, hash, enclen); 128 for (i = 0; i < 5; i++) 129 enc->hash[i] = htonl(hash[i]); 130 DEBUG(2, "SHA1 generate (0x%08x), len=%d\n", enc->hash[0], enclen); 131 132 while (enclen > 0) { 133 rijndaelEncrypt(aes_enc_ctx, data, data); 134 data += 16; 135 enclen -= 16; 136 } 137} 138 139int 140ead_decrypt_message(struct ead_msg *msg) 141{ 142 struct ead_msg_encrypted *enc = EAD_DATA(msg, enc); 143 unsigned char *data = (unsigned char *) enc; 144 uint32_t hash_old[5], hash_new[5]; 145 int len = ntohl(msg->len); 146 int i, enclen = len; 147 148 if (!len || (len % EAD_ENC_PAD > 0)) 149 return 0; 150 151 while (len > 0) { 152 rijndaelDecrypt(aes_dec_ctx, data, data); 153 data += 16; 154 len -= 16; 155 } 156 157 data = (unsigned char *) enc; 158 159 if (enc->pad >= EAD_ENC_PAD) { 160 DEBUG(2, "Invalid padding length\n"); 161 return 0; 162 } 163 164 if (!ead_check_rx_iv(ntohl(enc->iv))) { 165 DEBUG(2, "RX IV mismatch (0x%08x <> 0x%08x)\n", ead_rx_iv, ntohl(enc->iv)); 166 return 0; 167 } 168 169 for (i = 0; i < 5; i++) 170 hash_old[i] = ntohl(enc->hash[i]); 171 ead_hash_message(enc, hash_new, enclen); 172 if (memcmp(hash_old, hash_new, sizeof(hash_old)) != 0) { 173 DEBUG(2, "SHA1 mismatch (0x%08x != 0x%08x), len=%d\n", hash_old[0], hash_new[0], enclen); 174 return 0; 175 } 176 177 enclen -= enc->pad + sizeof(struct ead_msg_encrypted); 178 return enclen; 179} 180