1 /* 2 * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved. 3 * 4 * Licensed under the Apache License 2.0 (the "License"). You may not use 5 * this file except in compliance with the License. You can obtain a copy 6 * in the file LICENSE in the source distribution or at 7 * https://www.openssl.org/source/license.html 8 */ 9 10 /* RC4_HMAC_MD5 cipher implementation */ 11 12 /* 13 * MD5 and RC4 low level APIs are deprecated for public use, but still ok for 14 * internal use. 15 */ 16 #include "internal/deprecated.h" 17 18 #include "cipher_rc4_hmac_md5.h" 19 20 #define NO_PAYLOAD_LENGTH ((size_t)-1) 21 22 #if defined(RC4_ASM) \ 23 && defined(MD5_ASM) \ 24 && (defined(__x86_64) \ 25 || defined(__x86_64__) \ 26 || defined(_M_AMD64) \ 27 || defined(_M_X64)) 28 # define STITCHED_CALL 29 # define MOD 32 /* 32 is $MOD from rc4_md5-x86_64.pl */ 30 #else 31 # define rc4_off 0 32 # define md5_off 0 33 #endif 34 35 static int cipher_hw_rc4_hmac_md5_initkey(PROV_CIPHER_CTX *bctx, 36 const uint8_t *key, size_t keylen) 37 { 38 PROV_RC4_HMAC_MD5_CTX *ctx = (PROV_RC4_HMAC_MD5_CTX *)bctx; 39 40 RC4_set_key(&ctx->ks.ks, keylen, key); 41 MD5_Init(&ctx->head); /* handy when benchmarking */ 42 ctx->tail = ctx->head; 43 ctx->md = ctx->head; 44 ctx->payload_length = NO_PAYLOAD_LENGTH; 45 bctx->removetlsfixed = MD5_DIGEST_LENGTH; 46 return 1; 47 } 48 49 static int cipher_hw_rc4_hmac_md5_cipher(PROV_CIPHER_CTX *bctx, 50 unsigned char *out, 51 const unsigned char *in, size_t len) 52 { 53 PROV_RC4_HMAC_MD5_CTX *ctx = (PROV_RC4_HMAC_MD5_CTX *)bctx; 54 RC4_KEY *ks = &ctx->ks.ks; 55 56 #if defined(STITCHED_CALL) 57 size_t rc4_off = MOD - 1 - (ks->x & (MOD - 1)); 58 size_t md5_off = MD5_CBLOCK - ctx->md.num, blocks; 59 unsigned int l; 60 #endif 61 size_t plen = ctx->payload_length; 62 63 if (plen != NO_PAYLOAD_LENGTH && len != (plen + MD5_DIGEST_LENGTH)) 64 return 0; 65 66 if (ctx->base.enc) { 67 if (plen == NO_PAYLOAD_LENGTH) 68 plen = len; 69 #if defined(STITCHED_CALL) 70 /* cipher has to "fall behind" */ 71 if (rc4_off > md5_off) 72 md5_off += MD5_CBLOCK; 73 74 if (plen > md5_off 75 && (blocks = (plen - md5_off) / MD5_CBLOCK) 76 && (OPENSSL_ia32cap_P[0] & (1 << 20)) == 0) { 77 MD5_Update(&ctx->md, in, md5_off); 78 RC4(ks, rc4_off, in, out); 79 80 rc4_md5_enc(ks, in + rc4_off, out + rc4_off, 81 &ctx->md, in + md5_off, blocks); 82 blocks *= MD5_CBLOCK; 83 rc4_off += blocks; 84 md5_off += blocks; 85 ctx->md.Nh += blocks >> 29; 86 ctx->md.Nl += blocks <<= 3; 87 if (ctx->md.Nl < (unsigned int)blocks) 88 ctx->md.Nh++; 89 } else { 90 rc4_off = 0; 91 md5_off = 0; 92 } 93 #endif 94 MD5_Update(&ctx->md, in + md5_off, plen - md5_off); 95 96 if (plen != len) { /* "TLS" mode of operation */ 97 if (in != out) 98 memcpy(out + rc4_off, in + rc4_off, plen - rc4_off); 99 100 /* calculate HMAC and append it to payload */ 101 MD5_Final(out + plen, &ctx->md); 102 ctx->md = ctx->tail; 103 MD5_Update(&ctx->md, out + plen, MD5_DIGEST_LENGTH); 104 MD5_Final(out + plen, &ctx->md); 105 /* encrypt HMAC at once */ 106 RC4(ks, len - rc4_off, out + rc4_off, out + rc4_off); 107 } else { 108 RC4(ks, len - rc4_off, in + rc4_off, out + rc4_off); 109 } 110 } else { 111 unsigned char mac[MD5_DIGEST_LENGTH]; 112 113 #if defined(STITCHED_CALL) 114 /* digest has to "fall behind" */ 115 if (md5_off > rc4_off) 116 rc4_off += 2 * MD5_CBLOCK; 117 else 118 rc4_off += MD5_CBLOCK; 119 120 if (len > rc4_off 121 && (blocks = (len - rc4_off) / MD5_CBLOCK) 122 && (OPENSSL_ia32cap_P[0] & (1 << 20)) == 0) { 123 RC4(ks, rc4_off, in, out); 124 MD5_Update(&ctx->md, out, md5_off); 125 126 rc4_md5_enc(ks, in + rc4_off, out + rc4_off, 127 &ctx->md, out + md5_off, blocks); 128 blocks *= MD5_CBLOCK; 129 rc4_off += blocks; 130 md5_off += blocks; 131 l = (ctx->md.Nl + (blocks << 3)) & 0xffffffffU; 132 if (l < ctx->md.Nl) 133 ctx->md.Nh++; 134 ctx->md.Nl = l; 135 ctx->md.Nh += blocks >> 29; 136 } else { 137 md5_off = 0; 138 rc4_off = 0; 139 } 140 #endif 141 /* decrypt HMAC at once */ 142 RC4(ks, len - rc4_off, in + rc4_off, out + rc4_off); 143 if (plen != NO_PAYLOAD_LENGTH) { 144 /* "TLS" mode of operation */ 145 MD5_Update(&ctx->md, out + md5_off, plen - md5_off); 146 147 /* calculate HMAC and verify it */ 148 MD5_Final(mac, &ctx->md); 149 ctx->md = ctx->tail; 150 MD5_Update(&ctx->md, mac, MD5_DIGEST_LENGTH); 151 MD5_Final(mac, &ctx->md); 152 153 if (CRYPTO_memcmp(out + plen, mac, MD5_DIGEST_LENGTH)) 154 return 0; 155 } else { 156 MD5_Update(&ctx->md, out + md5_off, len - md5_off); 157 } 158 } 159 160 ctx->payload_length = NO_PAYLOAD_LENGTH; 161 162 return 1; 163 } 164 165 static int cipher_hw_rc4_hmac_md5_tls_init(PROV_CIPHER_CTX *bctx, 166 unsigned char *aad, size_t aad_len) 167 { 168 PROV_RC4_HMAC_MD5_CTX *ctx = (PROV_RC4_HMAC_MD5_CTX *)bctx; 169 unsigned int len; 170 171 if (aad_len != EVP_AEAD_TLS1_AAD_LEN) 172 return 0; 173 174 len = aad[aad_len - 2] << 8 | aad[aad_len - 1]; 175 176 if (!bctx->enc) { 177 if (len < MD5_DIGEST_LENGTH) 178 return 0; 179 len -= MD5_DIGEST_LENGTH; 180 aad[aad_len - 2] = len >> 8; 181 aad[aad_len - 1] = len; 182 } 183 ctx->payload_length = len; 184 ctx->md = ctx->head; 185 MD5_Update(&ctx->md, aad, aad_len); 186 187 return MD5_DIGEST_LENGTH; 188 } 189 190 static void cipher_hw_rc4_hmac_md5_init_mackey(PROV_CIPHER_CTX *bctx, 191 const unsigned char *key, 192 size_t len) 193 { 194 PROV_RC4_HMAC_MD5_CTX *ctx = (PROV_RC4_HMAC_MD5_CTX *)bctx; 195 unsigned int i; 196 unsigned char hmac_key[64]; 197 198 memset(hmac_key, 0, sizeof(hmac_key)); 199 200 if (len > (int)sizeof(hmac_key)) { 201 MD5_Init(&ctx->head); 202 MD5_Update(&ctx->head, key, len); 203 MD5_Final(hmac_key, &ctx->head); 204 } else { 205 memcpy(hmac_key, key, len); 206 } 207 208 for (i = 0; i < sizeof(hmac_key); i++) 209 hmac_key[i] ^= 0x36; /* ipad */ 210 MD5_Init(&ctx->head); 211 MD5_Update(&ctx->head, hmac_key, sizeof(hmac_key)); 212 213 for (i = 0; i < sizeof(hmac_key); i++) 214 hmac_key[i] ^= 0x36 ^ 0x5c; /* opad */ 215 MD5_Init(&ctx->tail); 216 MD5_Update(&ctx->tail, hmac_key, sizeof(hmac_key)); 217 218 OPENSSL_cleanse(hmac_key, sizeof(hmac_key)); 219 } 220 221 static const PROV_CIPHER_HW_RC4_HMAC_MD5 rc4_hmac_md5_hw = { 222 { 223 cipher_hw_rc4_hmac_md5_initkey, 224 cipher_hw_rc4_hmac_md5_cipher 225 }, 226 cipher_hw_rc4_hmac_md5_tls_init, 227 cipher_hw_rc4_hmac_md5_init_mackey 228 }; 229 230 const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc4_hmac_md5(size_t keybits) 231 { 232 return (PROV_CIPHER_HW *)&rc4_hmac_md5_hw; 233 } 234