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 <[email protected]> 33 * Dirk Ziegelmeier <[email protected]> 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 if (snmp_pbuf_stream_read(&read_stream, &in_bytes[j]) != ERR_OK) { 158 goto error; 159 } 160 } 161 162 if (mbedtls_cipher_update(&ctx, in_bytes, LWIP_ARRAYSIZE(in_bytes), out_bytes, &out_len) != 0) { 163 goto error; 164 } 165 166 if (snmp_pbuf_stream_writebuf(&write_stream, out_bytes, (u16_t)out_len) != ERR_OK) { 167 goto error; 168 } 169 } 170 171 out_len = LWIP_ARRAYSIZE(out_bytes); 172 if (mbedtls_cipher_finish(&ctx, out_bytes, &out_len) != 0) { 173 goto error; 174 } 175 176 if (snmp_pbuf_stream_writebuf(&write_stream, out_bytes, (u16_t)out_len) != ERR_OK) { 177 goto error; 178 } 179 } else if (algo == SNMP_V3_PRIV_ALGO_AES) { 180 u8_t iv_local[16]; 181 182 cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_CFB128); 183 if (mbedtls_cipher_setup(&ctx, cipher_info) != 0) { 184 return ERR_ARG; 185 } 186 if (mbedtls_cipher_setkey(&ctx, key, 16 * 8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT) ? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) { 187 goto error; 188 } 189 190 /* 191 * IV is the big endian concatenation of boots, 192 * uptime and priv param - see RFC3826. 193 */ 194 iv_local[0 + 0] = (engine_boots >> 24) & 0xFF; 195 iv_local[0 + 1] = (engine_boots >> 16) & 0xFF; 196 iv_local[0 + 2] = (engine_boots >> 8) & 0xFF; 197 iv_local[0 + 3] = (engine_boots >> 0) & 0xFF; 198 iv_local[4 + 0] = (engine_time >> 24) & 0xFF; 199 iv_local[4 + 1] = (engine_time >> 16) & 0xFF; 200 iv_local[4 + 2] = (engine_time >> 8) & 0xFF; 201 iv_local[4 + 3] = (engine_time >> 0) & 0xFF; 202 SMEMCPY(iv_local + 8, priv_param, 8); 203 if (mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) { 204 goto error; 205 } 206 207 for (i = 0; i < length; i++) { 208 u8_t in_byte; 209 u8_t out_byte; 210 size_t out_len = sizeof(out_byte); 211 212 if (snmp_pbuf_stream_read(&read_stream, &in_byte) != ERR_OK) { 213 goto error; 214 } 215 if (mbedtls_cipher_update(&ctx, &in_byte, sizeof(in_byte), &out_byte, &out_len) != 0) { 216 goto error; 217 } 218 if (snmp_pbuf_stream_write(&write_stream, out_byte) != ERR_OK) { 219 goto error; 220 } 221 } 222 } else { 223 return ERR_ARG; 224 } 225 226 mbedtls_cipher_free(&ctx); 227 return ERR_OK; 228 229 error: 230 mbedtls_cipher_free(&ctx); 231 return ERR_OK; 232 } 233 234 #endif /* LWIP_SNMP_V3_CRYPTO */ 235 236 /* A.2.1. Password to Key Sample Code for MD5 */ 237 void 238 snmpv3_password_to_key_md5( 239 const u8_t *password, /* IN */ 240 size_t passwordlen, /* IN */ 241 const u8_t *engineID, /* IN - pointer to snmpEngineID */ 242 u8_t engineLength,/* IN - length of snmpEngineID */ 243 u8_t *key) /* OUT - pointer to caller 16-octet buffer */ 244 { 245 mbedtls_md5_context MD; 246 u8_t *cp, password_buf[64]; 247 u32_t password_index = 0; 248 u8_t i; 249 u32_t count = 0; 250 251 mbedtls_md5_init(&MD); /* initialize MD5 */ 252 mbedtls_md5_starts(&MD); 253 254 /**********************************************/ 255 /* Use while loop until we've done 1 Megabyte */ 256 /**********************************************/ 257 while (count < 1048576) { 258 cp = password_buf; 259 for (i = 0; i < 64; i++) { 260 /*************************************************/ 261 /* Take the next octet of the password, wrapping */ 262 /* to the beginning of the password as necessary.*/ 263 /*************************************************/ 264 *cp++ = password[password_index++ % passwordlen]; 265 } 266 mbedtls_md5_update(&MD, password_buf, 64); 267 count += 64; 268 } 269 mbedtls_md5_finish(&MD, key); /* tell MD5 we're done */ 270 271 /*****************************************************/ 272 /* Now localize the key with the engineID and pass */ 273 /* through MD5 to produce final key */ 274 /* May want to ensure that engineLength <= 32, */ 275 /* otherwise need to use a buffer larger than 64 */ 276 /*****************************************************/ 277 SMEMCPY(password_buf, key, 16); 278 MEMCPY(password_buf + 16, engineID, engineLength); 279 SMEMCPY(password_buf + 16 + engineLength, key, 16); 280 281 mbedtls_md5_starts(&MD); 282 mbedtls_md5_update(&MD, password_buf, 32 + engineLength); 283 mbedtls_md5_finish(&MD, key); 284 285 mbedtls_md5_free(&MD); 286 return; 287 } 288 289 /* A.2.2. Password to Key Sample Code for SHA */ 290 void 291 snmpv3_password_to_key_sha( 292 const u8_t *password, /* IN */ 293 size_t passwordlen, /* IN */ 294 const u8_t *engineID, /* IN - pointer to snmpEngineID */ 295 u8_t engineLength,/* IN - length of snmpEngineID */ 296 u8_t *key) /* OUT - pointer to caller 20-octet buffer */ 297 { 298 mbedtls_sha1_context SH; 299 u8_t *cp, password_buf[72]; 300 u32_t password_index = 0; 301 u8_t i; 302 u32_t count = 0; 303 304 mbedtls_sha1_init(&SH); /* initialize SHA */ 305 mbedtls_sha1_starts(&SH); 306 307 /**********************************************/ 308 /* Use while loop until we've done 1 Megabyte */ 309 /**********************************************/ 310 while (count < 1048576) { 311 cp = password_buf; 312 for (i = 0; i < 64; i++) { 313 /*************************************************/ 314 /* Take the next octet of the password, wrapping */ 315 /* to the beginning of the password as necessary.*/ 316 /*************************************************/ 317 *cp++ = password[password_index++ % passwordlen]; 318 } 319 mbedtls_sha1_update(&SH, password_buf, 64); 320 count += 64; 321 } 322 mbedtls_sha1_finish(&SH, key); /* tell SHA we're done */ 323 324 /*****************************************************/ 325 /* Now localize the key with the engineID and pass */ 326 /* through SHA to produce final key */ 327 /* May want to ensure that engineLength <= 32, */ 328 /* otherwise need to use a buffer larger than 72 */ 329 /*****************************************************/ 330 SMEMCPY(password_buf, key, 20); 331 MEMCPY(password_buf + 20, engineID, engineLength); 332 SMEMCPY(password_buf + 20 + engineLength, key, 20); 333 334 mbedtls_sha1_starts(&SH); 335 mbedtls_sha1_update(&SH, password_buf, 40 + engineLength); 336 mbedtls_sha1_finish(&SH, key); 337 338 mbedtls_sha1_free(&SH); 339 return; 340 } 341 342 #endif /* LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS */ 343