1/* session.c --- Data integrity/privacy protection of DIGEST-MD5.
2 * Copyright (C) 2002-2012 Simon Josefsson
3 *
4 * This file is part of GNU SASL Library.
5 *
6 * GNU SASL Library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public License
8 * as published by the Free Software Foundation; either version 2.1 of
9 * the License, or (at your option) any later version.
10 *
11 * GNU SASL Library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with GNU SASL Library; if not, write to the Free
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27/* Get specification. */
28#include "session.h"
29
30/* Get malloc, free. */
31#include <stdlib.h>
32
33/* Get memcpy, strdup, strlen. */
34#include <string.h>
35
36/* Get gc_hmac_md5. */
37#include <gc.h>
38
39#define MD5LEN 16
40#define SASL_INTEGRITY_PREFIX_LENGTH 4
41#define MAC_DATA_LEN 4
42#define MAC_HMAC_LEN 10
43#define MAC_MSG_TYPE "\x00\x01"
44#define MAC_MSG_TYPE_LEN 2
45#define MAC_SEQNUM_LEN 4
46
47int
48digest_md5_encode (const char *input, size_t input_len,
49 char **output, size_t * output_len,
50 digest_md5_qop qop,
51 unsigned long sendseqnum, char key[DIGEST_MD5_LENGTH])
52{
53 int res;
54
55 if (qop & DIGEST_MD5_QOP_AUTH_CONF)
56 {
57 return -1;
58 }
59 else if (qop & DIGEST_MD5_QOP_AUTH_INT)
60 {
61 char *seqnumin;
62 char hash[GC_MD5_DIGEST_SIZE];
63 size_t len;
64
65 seqnumin = malloc (MAC_SEQNUM_LEN + input_len);
66 if (seqnumin == NULL)
67 return -1;
68
69 seqnumin[0] = (sendseqnum >> 24) & 0xFF;
70 seqnumin[1] = (sendseqnum >> 16) & 0xFF;
71 seqnumin[2] = (sendseqnum >> 8) & 0xFF;
72 seqnumin[3] = sendseqnum & 0xFF;
73 memcpy (seqnumin + MAC_SEQNUM_LEN, input, input_len);
74
75 res = gc_hmac_md5 (key, MD5LEN,
76 seqnumin, MAC_SEQNUM_LEN + input_len, hash);
77 free (seqnumin);
78 if (res)
79 return -1;
80
81 *output_len = MAC_DATA_LEN + input_len + MAC_HMAC_LEN +
82 MAC_MSG_TYPE_LEN + MAC_SEQNUM_LEN;
83 *output = malloc (*output_len);
84 if (!*output)
85 return -1;
86
87 len = MAC_DATA_LEN;
88 memcpy (*output + len, input, input_len);
89 len += input_len;
90 memcpy (*output + len, hash, MAC_HMAC_LEN);
91 len += MAC_HMAC_LEN;
92 memcpy (*output + len, MAC_MSG_TYPE, MAC_MSG_TYPE_LEN);
93 len += MAC_MSG_TYPE_LEN;
94 (*output + len)[0] = (sendseqnum >> 24) & 0xFF;
95 (*output + len)[1] = (sendseqnum >> 16) & 0xFF;
96 (*output + len)[2] = (sendseqnum >> 8) & 0xFF;
97 (*output + len)[3] = sendseqnum & 0xFF;
98 len += MAC_SEQNUM_LEN;
99 (*output)[0] = ((len - MAC_DATA_LEN) >> 24) & 0xFF;
100 (*output)[1] = ((len - MAC_DATA_LEN) >> 16) & 0xFF;
101 (*output)[2] = ((len - MAC_DATA_LEN) >> 8) & 0xFF;
102 (*output)[3] = (len - MAC_DATA_LEN) & 0xFF;
103 }
104 else
105 {
106 *output_len = input_len;
107 *output = malloc (input_len);
108 if (!*output)
109 return -1;
110 memcpy (*output, input, input_len);
111 }
112
113 return 0;
114}
115
116#define C2I(buf) ((buf[3] & 0xFF) | \
117 ((buf[2] & 0xFF) << 8) | \
118 ((buf[1] & 0xFF) << 16) | \
119 ((buf[0] & 0xFF) << 24))
120
121int
122digest_md5_decode (const char *input, size_t input_len,
123 char **output, size_t * output_len,
124 digest_md5_qop qop,
125 unsigned long readseqnum, char key[DIGEST_MD5_LENGTH])
126{
127 if (qop & DIGEST_MD5_QOP_AUTH_CONF)
128 {
129 return -1;
130 }
131 else if (qop & DIGEST_MD5_QOP_AUTH_INT)
132 {
133 char *seqnumin;
134 char hash[GC_MD5_DIGEST_SIZE];
135 unsigned long len;
136 char tmpbuf[SASL_INTEGRITY_PREFIX_LENGTH];
137 int res;
138
139 if (input_len < SASL_INTEGRITY_PREFIX_LENGTH)
140 return -2;
141
142 len = C2I (input);
143
144 if (input_len < SASL_INTEGRITY_PREFIX_LENGTH + len)
145 return -2;
146
147 len -= MAC_HMAC_LEN + MAC_MSG_TYPE_LEN + MAC_SEQNUM_LEN;
148
149 seqnumin = malloc (SASL_INTEGRITY_PREFIX_LENGTH + len);
150 if (seqnumin == NULL)
151 return -1;
152
153 tmpbuf[0] = (readseqnum >> 24) & 0xFF;
154 tmpbuf[1] = (readseqnum >> 16) & 0xFF;
155 tmpbuf[2] = (readseqnum >> 8) & 0xFF;
156 tmpbuf[3] = readseqnum & 0xFF;
157
158 memcpy (seqnumin, tmpbuf, SASL_INTEGRITY_PREFIX_LENGTH);
159 memcpy (seqnumin + SASL_INTEGRITY_PREFIX_LENGTH,
160 input + MAC_DATA_LEN, len);
161
162 res = gc_hmac_md5 (key, MD5LEN, seqnumin, MAC_SEQNUM_LEN + len, hash);
163 free (seqnumin);
164 if (res)
165 return -1;
166
167 if (memcmp
168 (hash,
169 input + input_len - MAC_SEQNUM_LEN - MAC_MSG_TYPE_LEN -
170 MAC_HMAC_LEN, MAC_HMAC_LEN) == 0
171 && memcmp (MAC_MSG_TYPE,
172 input + input_len - MAC_SEQNUM_LEN - MAC_MSG_TYPE_LEN,
173 MAC_MSG_TYPE_LEN) == 0
174 && memcmp (tmpbuf, input + input_len - MAC_SEQNUM_LEN,
175 MAC_SEQNUM_LEN) == 0)
176 {
177 *output_len = len;
178 *output = malloc (*output_len);
179 if (!*output)
180 return -1;
181 memcpy (*output, input + MAC_DATA_LEN, len);
182 }
183 else
184 return -1;
185 }
186 else
187 {
188 *output_len = input_len;
189 *output = malloc (input_len);
190 if (!*output)
191 return -1;
192 memcpy (*output, input, input_len);
193 }
194
195 return 0;
196}
197