• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
snmpv3_auth(struct snmp_pbuf_stream * stream,u16_t length,const u8_t * key,snmpv3_auth_algo_t algo,u8_t * hmac_out)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
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)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
snmpv3_password_to_key_md5(const u8_t * password,size_t passwordlen,const u8_t * engineID,u8_t engineLength,u8_t * key)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
snmpv3_password_to_key_sha(const u8_t * password,size_t passwordlen,const u8_t * engineID,u8_t engineLength,u8_t * key)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