1 /** 2 * @file 3 * SNMPv3 crypto/auth functions implemented for ARM mbedtls. 4 */ 5 6 /* 7 * Copyright (c) 2016 Elias Oenal and Dirk Ziegelmeier. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without modification, 11 * are permitted provided that the following conditions are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright notice, 14 * this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright notice, 16 * this list of conditions and the following disclaimer in the documentation 17 * and/or other materials provided with the distribution. 18 * 3. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 22 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 24 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 26 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 29 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 30 * OF SUCH DAMAGE. 31 * 32 * Author: Elias Oenal <lwip@eliasoenal.com> 33 * Dirk Ziegelmeier <dirk@ziegelmeier.net> 34 */ 35 36 #include "lwip/apps/snmpv3.h" 37 #include "snmpv3_priv.h" 38 #include "lwip/arch.h" 39 #include "snmp_msg.h" 40 #include "lwip/sys.h" 41 #include <string.h> 42 43 #if LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS 44 45 #include "mbedtls/md.h" 46 #include "mbedtls/cipher.h" 47 48 #include "mbedtls/md5.h" 49 #include "mbedtls/sha1.h" 50 51 err_t 52 snmpv3_auth(struct snmp_pbuf_stream* stream, u16_t length, 53 const u8_t* key, snmpv3_auth_algo_t algo, u8_t* hmac_out) 54 { 55 u32_t i; 56 u8_t key_len; 57 const mbedtls_md_info_t *md_info; 58 mbedtls_md_context_t ctx; 59 struct snmp_pbuf_stream read_stream; 60 snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length); 61 62 if (algo == SNMP_V3_AUTH_ALGO_MD5) { 63 md_info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5); 64 key_len = SNMP_V3_MD5_LEN; 65 } else if (algo == SNMP_V3_AUTH_ALGO_SHA) { 66 md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1); 67 key_len = SNMP_V3_SHA_LEN; 68 } else { 69 return ERR_ARG; 70 } 71 72 mbedtls_md_init(&ctx); 73 if(mbedtls_md_setup(&ctx, md_info, 1) != 0) { 74 return ERR_ARG; 75 } 76 77 if (mbedtls_md_hmac_starts(&ctx, key, key_len) != 0) { 78 goto free_md; 79 } 80 81 for (i = 0; i < length; i++) { 82 u8_t byte; 83 84 if (snmp_pbuf_stream_read(&read_stream, &byte)) { 85 goto free_md; 86 } 87 88 if (mbedtls_md_hmac_update(&ctx, &byte, 1) != 0) { 89 goto free_md; 90 } 91 } 92 93 if (mbedtls_md_hmac_finish(&ctx, hmac_out) != 0) { 94 goto free_md; 95 } 96 97 mbedtls_md_free(&ctx); 98 return ERR_OK; 99 100 free_md: 101 mbedtls_md_free(&ctx); 102 return ERR_ARG; 103 } 104 105 #if LWIP_SNMP_V3_CRYPTO 106 107 err_t 108 snmpv3_crypt(struct snmp_pbuf_stream* stream, u16_t length, 109 const u8_t* key, const u8_t* priv_param, const u32_t engine_boots, 110 const u32_t engine_time, snmpv3_priv_algo_t algo, snmpv3_priv_mode_t mode) 111 { 112 size_t i; 113 mbedtls_cipher_context_t ctx; 114 const mbedtls_cipher_info_t *cipher_info; 115 116 struct snmp_pbuf_stream read_stream; 117 struct snmp_pbuf_stream write_stream; 118 snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length); 119 snmp_pbuf_stream_init(&write_stream, stream->pbuf, stream->offset, stream->length); 120 mbedtls_cipher_init(&ctx); 121 122 if (algo == SNMP_V3_PRIV_ALGO_DES) { 123 u8_t iv_local[8]; 124 u8_t out_bytes[8]; 125 size_t out_len; 126 127 /* RFC 3414 mandates padding for DES */ 128 if ((length & 0x07) != 0) { 129 return ERR_ARG; 130 } 131 132 cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_DES_CBC); 133 if(mbedtls_cipher_setup(&ctx, cipher_info) != 0) { 134 return ERR_ARG; 135 } 136 if(mbedtls_cipher_set_padding_mode(&ctx, MBEDTLS_PADDING_NONE) != 0) { 137 return ERR_ARG; 138 } 139 if(mbedtls_cipher_setkey(&ctx, key, 8*8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT)? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) { 140 goto error; 141 } 142 143 /* Prepare IV */ 144 for (i = 0; i < LWIP_ARRAYSIZE(iv_local); i++) { 145 iv_local[i] = priv_param[i] ^ key[i + 8]; 146 } 147 if(mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) { 148 goto error; 149 } 150 151 for (i = 0; i < length; i += 8) { 152 size_t j; 153 u8_t in_bytes[8]; 154 out_len = LWIP_ARRAYSIZE(out_bytes) ; 155 156 for (j = 0; j < LWIP_ARRAYSIZE(in_bytes); j++) { 157 snmp_pbuf_stream_read(&read_stream, &in_bytes[j]); 158 } 159 160 if(mbedtls_cipher_update(&ctx, in_bytes, LWIP_ARRAYSIZE(in_bytes), out_bytes, &out_len) != 0) { 161 goto error; 162 } 163 164 snmp_pbuf_stream_writebuf(&write_stream, out_bytes, (u16_t)out_len); 165 } 166 167 out_len = LWIP_ARRAYSIZE(out_bytes); 168 if(mbedtls_cipher_finish(&ctx, out_bytes, &out_len) != 0) { 169 goto error; 170 } 171 snmp_pbuf_stream_writebuf(&write_stream, out_bytes, (u16_t)out_len); 172 } else if (algo == SNMP_V3_PRIV_ALGO_AES) { 173 u8_t iv_local[16]; 174 175 cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_CFB128); 176 if(mbedtls_cipher_setup(&ctx, cipher_info) != 0) { 177 return ERR_ARG; 178 } 179 if(mbedtls_cipher_setkey(&ctx, key, 16*8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT)? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) { 180 goto error; 181 } 182 183 /* 184 * IV is the big endian concatenation of boots, 185 * uptime and priv param - see RFC3826. 186 */ 187 iv_local[0 + 0] = (engine_boots >> 24) & 0xFF; 188 iv_local[0 + 1] = (engine_boots >> 16) & 0xFF; 189 iv_local[0 + 2] = (engine_boots >> 8) & 0xFF; 190 iv_local[0 + 3] = (engine_boots >> 0) & 0xFF; 191 iv_local[4 + 0] = (engine_time >> 24) & 0xFF; 192 iv_local[4 + 1] = (engine_time >> 16) & 0xFF; 193 iv_local[4 + 2] = (engine_time >> 8) & 0xFF; 194 iv_local[4 + 3] = (engine_time >> 0) & 0xFF; 195 SMEMCPY(iv_local + 8, priv_param, 8); 196 if(mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) { 197 goto error; 198 } 199 200 for (i = 0; i < length; i++) { 201 u8_t in_byte; 202 u8_t out_byte; 203 size_t out_len = sizeof(out_byte); 204 205 snmp_pbuf_stream_read(&read_stream, &in_byte); 206 if(mbedtls_cipher_update(&ctx, &in_byte, sizeof(in_byte), &out_byte, &out_len) != 0) { 207 goto error; 208 } 209 snmp_pbuf_stream_write(&write_stream, out_byte); 210 } 211 } else { 212 return ERR_ARG; 213 } 214 215 mbedtls_cipher_free(&ctx); 216 return ERR_OK; 217 218 error: 219 mbedtls_cipher_free(&ctx); 220 return ERR_OK; 221 } 222 223 #endif /* LWIP_SNMP_V3_CRYPTO */ 224 225 /* A.2.1. Password to Key Sample Code for MD5 */ 226 void 227 snmpv3_password_to_key_md5( 228 const u8_t *password, /* IN */ 229 size_t passwordlen, /* IN */ 230 const u8_t *engineID, /* IN - pointer to snmpEngineID */ 231 u8_t engineLength,/* IN - length of snmpEngineID */ 232 u8_t *key) /* OUT - pointer to caller 16-octet buffer */ 233 { 234 mbedtls_md5_context MD; 235 u8_t *cp, password_buf[64]; 236 u32_t password_index = 0; 237 u8_t i; 238 u32_t count = 0; 239 240 mbedtls_md5_init(&MD); /* initialize MD5 */ 241 mbedtls_md5_starts(&MD); 242 243 /**********************************************/ 244 /* Use while loop until we've done 1 Megabyte */ 245 /**********************************************/ 246 while (count < 1048576) { 247 cp = password_buf; 248 for (i = 0; i < 64; i++) { 249 /*************************************************/ 250 /* Take the next octet of the password, wrapping */ 251 /* to the beginning of the password as necessary.*/ 252 /*************************************************/ 253 *cp++ = password[password_index++ % passwordlen]; 254 } 255 mbedtls_md5_update(&MD, password_buf, 64); 256 count += 64; 257 } 258 mbedtls_md5_finish(&MD, key); /* tell MD5 we're done */ 259 260 /*****************************************************/ 261 /* Now localize the key with the engineID and pass */ 262 /* through MD5 to produce final key */ 263 /* May want to ensure that engineLength <= 32, */ 264 /* otherwise need to use a buffer larger than 64 */ 265 /*****************************************************/ 266 SMEMCPY(password_buf, key, 16); 267 MEMCPY(password_buf + 16, engineID, engineLength); 268 SMEMCPY(password_buf + 16 + engineLength, key, 16); 269 270 mbedtls_md5_starts(&MD); 271 mbedtls_md5_update(&MD, password_buf, 32 + engineLength); 272 mbedtls_md5_finish(&MD, key); 273 274 mbedtls_md5_free(&MD); 275 return; 276 } 277 278 /* A.2.2. Password to Key Sample Code for SHA */ 279 void 280 snmpv3_password_to_key_sha( 281 const u8_t *password, /* IN */ 282 size_t passwordlen, /* IN */ 283 const u8_t *engineID, /* IN - pointer to snmpEngineID */ 284 u8_t engineLength,/* IN - length of snmpEngineID */ 285 u8_t *key) /* OUT - pointer to caller 20-octet buffer */ 286 { 287 mbedtls_sha1_context SH; 288 u8_t *cp, password_buf[72]; 289 u32_t password_index = 0; 290 u8_t i; 291 u32_t count = 0; 292 293 mbedtls_sha1_init(&SH); /* initialize SHA */ 294 mbedtls_sha1_starts(&SH); 295 296 /**********************************************/ 297 /* Use while loop until we've done 1 Megabyte */ 298 /**********************************************/ 299 while (count < 1048576) { 300 cp = password_buf; 301 for (i = 0; i < 64; i++) { 302 /*************************************************/ 303 /* Take the next octet of the password, wrapping */ 304 /* to the beginning of the password as necessary.*/ 305 /*************************************************/ 306 *cp++ = password[password_index++ % passwordlen]; 307 } 308 mbedtls_sha1_update(&SH, password_buf, 64); 309 count += 64; 310 } 311 mbedtls_sha1_finish(&SH, key); /* tell SHA we're done */ 312 313 /*****************************************************/ 314 /* Now localize the key with the engineID and pass */ 315 /* through SHA to produce final key */ 316 /* May want to ensure that engineLength <= 32, */ 317 /* otherwise need to use a buffer larger than 72 */ 318 /*****************************************************/ 319 SMEMCPY(password_buf, key, 20); 320 MEMCPY(password_buf + 20, engineID, engineLength); 321 SMEMCPY(password_buf + 20 + engineLength, key, 20); 322 323 mbedtls_sha1_starts(&SH); 324 mbedtls_sha1_update(&SH, password_buf, 40 + engineLength); 325 mbedtls_sha1_finish(&SH, key); 326 327 mbedtls_sha1_free(&SH); 328 return; 329 } 330 331 #endif /* LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS */ 332