/** * @file * SNMPv3 crypto/auth functions implemented for ARM mbedtls. */ /* * Copyright (c) 2016 Elias Oenal and Dirk Ziegelmeier. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * Author: Elias Oenal * Dirk Ziegelmeier */ #include "lwip/apps/snmpv3.h" #include "snmpv3_priv.h" #include "lwip/arch.h" #include "snmp_msg.h" #include "lwip/sys.h" #include #if LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS #include "mbedtls/md.h" #include "mbedtls/cipher.h" #include "mbedtls/md5.h" #include "mbedtls/sha1.h" err_t snmpv3_auth(struct snmp_pbuf_stream *stream, u16_t length, const u8_t *key, snmpv3_auth_algo_t algo, u8_t *hmac_out) { u32_t i; u8_t key_len; const mbedtls_md_info_t *md_info; mbedtls_md_context_t ctx; struct snmp_pbuf_stream read_stream; snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length); if (algo == SNMP_V3_AUTH_ALGO_MD5) { md_info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5); key_len = SNMP_V3_MD5_LEN; } else if (algo == SNMP_V3_AUTH_ALGO_SHA) { md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1); key_len = SNMP_V3_SHA_LEN; } else { return ERR_ARG; } mbedtls_md_init(&ctx); if (mbedtls_md_setup(&ctx, md_info, 1) != 0) { return ERR_ARG; } if (mbedtls_md_hmac_starts(&ctx, key, key_len) != 0) { goto free_md; } for (i = 0; i < length; i++) { u8_t byte; if (snmp_pbuf_stream_read(&read_stream, &byte)) { goto free_md; } if (mbedtls_md_hmac_update(&ctx, &byte, 1) != 0) { goto free_md; } } if (mbedtls_md_hmac_finish(&ctx, hmac_out) != 0) { goto free_md; } mbedtls_md_free(&ctx); return ERR_OK; free_md: mbedtls_md_free(&ctx); return ERR_ARG; } #if LWIP_SNMP_V3_CRYPTO err_t snmpv3_crypt(struct snmp_pbuf_stream *stream, u16_t length, const u8_t *key, const u8_t *priv_param, const u32_t engine_boots, const u32_t engine_time, snmpv3_priv_algo_t algo, snmpv3_priv_mode_t mode) { size_t i; mbedtls_cipher_context_t ctx; const mbedtls_cipher_info_t *cipher_info; struct snmp_pbuf_stream read_stream; struct snmp_pbuf_stream write_stream; snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length); snmp_pbuf_stream_init(&write_stream, stream->pbuf, stream->offset, stream->length); mbedtls_cipher_init(&ctx); if (algo == SNMP_V3_PRIV_ALGO_DES) { u8_t iv_local[8]; u8_t out_bytes[8]; size_t out_len; /* RFC 3414 mandates padding for DES */ if ((length & 0x07) != 0) { return ERR_ARG; } cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_DES_CBC); if (mbedtls_cipher_setup(&ctx, cipher_info) != 0) { return ERR_ARG; } if (mbedtls_cipher_set_padding_mode(&ctx, MBEDTLS_PADDING_NONE) != 0) { return ERR_ARG; } if (mbedtls_cipher_setkey(&ctx, key, 8 * 8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT) ? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) { goto error; } /* Prepare IV */ for (i = 0; i < LWIP_ARRAYSIZE(iv_local); i++) { iv_local[i] = priv_param[i] ^ key[i + 8]; } if (mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) { goto error; } for (i = 0; i < length; i += 8) { size_t j; u8_t in_bytes[8]; out_len = LWIP_ARRAYSIZE(out_bytes) ; for (j = 0; j < LWIP_ARRAYSIZE(in_bytes); j++) { if (snmp_pbuf_stream_read(&read_stream, &in_bytes[j]) != ERR_OK) { goto error; } } if (mbedtls_cipher_update(&ctx, in_bytes, LWIP_ARRAYSIZE(in_bytes), out_bytes, &out_len) != 0) { goto error; } if (snmp_pbuf_stream_writebuf(&write_stream, out_bytes, (u16_t)out_len) != ERR_OK) { goto error; } } out_len = LWIP_ARRAYSIZE(out_bytes); if (mbedtls_cipher_finish(&ctx, out_bytes, &out_len) != 0) { goto error; } if (snmp_pbuf_stream_writebuf(&write_stream, out_bytes, (u16_t)out_len) != ERR_OK) { goto error; } } else if (algo == SNMP_V3_PRIV_ALGO_AES) { u8_t iv_local[16]; cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_CFB128); if (mbedtls_cipher_setup(&ctx, cipher_info) != 0) { return ERR_ARG; } if (mbedtls_cipher_setkey(&ctx, key, 16 * 8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT) ? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) { goto error; } /* * IV is the big endian concatenation of boots, * uptime and priv param - see RFC3826. */ iv_local[0 + 0] = (engine_boots >> 24) & 0xFF; iv_local[0 + 1] = (engine_boots >> 16) & 0xFF; iv_local[0 + 2] = (engine_boots >> 8) & 0xFF; iv_local[0 + 3] = (engine_boots >> 0) & 0xFF; iv_local[4 + 0] = (engine_time >> 24) & 0xFF; iv_local[4 + 1] = (engine_time >> 16) & 0xFF; iv_local[4 + 2] = (engine_time >> 8) & 0xFF; iv_local[4 + 3] = (engine_time >> 0) & 0xFF; SMEMCPY(iv_local + 8, priv_param, 8); if (mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) { goto error; } for (i = 0; i < length; i++) { u8_t in_byte; u8_t out_byte; size_t out_len = sizeof(out_byte); if (snmp_pbuf_stream_read(&read_stream, &in_byte) != ERR_OK) { goto error; } if (mbedtls_cipher_update(&ctx, &in_byte, sizeof(in_byte), &out_byte, &out_len) != 0) { goto error; } if (snmp_pbuf_stream_write(&write_stream, out_byte) != ERR_OK) { goto error; } } } else { return ERR_ARG; } mbedtls_cipher_free(&ctx); return ERR_OK; error: mbedtls_cipher_free(&ctx); return ERR_OK; } #endif /* LWIP_SNMP_V3_CRYPTO */ /* A.2.1. Password to Key Sample Code for MD5 */ void snmpv3_password_to_key_md5( const u8_t *password, /* IN */ size_t passwordlen, /* IN */ const u8_t *engineID, /* IN - pointer to snmpEngineID */ u8_t engineLength,/* IN - length of snmpEngineID */ u8_t *key) /* OUT - pointer to caller 16-octet buffer */ { mbedtls_md5_context MD; u8_t *cp, password_buf[64]; u32_t password_index = 0; u8_t i; u32_t count = 0; mbedtls_md5_init(&MD); /* initialize MD5 */ mbedtls_md5_starts(&MD); /**********************************************/ /* Use while loop until we've done 1 Megabyte */ /**********************************************/ while (count < 1048576) { cp = password_buf; for (i = 0; i < 64; i++) { /*************************************************/ /* Take the next octet of the password, wrapping */ /* to the beginning of the password as necessary.*/ /*************************************************/ *cp++ = password[password_index++ % passwordlen]; } mbedtls_md5_update(&MD, password_buf, 64); count += 64; } mbedtls_md5_finish(&MD, key); /* tell MD5 we're done */ /*****************************************************/ /* Now localize the key with the engineID and pass */ /* through MD5 to produce final key */ /* May want to ensure that engineLength <= 32, */ /* otherwise need to use a buffer larger than 64 */ /*****************************************************/ SMEMCPY(password_buf, key, 16); MEMCPY(password_buf + 16, engineID, engineLength); SMEMCPY(password_buf + 16 + engineLength, key, 16); mbedtls_md5_starts(&MD); mbedtls_md5_update(&MD, password_buf, 32 + engineLength); mbedtls_md5_finish(&MD, key); mbedtls_md5_free(&MD); return; } /* A.2.2. Password to Key Sample Code for SHA */ void snmpv3_password_to_key_sha( const u8_t *password, /* IN */ size_t passwordlen, /* IN */ const u8_t *engineID, /* IN - pointer to snmpEngineID */ u8_t engineLength,/* IN - length of snmpEngineID */ u8_t *key) /* OUT - pointer to caller 20-octet buffer */ { mbedtls_sha1_context SH; u8_t *cp, password_buf[72]; u32_t password_index = 0; u8_t i; u32_t count = 0; mbedtls_sha1_init(&SH); /* initialize SHA */ mbedtls_sha1_starts(&SH); /**********************************************/ /* Use while loop until we've done 1 Megabyte */ /**********************************************/ while (count < 1048576) { cp = password_buf; for (i = 0; i < 64; i++) { /*************************************************/ /* Take the next octet of the password, wrapping */ /* to the beginning of the password as necessary.*/ /*************************************************/ *cp++ = password[password_index++ % passwordlen]; } mbedtls_sha1_update(&SH, password_buf, 64); count += 64; } mbedtls_sha1_finish(&SH, key); /* tell SHA we're done */ /*****************************************************/ /* Now localize the key with the engineID and pass */ /* through SHA to produce final key */ /* May want to ensure that engineLength <= 32, */ /* otherwise need to use a buffer larger than 72 */ /*****************************************************/ SMEMCPY(password_buf, key, 20); MEMCPY(password_buf + 20, engineID, engineLength); SMEMCPY(password_buf + 20 + engineLength, key, 20); mbedtls_sha1_starts(&SH); mbedtls_sha1_update(&SH, password_buf, 40 + engineLength); mbedtls_sha1_finish(&SH, key); mbedtls_sha1_free(&SH); return; } #endif /* LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS */