/src/FreeRDP/libfreerdp/core/security.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * FreeRDP: A Remote Desktop Protocol Implementation |
3 | | * RDP Security |
4 | | * |
5 | | * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com> |
6 | | * Copyright 2014 Norbert Federa <norbert.federa@thincast.com> |
7 | | * |
8 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
9 | | * you may not use this file except in compliance with the License. |
10 | | * You may obtain a copy of the License at |
11 | | * |
12 | | * http://www.apache.org/licenses/LICENSE-2.0 |
13 | | * |
14 | | * Unless required by applicable law or agreed to in writing, software |
15 | | * distributed under the License is distributed on an "AS IS" BASIS, |
16 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
17 | | * See the License for the specific language governing permissions and |
18 | | * limitations under the License. |
19 | | */ |
20 | | |
21 | | #include <freerdp/config.h> |
22 | | |
23 | | #include "settings.h" |
24 | | #include "security.h" |
25 | | |
26 | | #include <freerdp/log.h> |
27 | | #include <winpr/crypto.h> |
28 | | |
29 | | #define TAG FREERDP_TAG("core") |
30 | | |
31 | | static const BYTE A[] = { 'A' }; |
32 | | static const BYTE BB[] = { 'B', 'B' }; |
33 | | static const BYTE CCC[] = { 'C', 'C', 'C' }; |
34 | | |
35 | | /* 0x36 repeated 40 times */ |
36 | | static const BYTE pad1[40] = { "\x36\x36\x36\x36\x36\x36\x36\x36" |
37 | | "\x36\x36\x36\x36\x36\x36\x36\x36" |
38 | | "\x36\x36\x36\x36\x36\x36\x36\x36" |
39 | | "\x36\x36\x36\x36\x36\x36\x36\x36" |
40 | | "\x36\x36\x36\x36\x36\x36\x36\x36" }; |
41 | | |
42 | | /* 0x5C repeated 48 times */ |
43 | | static const BYTE pad2[48] = { "\x5C\x5C\x5C\x5C\x5C\x5C\x5C\x5C" |
44 | | "\x5C\x5C\x5C\x5C\x5C\x5C\x5C\x5C" |
45 | | "\x5C\x5C\x5C\x5C\x5C\x5C\x5C\x5C" |
46 | | "\x5C\x5C\x5C\x5C\x5C\x5C\x5C\x5C" |
47 | | "\x5C\x5C\x5C\x5C\x5C\x5C\x5C\x5C" |
48 | | "\x5C\x5C\x5C\x5C\x5C\x5C\x5C\x5C" }; |
49 | | |
50 | | static const BYTE fips_reverse_table[256] = { |
51 | | 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, |
52 | | 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, |
53 | | 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, |
54 | | 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, |
55 | | 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, |
56 | | 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, |
57 | | 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, |
58 | | 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, |
59 | | 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, |
60 | | 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, |
61 | | 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, |
62 | | 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, |
63 | | 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, |
64 | | 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, |
65 | | 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, |
66 | | 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff |
67 | | }; |
68 | | |
69 | | static const BYTE fips_oddparity_table[256] = { |
70 | | 0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x07, 0x07, 0x08, 0x08, 0x0b, 0x0b, 0x0d, 0x0d, 0x0e, 0x0e, |
71 | | 0x10, 0x10, 0x13, 0x13, 0x15, 0x15, 0x16, 0x16, 0x19, 0x19, 0x1a, 0x1a, 0x1c, 0x1c, 0x1f, 0x1f, |
72 | | 0x20, 0x20, 0x23, 0x23, 0x25, 0x25, 0x26, 0x26, 0x29, 0x29, 0x2a, 0x2a, 0x2c, 0x2c, 0x2f, 0x2f, |
73 | | 0x31, 0x31, 0x32, 0x32, 0x34, 0x34, 0x37, 0x37, 0x38, 0x38, 0x3b, 0x3b, 0x3d, 0x3d, 0x3e, 0x3e, |
74 | | 0x40, 0x40, 0x43, 0x43, 0x45, 0x45, 0x46, 0x46, 0x49, 0x49, 0x4a, 0x4a, 0x4c, 0x4c, 0x4f, 0x4f, |
75 | | 0x51, 0x51, 0x52, 0x52, 0x54, 0x54, 0x57, 0x57, 0x58, 0x58, 0x5b, 0x5b, 0x5d, 0x5d, 0x5e, 0x5e, |
76 | | 0x61, 0x61, 0x62, 0x62, 0x64, 0x64, 0x67, 0x67, 0x68, 0x68, 0x6b, 0x6b, 0x6d, 0x6d, 0x6e, 0x6e, |
77 | | 0x70, 0x70, 0x73, 0x73, 0x75, 0x75, 0x76, 0x76, 0x79, 0x79, 0x7a, 0x7a, 0x7c, 0x7c, 0x7f, 0x7f, |
78 | | 0x80, 0x80, 0x83, 0x83, 0x85, 0x85, 0x86, 0x86, 0x89, 0x89, 0x8a, 0x8a, 0x8c, 0x8c, 0x8f, 0x8f, |
79 | | 0x91, 0x91, 0x92, 0x92, 0x94, 0x94, 0x97, 0x97, 0x98, 0x98, 0x9b, 0x9b, 0x9d, 0x9d, 0x9e, 0x9e, |
80 | | 0xa1, 0xa1, 0xa2, 0xa2, 0xa4, 0xa4, 0xa7, 0xa7, 0xa8, 0xa8, 0xab, 0xab, 0xad, 0xad, 0xae, 0xae, |
81 | | 0xb0, 0xb0, 0xb3, 0xb3, 0xb5, 0xb5, 0xb6, 0xb6, 0xb9, 0xb9, 0xba, 0xba, 0xbc, 0xbc, 0xbf, 0xbf, |
82 | | 0xc1, 0xc1, 0xc2, 0xc2, 0xc4, 0xc4, 0xc7, 0xc7, 0xc8, 0xc8, 0xcb, 0xcb, 0xcd, 0xcd, 0xce, 0xce, |
83 | | 0xd0, 0xd0, 0xd3, 0xd3, 0xd5, 0xd5, 0xd6, 0xd6, 0xd9, 0xd9, 0xda, 0xda, 0xdc, 0xdc, 0xdf, 0xdf, |
84 | | 0xe0, 0xe0, 0xe3, 0xe3, 0xe5, 0xe5, 0xe6, 0xe6, 0xe9, 0xe9, 0xea, 0xea, 0xec, 0xec, 0xef, 0xef, |
85 | | 0xf1, 0xf1, 0xf2, 0xf2, 0xf4, 0xf4, 0xf7, 0xf7, 0xf8, 0xf8, 0xfb, 0xfb, 0xfd, 0xfd, 0xfe, 0xfe |
86 | | }; |
87 | | |
88 | | static BOOL security_salted_hash(const BYTE* salt, size_t salt_len, const BYTE* input, |
89 | | size_t length, const BYTE* salt1, size_t salt1_len, |
90 | | const BYTE* salt2, size_t salt2_len, BYTE* output, size_t out_len) |
91 | 0 | { |
92 | 0 | WINPR_DIGEST_CTX* sha1 = NULL; |
93 | 0 | WINPR_DIGEST_CTX* md5 = NULL; |
94 | 0 | BYTE sha1_digest[WINPR_SHA1_DIGEST_LENGTH] = { 0 }; |
95 | 0 | BOOL result = FALSE; |
96 | | |
97 | | /* SaltedHash(Salt, Input, Salt1, Salt2) = MD5(S + SHA1(Input + Salt + Salt1 + Salt2)) */ |
98 | 0 | WINPR_ASSERT(out_len >= WINPR_MD5_DIGEST_LENGTH); |
99 | | |
100 | | /* SHA1_Digest = SHA1(Input + Salt + Salt1 + Salt2) */ |
101 | 0 | if (!(sha1 = winpr_Digest_New())) |
102 | 0 | goto out; |
103 | | |
104 | 0 | if (!winpr_Digest_Init(sha1, WINPR_MD_SHA1)) |
105 | 0 | goto out; |
106 | | |
107 | 0 | if (!winpr_Digest_Update(sha1, input, length)) /* Input */ |
108 | 0 | goto out; |
109 | | |
110 | 0 | WINPR_ASSERT(salt_len == 48); |
111 | 0 | if (!winpr_Digest_Update(sha1, salt, salt_len)) /* Salt (48 bytes) */ |
112 | 0 | goto out; |
113 | | |
114 | 0 | WINPR_ASSERT(salt1_len == 32); |
115 | 0 | if (!winpr_Digest_Update(sha1, salt1, salt1_len)) /* Salt1 (32 bytes) */ |
116 | 0 | goto out; |
117 | | |
118 | 0 | WINPR_ASSERT(salt2_len == 32); |
119 | 0 | if (!winpr_Digest_Update(sha1, salt2, salt2_len)) /* Salt2 (32 bytes) */ |
120 | 0 | goto out; |
121 | | |
122 | 0 | if (!winpr_Digest_Final(sha1, sha1_digest, sizeof(sha1_digest))) |
123 | 0 | goto out; |
124 | | |
125 | | /* SaltedHash(Salt, Input, Salt1, Salt2) = MD5(S + SHA1_Digest) */ |
126 | 0 | if (!(md5 = winpr_Digest_New())) |
127 | 0 | goto out; |
128 | | |
129 | | /* Allow FIPS override for use of MD5 here, this is used for creating hashes of the |
130 | | * premaster_secret and master_secret */ |
131 | | /* used for RDP licensing as described in MS-RDPELE. This is for RDP licensing packets */ |
132 | | /* which will already be encrypted under FIPS, so the use of MD5 here is not for sensitive data |
133 | | * protection. */ |
134 | 0 | if (!winpr_Digest_Init_Allow_FIPS(md5, WINPR_MD_MD5)) |
135 | 0 | goto out; |
136 | | |
137 | 0 | if (!winpr_Digest_Update(md5, salt, 48)) /* Salt (48 bytes) */ |
138 | 0 | goto out; |
139 | | |
140 | 0 | if (!winpr_Digest_Update(md5, sha1_digest, sizeof(sha1_digest))) /* SHA1_Digest */ |
141 | 0 | goto out; |
142 | | |
143 | 0 | if (!winpr_Digest_Final(md5, output, out_len)) |
144 | 0 | goto out; |
145 | | |
146 | 0 | result = TRUE; |
147 | 0 | out: |
148 | 0 | winpr_Digest_Free(sha1); |
149 | 0 | winpr_Digest_Free(md5); |
150 | 0 | return result; |
151 | 0 | } |
152 | | |
153 | | static BOOL security_premaster_hash(const BYTE* input, size_t length, const BYTE* premaster_secret, |
154 | | size_t pre_len, const BYTE* client_random, size_t client_len, |
155 | | const BYTE* server_random, size_t server_len, BYTE* output, |
156 | | size_t out_len) |
157 | 0 | { |
158 | | /* PremasterHash(Input) = SaltedHash(PremasterSecret, Input, ClientRandom, ServerRandom) */ |
159 | 0 | return security_salted_hash(premaster_secret, pre_len, input, length, client_random, client_len, |
160 | 0 | server_random, server_len, output, out_len); |
161 | 0 | } |
162 | | |
163 | | BOOL security_master_secret(const BYTE* premaster_secret, size_t pre_len, const BYTE* client_random, |
164 | | size_t client_len, const BYTE* server_random, size_t server_len, |
165 | | BYTE* output, size_t out_len) |
166 | 0 | { |
167 | | /* MasterSecret = PremasterHash('A') + PremasterHash('BB') + PremasterHash('CCC') */ |
168 | 0 | WINPR_ASSERT(out_len >= 32); |
169 | 0 | return security_premaster_hash(A, sizeof(A), premaster_secret, pre_len, client_random, |
170 | 0 | client_len, server_random, server_len, &output[0], out_len) && |
171 | 0 | security_premaster_hash(BB, sizeof(BB), premaster_secret, pre_len, client_random, |
172 | 0 | client_len, server_random, server_len, &output[16], |
173 | 0 | out_len - 16) && |
174 | 0 | security_premaster_hash(CCC, sizeof(CCC), premaster_secret, pre_len, client_random, |
175 | 0 | client_len, server_random, server_len, &output[32], |
176 | 0 | out_len - 32); |
177 | 0 | } |
178 | | |
179 | | static BOOL security_master_hash(const BYTE* input, size_t length, const BYTE* master_secret, |
180 | | size_t master_len, const BYTE* client_random, size_t client_len, |
181 | | const BYTE* server_random, size_t server_len, BYTE* output, |
182 | | size_t out_len) |
183 | 0 | { |
184 | | /* MasterHash(Input) = SaltedHash(MasterSecret, Input, ServerRandom, ClientRandom) */ |
185 | 0 | return security_salted_hash(master_secret, master_len, input, length, server_random, server_len, |
186 | 0 | client_random, client_len, output, out_len); |
187 | 0 | } |
188 | | |
189 | | BOOL security_session_key_blob(const BYTE* master_secret, size_t master_len, |
190 | | const BYTE* client_random, size_t client_len, |
191 | | const BYTE* server_random, size_t server_len, BYTE* output, |
192 | | size_t out_len) |
193 | 0 | { |
194 | | /* MasterHash = MasterHash('A') + MasterHash('BB') + MasterHash('CCC') */ |
195 | 0 | WINPR_ASSERT(out_len >= 32); |
196 | 0 | return security_master_hash(A, sizeof(A), master_secret, master_len, client_random, client_len, |
197 | 0 | server_random, server_len, &output[0], 16) && |
198 | 0 | security_master_hash(BB, sizeof(BB), master_secret, master_len, client_random, |
199 | 0 | client_len, server_random, server_len, &output[16], 16) && |
200 | 0 | security_master_hash(CCC, sizeof(CCC), master_secret, master_len, client_random, |
201 | 0 | client_len, server_random, server_len, &output[32], out_len - 32); |
202 | 0 | } |
203 | | |
204 | | void security_mac_salt_key(const BYTE* session_key_blob, WINPR_ATTR_UNUSED size_t session_len, |
205 | | WINPR_ATTR_UNUSED const BYTE* client_random, |
206 | | WINPR_ATTR_UNUSED size_t client_len, |
207 | | WINPR_ATTR_UNUSED const BYTE* server_random, |
208 | | WINPR_ATTR_UNUSED size_t server_len, BYTE* output, |
209 | | WINPR_ATTR_UNUSED size_t out_len) |
210 | 0 | { |
211 | | /* MacSaltKey = First128Bits(SessionKeyBlob) */ |
212 | 0 | WINPR_ASSERT(out_len >= 16); |
213 | 0 | WINPR_ASSERT(session_len >= 16); |
214 | 0 | memcpy(output, session_key_blob, 16); |
215 | 0 | } |
216 | | |
217 | | static BOOL security_md5_16_32_32(const BYTE* in0, const BYTE* in1, const BYTE* in2, BYTE* output, |
218 | | size_t out_len) |
219 | 0 | { |
220 | 0 | WINPR_DIGEST_CTX* md5 = NULL; |
221 | 0 | BOOL result = FALSE; |
222 | |
|
223 | 0 | WINPR_ASSERT(WINPR_MD5_DIGEST_LENGTH <= out_len); |
224 | | |
225 | 0 | if (!(md5 = winpr_Digest_New())) |
226 | 0 | return FALSE; |
227 | | |
228 | 0 | if (!winpr_Digest_Init(md5, WINPR_MD_MD5)) |
229 | 0 | goto out; |
230 | | |
231 | 0 | if (!winpr_Digest_Update(md5, in0, 16)) |
232 | 0 | goto out; |
233 | | |
234 | 0 | if (!winpr_Digest_Update(md5, in1, 32)) |
235 | 0 | goto out; |
236 | | |
237 | 0 | if (!winpr_Digest_Update(md5, in2, 32)) |
238 | 0 | goto out; |
239 | | |
240 | 0 | if (!winpr_Digest_Final(md5, output, out_len)) |
241 | 0 | goto out; |
242 | | |
243 | 0 | result = TRUE; |
244 | 0 | out: |
245 | 0 | winpr_Digest_Free(md5); |
246 | 0 | return result; |
247 | 0 | } |
248 | | |
249 | | static BOOL security_md5_16_32_32_Allow_FIPS(const BYTE* in0, const BYTE* in1, const BYTE* in2, |
250 | | BYTE* output, size_t out_len) |
251 | 0 | { |
252 | 0 | WINPR_DIGEST_CTX* md5 = NULL; |
253 | 0 | BOOL result = FALSE; |
254 | |
|
255 | 0 | WINPR_ASSERT(out_len >= WINPR_MD5_DIGEST_LENGTH); |
256 | | |
257 | 0 | if (!(md5 = winpr_Digest_New())) |
258 | 0 | return FALSE; |
259 | 0 | if (!winpr_Digest_Init_Allow_FIPS(md5, WINPR_MD_MD5)) |
260 | 0 | goto out; |
261 | 0 | if (!winpr_Digest_Update(md5, in0, 16)) |
262 | 0 | goto out; |
263 | 0 | if (!winpr_Digest_Update(md5, in1, 32)) |
264 | 0 | goto out; |
265 | 0 | if (!winpr_Digest_Update(md5, in2, 32)) |
266 | 0 | goto out; |
267 | 0 | if (!winpr_Digest_Final(md5, output, out_len)) |
268 | 0 | goto out; |
269 | | |
270 | 0 | result = TRUE; |
271 | 0 | out: |
272 | 0 | winpr_Digest_Free(md5); |
273 | 0 | return result; |
274 | 0 | } |
275 | | |
276 | | BOOL security_licensing_encryption_key(const BYTE* session_key_blob, size_t session_len, |
277 | | const BYTE* client_random, size_t client_len, |
278 | | const BYTE* server_random, size_t server_len, BYTE* output, |
279 | | size_t out_len) |
280 | 0 | { |
281 | 0 | if (session_len < 16) |
282 | 0 | return FALSE; |
283 | 0 | if (client_len < 32) |
284 | 0 | return FALSE; |
285 | 0 | if (server_len < 32) |
286 | 0 | return FALSE; |
287 | | /* LicensingEncryptionKey = MD5(Second128Bits(SessionKeyBlob) + ClientRandom + ServerRandom)) |
288 | | * Allow FIPS use of MD5 here, this is just used for creating the licensing encryption key as |
289 | | * described in MS-RDPELE. This is for RDP licensing packets which will already be encrypted |
290 | | * under FIPS, so the use of MD5 here is not for sensitive data protection. */ |
291 | 0 | return security_md5_16_32_32_Allow_FIPS(&session_key_blob[16], client_random, server_random, |
292 | 0 | output, out_len); |
293 | 0 | } |
294 | | |
295 | | static void security_UINT32_le(BYTE* output, WINPR_ATTR_UNUSED size_t out_len, UINT32 value) |
296 | 0 | { |
297 | 0 | WINPR_ASSERT(output); |
298 | 0 | WINPR_ASSERT(out_len >= 4); |
299 | 0 | output[0] = (value)&0xFF; |
300 | 0 | output[1] = (value >> 8) & 0xFF; |
301 | 0 | output[2] = (value >> 16) & 0xFF; |
302 | 0 | output[3] = (value >> 24) & 0xFF; |
303 | 0 | } |
304 | | |
305 | | BOOL security_mac_data(const BYTE* mac_salt_key, size_t mac_salt_key_length, const BYTE* data, |
306 | | size_t length, BYTE* output, size_t output_length) |
307 | 0 | { |
308 | 0 | WINPR_DIGEST_CTX* sha1 = NULL; |
309 | 0 | WINPR_DIGEST_CTX* md5 = NULL; |
310 | 0 | BYTE length_le[4] = { 0 }; |
311 | 0 | BYTE sha1_digest[WINPR_SHA1_DIGEST_LENGTH] = { 0 }; |
312 | 0 | BOOL result = FALSE; |
313 | |
|
314 | 0 | WINPR_ASSERT(length <= UINT32_MAX); |
315 | 0 | WINPR_ASSERT(mac_salt_key_length == WINPR_MD5_DIGEST_LENGTH); |
316 | 0 | WINPR_ASSERT(output_length == WINPR_MD5_DIGEST_LENGTH); |
317 | | |
318 | | /* MacData = MD5(MacSaltKey + pad2 + SHA1(MacSaltKey + pad1 + length + data)) */ |
319 | 0 | security_UINT32_le(length_le, sizeof(length_le), |
320 | 0 | (UINT32)length); /* length must be little-endian */ |
321 | | |
322 | | /* SHA1_Digest = SHA1(MacSaltKey + pad1 + length + data) */ |
323 | 0 | if (!(sha1 = winpr_Digest_New())) |
324 | 0 | goto out; |
325 | | |
326 | 0 | if (!winpr_Digest_Init(sha1, WINPR_MD_SHA1)) |
327 | 0 | goto out; |
328 | | |
329 | 0 | if (!winpr_Digest_Update(sha1, mac_salt_key, mac_salt_key_length)) /* MacSaltKey */ |
330 | 0 | goto out; |
331 | | |
332 | 0 | if (!winpr_Digest_Update(sha1, pad1, sizeof(pad1))) /* pad1 */ |
333 | 0 | goto out; |
334 | | |
335 | 0 | if (!winpr_Digest_Update(sha1, length_le, sizeof(length_le))) /* length */ |
336 | 0 | goto out; |
337 | | |
338 | 0 | if (!winpr_Digest_Update(sha1, data, length)) /* data */ |
339 | 0 | goto out; |
340 | | |
341 | 0 | if (!winpr_Digest_Final(sha1, sha1_digest, sizeof(sha1_digest))) |
342 | 0 | goto out; |
343 | | |
344 | | /* MacData = MD5(MacSaltKey + pad2 + SHA1_Digest) */ |
345 | 0 | if (!(md5 = winpr_Digest_New())) |
346 | 0 | goto out; |
347 | | |
348 | | /* Allow FIPS override for use of MD5 here, this is only used for creating the MACData field of |
349 | | * the */ |
350 | | /* Client Platform Challenge Response packet (from MS-RDPELE section 2.2.2.5). This is for RDP |
351 | | * licensing packets */ |
352 | | /* which will already be encrypted under FIPS, so the use of MD5 here is not for sensitive data |
353 | | * protection. */ |
354 | 0 | if (!winpr_Digest_Init_Allow_FIPS(md5, WINPR_MD_MD5)) |
355 | 0 | goto out; |
356 | | |
357 | 0 | if (!winpr_Digest_Update(md5, mac_salt_key, 16)) /* MacSaltKey */ |
358 | 0 | goto out; |
359 | | |
360 | 0 | if (!winpr_Digest_Update(md5, pad2, sizeof(pad2))) /* pad2 */ |
361 | 0 | goto out; |
362 | | |
363 | 0 | if (!winpr_Digest_Update(md5, sha1_digest, sizeof(sha1_digest))) /* SHA1_Digest */ |
364 | 0 | goto out; |
365 | | |
366 | 0 | if (!winpr_Digest_Final(md5, output, output_length)) |
367 | 0 | goto out; |
368 | | |
369 | 0 | result = TRUE; |
370 | 0 | out: |
371 | 0 | if (!result) |
372 | 0 | WLog_ERR(TAG, "failed to create security mac"); |
373 | 0 | winpr_Digest_Free(sha1); |
374 | 0 | winpr_Digest_Free(md5); |
375 | 0 | return result; |
376 | 0 | } |
377 | | |
378 | | BOOL security_mac_signature(rdpRdp* rdp, const BYTE* data, UINT32 length, BYTE* output, |
379 | | size_t out_len) |
380 | 0 | { |
381 | 0 | WINPR_DIGEST_CTX* sha1 = NULL; |
382 | 0 | WINPR_DIGEST_CTX* md5 = NULL; |
383 | 0 | BYTE length_le[4] = { 0 }; |
384 | 0 | BYTE md5_digest[WINPR_MD5_DIGEST_LENGTH] = { 0 }; |
385 | 0 | BYTE sha1_digest[WINPR_SHA1_DIGEST_LENGTH] = { 0 }; |
386 | 0 | BOOL result = FALSE; |
387 | |
|
388 | 0 | WINPR_ASSERT(rdp); |
389 | 0 | WINPR_ASSERT(data || (length == 0)); |
390 | 0 | WINPR_ASSERT(output); |
391 | 0 | WINPR_ASSERT(out_len >= 8); |
392 | 0 | if (out_len < 8) |
393 | 0 | return FALSE; |
394 | | |
395 | 0 | security_UINT32_le(length_le, sizeof(length_le), length); /* length must be little-endian */ |
396 | | |
397 | | /* SHA1_Digest = SHA1(MACKeyN + pad1 + length + data) */ |
398 | 0 | if (!(sha1 = winpr_Digest_New())) |
399 | 0 | goto out; |
400 | | |
401 | 0 | if (!winpr_Digest_Init(sha1, WINPR_MD_SHA1)) |
402 | 0 | goto out; |
403 | | |
404 | 0 | if (!winpr_Digest_Update(sha1, rdp->sign_key, rdp->rc4_key_len)) /* MacKeyN */ |
405 | 0 | goto out; |
406 | | |
407 | 0 | if (!winpr_Digest_Update(sha1, pad1, sizeof(pad1))) /* pad1 */ |
408 | 0 | goto out; |
409 | | |
410 | 0 | if (!winpr_Digest_Update(sha1, length_le, sizeof(length_le))) /* length */ |
411 | 0 | goto out; |
412 | | |
413 | 0 | if (!winpr_Digest_Update(sha1, data, length)) /* data */ |
414 | 0 | goto out; |
415 | | |
416 | 0 | if (!winpr_Digest_Final(sha1, sha1_digest, sizeof(sha1_digest))) |
417 | 0 | goto out; |
418 | | |
419 | | /* MACSignature = First64Bits(MD5(MACKeyN + pad2 + SHA1_Digest)) */ |
420 | 0 | if (!(md5 = winpr_Digest_New())) |
421 | 0 | goto out; |
422 | | |
423 | 0 | if (!winpr_Digest_Init(md5, WINPR_MD_MD5)) |
424 | 0 | goto out; |
425 | | |
426 | 0 | if (!winpr_Digest_Update(md5, rdp->sign_key, rdp->rc4_key_len)) /* MacKeyN */ |
427 | 0 | goto out; |
428 | | |
429 | 0 | if (!winpr_Digest_Update(md5, pad2, sizeof(pad2))) /* pad2 */ |
430 | 0 | goto out; |
431 | | |
432 | 0 | if (!winpr_Digest_Update(md5, sha1_digest, sizeof(sha1_digest))) /* SHA1_Digest */ |
433 | 0 | goto out; |
434 | | |
435 | 0 | if (!winpr_Digest_Final(md5, md5_digest, sizeof(md5_digest))) |
436 | 0 | goto out; |
437 | | |
438 | 0 | memcpy(output, md5_digest, 8); |
439 | 0 | result = TRUE; |
440 | 0 | out: |
441 | 0 | if (!result) |
442 | 0 | WLog_WARN(TAG, "security mac generation failed"); |
443 | 0 | winpr_Digest_Free(sha1); |
444 | 0 | winpr_Digest_Free(md5); |
445 | 0 | return result; |
446 | 0 | } |
447 | | |
448 | | BOOL security_salted_mac_signature(rdpRdp* rdp, const BYTE* data, UINT32 length, BOOL encryption, |
449 | | BYTE* output, size_t out_len) |
450 | 0 | { |
451 | 0 | WINPR_DIGEST_CTX* sha1 = NULL; |
452 | 0 | WINPR_DIGEST_CTX* md5 = NULL; |
453 | 0 | BYTE length_le[4] = { 0 }; |
454 | 0 | BYTE use_count_le[4] = { 0 }; |
455 | 0 | BYTE md5_digest[WINPR_MD5_DIGEST_LENGTH] = { 0 }; |
456 | 0 | BYTE sha1_digest[WINPR_SHA1_DIGEST_LENGTH] = { 0 }; |
457 | 0 | BOOL result = FALSE; |
458 | |
|
459 | 0 | WINPR_ASSERT(rdp); |
460 | 0 | WINPR_ASSERT(data || (length == 0)); |
461 | 0 | WINPR_ASSERT(output); |
462 | 0 | WINPR_ASSERT(out_len >= 8); |
463 | 0 | if (out_len < 8) |
464 | 0 | return FALSE; |
465 | | |
466 | 0 | security_UINT32_le(length_le, sizeof(length_le), length); /* length must be little-endian */ |
467 | |
|
468 | 0 | if (encryption) |
469 | 0 | { |
470 | 0 | security_UINT32_le(use_count_le, sizeof(use_count_le), rdp->encrypt_checksum_use_count); |
471 | 0 | } |
472 | 0 | else |
473 | 0 | { |
474 | | /* |
475 | | * We calculate checksum on plain text, so we must have already |
476 | | * decrypt it, which means decrypt_checksum_use_count is off by one. |
477 | | */ |
478 | 0 | security_UINT32_le(use_count_le, sizeof(use_count_le), |
479 | 0 | rdp->decrypt_checksum_use_count - 1u); |
480 | 0 | } |
481 | | |
482 | | /* SHA1_Digest = SHA1(MACKeyN + pad1 + length + data) */ |
483 | 0 | if (!(sha1 = winpr_Digest_New())) |
484 | 0 | goto out; |
485 | | |
486 | 0 | if (!winpr_Digest_Init(sha1, WINPR_MD_SHA1)) |
487 | 0 | goto out; |
488 | | |
489 | 0 | if (!winpr_Digest_Update(sha1, rdp->sign_key, rdp->rc4_key_len)) /* MacKeyN */ |
490 | 0 | goto out; |
491 | | |
492 | 0 | if (!winpr_Digest_Update(sha1, pad1, sizeof(pad1))) /* pad1 */ |
493 | 0 | goto out; |
494 | | |
495 | 0 | if (!winpr_Digest_Update(sha1, length_le, sizeof(length_le))) /* length */ |
496 | 0 | goto out; |
497 | | |
498 | 0 | if (!winpr_Digest_Update(sha1, data, length)) /* data */ |
499 | 0 | goto out; |
500 | | |
501 | 0 | if (!winpr_Digest_Update(sha1, use_count_le, sizeof(use_count_le))) /* encryptionCount */ |
502 | 0 | goto out; |
503 | | |
504 | 0 | if (!winpr_Digest_Final(sha1, sha1_digest, sizeof(sha1_digest))) |
505 | 0 | goto out; |
506 | | |
507 | | /* MACSignature = First64Bits(MD5(MACKeyN + pad2 + SHA1_Digest)) */ |
508 | 0 | if (!(md5 = winpr_Digest_New())) |
509 | 0 | goto out; |
510 | | |
511 | 0 | if (!winpr_Digest_Init(md5, WINPR_MD_MD5)) |
512 | 0 | goto out; |
513 | | |
514 | 0 | if (!winpr_Digest_Update(md5, rdp->sign_key, rdp->rc4_key_len)) /* MacKeyN */ |
515 | 0 | goto out; |
516 | | |
517 | 0 | if (!winpr_Digest_Update(md5, pad2, sizeof(pad2))) /* pad2 */ |
518 | 0 | goto out; |
519 | | |
520 | 0 | if (!winpr_Digest_Update(md5, sha1_digest, sizeof(sha1_digest))) /* SHA1_Digest */ |
521 | 0 | goto out; |
522 | | |
523 | 0 | if (!winpr_Digest_Final(md5, md5_digest, sizeof(md5_digest))) |
524 | 0 | goto out; |
525 | | |
526 | 0 | memcpy(output, md5_digest, 8); |
527 | 0 | result = TRUE; |
528 | 0 | out: |
529 | 0 | if (!result) |
530 | 0 | WLog_WARN(TAG, "security mac signature generation failed"); |
531 | |
|
532 | 0 | winpr_Digest_Free(sha1); |
533 | 0 | winpr_Digest_Free(md5); |
534 | 0 | return result; |
535 | 0 | } |
536 | | |
537 | | static BOOL security_A(const BYTE* master_secret, size_t master_len, const BYTE* client_random, |
538 | | size_t client_len, const BYTE* server_random, size_t server_len, |
539 | | BYTE* output, size_t out_len) |
540 | 0 | { |
541 | 0 | WINPR_ASSERT(out_len >= 32); |
542 | | |
543 | 0 | return security_premaster_hash(A, sizeof(A), master_secret, master_len, client_random, |
544 | 0 | client_len, server_random, server_len, &output[0], 16) && |
545 | 0 | security_premaster_hash(BB, sizeof(BB), master_secret, master_len, client_random, |
546 | 0 | client_len, server_random, server_len, &output[16], 16) && |
547 | 0 | security_premaster_hash(CCC, sizeof(CCC), master_secret, master_len, client_random, |
548 | 0 | client_len, server_random, server_len, &output[32], |
549 | 0 | out_len - 32); |
550 | 0 | } |
551 | | |
552 | | static BOOL security_X(const BYTE* master_secret, size_t master_len, const BYTE* client_random, |
553 | | size_t client_len, const BYTE* server_random, size_t server_len, |
554 | | BYTE* output, size_t out_len) |
555 | 0 | { |
556 | 0 | const BYTE X[] = { 'X' }; |
557 | 0 | const BYTE YY[] = { 'Y', 'Y' }; |
558 | 0 | const BYTE ZZZ[] = { 'Z', 'Z', 'Z' }; |
559 | |
|
560 | 0 | WINPR_ASSERT(out_len >= 32); |
561 | | |
562 | 0 | return security_premaster_hash(X, sizeof(X), master_secret, master_len, client_random, |
563 | 0 | client_len, server_random, server_len, &output[0], 16) && |
564 | 0 | security_premaster_hash(YY, sizeof(YY), master_secret, master_len, client_random, |
565 | 0 | client_len, server_random, server_len, &output[16], 16) && |
566 | 0 | security_premaster_hash(ZZZ, sizeof(ZZZ), master_secret, master_len, client_random, |
567 | 0 | client_len, server_random, server_len, &output[32], |
568 | 0 | out_len - 32); |
569 | 0 | } |
570 | | |
571 | | static void fips_expand_key_bits(const BYTE* in, WINPR_ATTR_UNUSED size_t in_len, BYTE* out, |
572 | | WINPR_ATTR_UNUSED size_t out_len) |
573 | 0 | { |
574 | 0 | BYTE buf[21] = { 0 }; |
575 | |
|
576 | 0 | WINPR_ASSERT(in); |
577 | 0 | WINPR_ASSERT(in_len >= sizeof(buf)); |
578 | | |
579 | 0 | WINPR_ASSERT(out); |
580 | 0 | WINPR_ASSERT(out_len >= 24); |
581 | | |
582 | | /* reverse every byte in the key */ |
583 | 0 | for (size_t i = 0; i < sizeof(buf); i++) |
584 | 0 | buf[i] = fips_reverse_table[in[i]]; |
585 | | |
586 | | /* insert a zero-bit after every 7th bit */ |
587 | 0 | size_t b = 0; |
588 | 0 | for (size_t i = 0; i < 24; i++, b += 7) |
589 | 0 | { |
590 | 0 | const size_t p = b / 8; |
591 | 0 | const size_t r = b % 8; |
592 | |
|
593 | 0 | WINPR_ASSERT(p < sizeof(buf)); |
594 | 0 | if (r <= 1) |
595 | 0 | { |
596 | 0 | out[i] = (buf[p] << r) & 0xfe; |
597 | 0 | } |
598 | 0 | else |
599 | 0 | { |
600 | 0 | WINPR_ASSERT(p + 1 < sizeof(buf)); |
601 | | /* c is accumulator */ |
602 | 0 | BYTE c = (BYTE)(buf[p] << r) & 0xFF; |
603 | 0 | c |= buf[p + 1] >> (8 - r); |
604 | 0 | out[i] = c & 0xfe; |
605 | 0 | } |
606 | 0 | } |
607 | | |
608 | | /* reverse every byte */ |
609 | | /* alter lsb so the byte has odd parity */ |
610 | 0 | for (size_t i = 0; i < 24; i++) |
611 | 0 | out[i] = fips_oddparity_table[fips_reverse_table[out[i]]]; |
612 | 0 | } |
613 | | |
614 | | BOOL security_establish_keys(rdpRdp* rdp) |
615 | 0 | { |
616 | 0 | BYTE pre_master_secret[48] = { 0 }; |
617 | 0 | BYTE master_secret[48] = { 0 }; |
618 | 0 | BYTE session_key_blob[48] = { 0 }; |
619 | 0 | BYTE salt[] = { 0xD1, 0x26, 0x9E }; /* 40 bits: 3 bytes, 56 bits: 1 byte */ |
620 | 0 | BOOL status = FALSE; |
621 | |
|
622 | 0 | WINPR_ASSERT(rdp); |
623 | 0 | const rdpSettings* settings = rdp->settings; |
624 | 0 | WINPR_ASSERT(settings); |
625 | | |
626 | 0 | const BYTE* server_random = freerdp_settings_get_pointer(settings, FreeRDP_ServerRandom); |
627 | 0 | const BYTE* client_random = freerdp_settings_get_pointer(settings, FreeRDP_ClientRandom); |
628 | 0 | WINPR_ASSERT(client_random); |
629 | 0 | WINPR_ASSERT(server_random); |
630 | | |
631 | 0 | const UINT32 ClientRandomLength = |
632 | 0 | freerdp_settings_get_uint32(settings, FreeRDP_ClientRandomLength); |
633 | 0 | const UINT32 ServerRandomLength = |
634 | 0 | freerdp_settings_get_uint32(settings, FreeRDP_ServerRandomLength); |
635 | 0 | WINPR_ASSERT(ClientRandomLength == 32); |
636 | 0 | WINPR_ASSERT(ServerRandomLength == 32); |
637 | | |
638 | 0 | if (settings->EncryptionMethods == ENCRYPTION_METHOD_FIPS) |
639 | 0 | { |
640 | 0 | BYTE client_encrypt_key_t[WINPR_SHA1_DIGEST_LENGTH + 1] = { 0 }; |
641 | 0 | BYTE client_decrypt_key_t[WINPR_SHA1_DIGEST_LENGTH + 1] = { 0 }; |
642 | 0 | WINPR_DIGEST_CTX* sha1 = winpr_Digest_New(); |
643 | 0 | if (!sha1) |
644 | 0 | return FALSE; |
645 | | |
646 | 0 | if (!winpr_Digest_Init(sha1, WINPR_MD_SHA1) || |
647 | 0 | !winpr_Digest_Update(sha1, client_random + 16, 16) || |
648 | 0 | !winpr_Digest_Update(sha1, server_random + 16, 16) || |
649 | 0 | !winpr_Digest_Final(sha1, client_encrypt_key_t, sizeof(client_encrypt_key_t))) |
650 | 0 | { |
651 | 0 | winpr_Digest_Free(sha1); |
652 | 0 | return FALSE; |
653 | 0 | } |
654 | | |
655 | 0 | client_encrypt_key_t[20] = client_encrypt_key_t[0]; |
656 | |
|
657 | 0 | if (!winpr_Digest_Init(sha1, WINPR_MD_SHA1) || |
658 | 0 | !winpr_Digest_Update(sha1, client_random, 16) || |
659 | 0 | !winpr_Digest_Update(sha1, server_random, 16) || |
660 | 0 | !winpr_Digest_Final(sha1, client_decrypt_key_t, sizeof(client_decrypt_key_t))) |
661 | 0 | { |
662 | 0 | winpr_Digest_Free(sha1); |
663 | 0 | return FALSE; |
664 | 0 | } |
665 | | |
666 | 0 | client_decrypt_key_t[20] = client_decrypt_key_t[0]; |
667 | |
|
668 | 0 | if (!winpr_Digest_Init(sha1, WINPR_MD_SHA1) || |
669 | 0 | !winpr_Digest_Update(sha1, client_decrypt_key_t, WINPR_SHA1_DIGEST_LENGTH) || |
670 | 0 | !winpr_Digest_Update(sha1, client_encrypt_key_t, WINPR_SHA1_DIGEST_LENGTH) || |
671 | 0 | !winpr_Digest_Final(sha1, rdp->fips_sign_key, WINPR_SHA1_DIGEST_LENGTH)) |
672 | 0 | { |
673 | 0 | winpr_Digest_Free(sha1); |
674 | 0 | return FALSE; |
675 | 0 | } |
676 | | |
677 | 0 | winpr_Digest_Free(sha1); |
678 | |
|
679 | 0 | if (settings->ServerMode) |
680 | 0 | { |
681 | 0 | fips_expand_key_bits(client_encrypt_key_t, sizeof(client_encrypt_key_t), |
682 | 0 | rdp->fips_decrypt_key, sizeof(rdp->fips_decrypt_key)); |
683 | 0 | fips_expand_key_bits(client_decrypt_key_t, sizeof(client_decrypt_key_t), |
684 | 0 | rdp->fips_encrypt_key, sizeof(rdp->fips_encrypt_key)); |
685 | 0 | } |
686 | 0 | else |
687 | 0 | { |
688 | 0 | fips_expand_key_bits(client_encrypt_key_t, sizeof(client_encrypt_key_t), |
689 | 0 | rdp->fips_encrypt_key, sizeof(rdp->fips_encrypt_key)); |
690 | 0 | fips_expand_key_bits(client_decrypt_key_t, sizeof(client_decrypt_key_t), |
691 | 0 | rdp->fips_decrypt_key, sizeof(rdp->fips_decrypt_key)); |
692 | 0 | } |
693 | 0 | } |
694 | | |
695 | 0 | memcpy(pre_master_secret, client_random, 24); |
696 | 0 | memcpy(pre_master_secret + 24, server_random, 24); |
697 | |
|
698 | 0 | if (!security_A(pre_master_secret, sizeof(pre_master_secret), client_random, ClientRandomLength, |
699 | 0 | server_random, ServerRandomLength, master_secret, sizeof(master_secret)) || |
700 | 0 | !security_X(master_secret, sizeof(master_secret), client_random, ClientRandomLength, |
701 | 0 | server_random, ServerRandomLength, session_key_blob, sizeof(session_key_blob))) |
702 | 0 | { |
703 | 0 | return FALSE; |
704 | 0 | } |
705 | | |
706 | 0 | memcpy(rdp->sign_key, session_key_blob, 16); |
707 | |
|
708 | 0 | if (settings->ServerMode) |
709 | 0 | { |
710 | 0 | status = security_md5_16_32_32(&session_key_blob[16], client_random, server_random, |
711 | 0 | rdp->encrypt_key, sizeof(rdp->encrypt_key)); |
712 | 0 | status &= security_md5_16_32_32(&session_key_blob[32], client_random, server_random, |
713 | 0 | rdp->decrypt_key, sizeof(rdp->decrypt_key)); |
714 | 0 | } |
715 | 0 | else |
716 | 0 | { |
717 | | /* Allow FIPS use of MD5 here, this is just used for generation of the SessionKeyBlob as |
718 | | * described in MS-RDPELE. */ |
719 | | /* This is for RDP licensing packets which will already be encrypted under FIPS, so the use |
720 | | * of MD5 here is not */ |
721 | | /* for sensitive data protection. */ |
722 | 0 | status = |
723 | 0 | security_md5_16_32_32_Allow_FIPS(&session_key_blob[16], client_random, server_random, |
724 | 0 | rdp->decrypt_key, sizeof(rdp->decrypt_key)); |
725 | 0 | status &= |
726 | 0 | security_md5_16_32_32_Allow_FIPS(&session_key_blob[32], client_random, server_random, |
727 | 0 | rdp->encrypt_key, sizeof(rdp->encrypt_key)); |
728 | 0 | } |
729 | |
|
730 | 0 | if (!status) |
731 | 0 | return FALSE; |
732 | | |
733 | 0 | if (settings->EncryptionMethods == ENCRYPTION_METHOD_40BIT) |
734 | 0 | { |
735 | 0 | memcpy(rdp->sign_key, salt, 3); |
736 | 0 | memcpy(rdp->decrypt_key, salt, 3); |
737 | 0 | memcpy(rdp->encrypt_key, salt, 3); |
738 | 0 | rdp->rc4_key_len = 8; |
739 | 0 | } |
740 | 0 | else if (settings->EncryptionMethods == ENCRYPTION_METHOD_56BIT) |
741 | 0 | { |
742 | 0 | memcpy(rdp->sign_key, salt, 1); |
743 | 0 | memcpy(rdp->decrypt_key, salt, 1); |
744 | 0 | memcpy(rdp->encrypt_key, salt, 1); |
745 | 0 | rdp->rc4_key_len = 8; |
746 | 0 | } |
747 | 0 | else if (settings->EncryptionMethods == ENCRYPTION_METHOD_128BIT) |
748 | 0 | { |
749 | 0 | rdp->rc4_key_len = 16; |
750 | 0 | } |
751 | |
|
752 | 0 | if (!security_lock(rdp)) |
753 | 0 | return FALSE; |
754 | 0 | memcpy(rdp->decrypt_update_key, rdp->decrypt_key, 16); |
755 | 0 | memcpy(rdp->encrypt_update_key, rdp->encrypt_key, 16); |
756 | 0 | rdp->decrypt_use_count = 0; |
757 | 0 | rdp->decrypt_checksum_use_count = 0; |
758 | 0 | rdp->encrypt_use_count = 0; |
759 | 0 | rdp->encrypt_checksum_use_count = 0; |
760 | |
|
761 | 0 | return security_unlock(rdp); |
762 | 0 | } |
763 | | |
764 | | static BOOL security_key_update(BYTE* key, BYTE* update_key, size_t key_len, rdpRdp* rdp) |
765 | 0 | { |
766 | 0 | BYTE sha1h[WINPR_SHA1_DIGEST_LENGTH] = { 0 }; |
767 | 0 | WINPR_DIGEST_CTX* sha1 = NULL; |
768 | 0 | WINPR_DIGEST_CTX* md5 = NULL; |
769 | 0 | WINPR_RC4_CTX* rc4 = NULL; |
770 | 0 | BYTE salt[] = { 0xD1, 0x26, 0x9E }; /* 40 bits: 3 bytes, 56 bits: 1 byte */ |
771 | 0 | BOOL result = FALSE; |
772 | 0 | WLog_DBG(TAG, "updating RDP key"); |
773 | |
|
774 | 0 | if (!(sha1 = winpr_Digest_New())) |
775 | 0 | goto out; |
776 | | |
777 | 0 | if (!winpr_Digest_Init(sha1, WINPR_MD_SHA1)) |
778 | 0 | goto out; |
779 | | |
780 | 0 | if (!winpr_Digest_Update(sha1, update_key, key_len)) |
781 | 0 | goto out; |
782 | | |
783 | 0 | if (!winpr_Digest_Update(sha1, pad1, sizeof(pad1))) |
784 | 0 | goto out; |
785 | | |
786 | 0 | if (!winpr_Digest_Update(sha1, key, key_len)) |
787 | 0 | goto out; |
788 | | |
789 | 0 | if (!winpr_Digest_Final(sha1, sha1h, sizeof(sha1h))) |
790 | 0 | goto out; |
791 | | |
792 | 0 | if (!(md5 = winpr_Digest_New())) |
793 | 0 | goto out; |
794 | | |
795 | 0 | if (!winpr_Digest_Init(md5, WINPR_MD_MD5)) |
796 | 0 | goto out; |
797 | | |
798 | 0 | if (!winpr_Digest_Update(md5, update_key, key_len)) |
799 | 0 | goto out; |
800 | | |
801 | 0 | if (!winpr_Digest_Update(md5, pad2, sizeof(pad2))) |
802 | 0 | goto out; |
803 | | |
804 | 0 | if (!winpr_Digest_Update(md5, sha1h, sizeof(sha1h))) |
805 | 0 | goto out; |
806 | | |
807 | 0 | if (!winpr_Digest_Final(md5, key, WINPR_MD5_DIGEST_LENGTH)) |
808 | 0 | goto out; |
809 | | |
810 | 0 | if (!(rc4 = winpr_RC4_New(key, key_len))) |
811 | 0 | goto out; |
812 | | |
813 | 0 | if (!winpr_RC4_Update(rc4, key_len, key, key)) |
814 | 0 | goto out; |
815 | | |
816 | 0 | if (rdp->settings->EncryptionMethods == ENCRYPTION_METHOD_40BIT) |
817 | 0 | memcpy(key, salt, 3); |
818 | 0 | else if (rdp->settings->EncryptionMethods == ENCRYPTION_METHOD_56BIT) |
819 | 0 | memcpy(key, salt, 1); |
820 | |
|
821 | 0 | result = TRUE; |
822 | 0 | out: |
823 | 0 | winpr_Digest_Free(sha1); |
824 | 0 | winpr_Digest_Free(md5); |
825 | 0 | winpr_RC4_Free(rc4); |
826 | 0 | return result; |
827 | 0 | } |
828 | | |
829 | | BOOL security_encrypt(BYTE* data, size_t length, rdpRdp* rdp) |
830 | 0 | { |
831 | 0 | BOOL rc = FALSE; |
832 | |
|
833 | 0 | WINPR_ASSERT(rdp); |
834 | 0 | if (!rdp->rc4_encrypt_key) |
835 | 0 | { |
836 | 0 | WLog_ERR(TAG, "rdp->rc4_encrypt_key=%p", rdp->rc4_encrypt_key); |
837 | 0 | goto fail; |
838 | 0 | } |
839 | | |
840 | 0 | if (rdp->encrypt_use_count >= 4096) |
841 | 0 | { |
842 | 0 | if (!security_key_update(rdp->encrypt_key, rdp->encrypt_update_key, rdp->rc4_key_len, rdp)) |
843 | 0 | goto fail; |
844 | | |
845 | 0 | if (!rdp_reset_rc4_encrypt_keys(rdp)) |
846 | 0 | goto fail; |
847 | 0 | } |
848 | | |
849 | 0 | if (!winpr_RC4_Update(rdp->rc4_encrypt_key, length, data, data)) |
850 | 0 | goto fail; |
851 | | |
852 | 0 | rdp->encrypt_use_count++; |
853 | 0 | rdp->encrypt_checksum_use_count++; |
854 | 0 | rc = TRUE; |
855 | 0 | fail: |
856 | 0 | return rc; |
857 | 0 | } |
858 | | |
859 | | BOOL security_decrypt(BYTE* data, size_t length, rdpRdp* rdp) |
860 | 0 | { |
861 | 0 | BOOL rc = FALSE; |
862 | |
|
863 | 0 | WINPR_ASSERT(data || (length == 0)); |
864 | 0 | WINPR_ASSERT(rdp); |
865 | | |
866 | 0 | if (!rdp->rc4_decrypt_key) |
867 | 0 | { |
868 | 0 | WLog_ERR(TAG, "rdp->rc4_decrypt_key=%p", rdp->rc4_decrypt_key); |
869 | 0 | goto fail; |
870 | 0 | } |
871 | | |
872 | 0 | if (rdp->decrypt_use_count >= 4096) |
873 | 0 | { |
874 | 0 | if (!security_key_update(rdp->decrypt_key, rdp->decrypt_update_key, rdp->rc4_key_len, rdp)) |
875 | 0 | goto fail; |
876 | | |
877 | 0 | if (!rdp_reset_rc4_decrypt_keys(rdp)) |
878 | 0 | goto fail; |
879 | 0 | } |
880 | | |
881 | 0 | if (!winpr_RC4_Update(rdp->rc4_decrypt_key, length, data, data)) |
882 | 0 | goto fail; |
883 | | |
884 | 0 | rdp->decrypt_use_count += 1; |
885 | 0 | rdp->decrypt_checksum_use_count++; |
886 | 0 | rc = TRUE; |
887 | 0 | fail: |
888 | 0 | if (!rc) |
889 | 0 | WLog_WARN(TAG, "Failed to decrypt security"); |
890 | 0 | return rc; |
891 | 0 | } |
892 | | |
893 | | BOOL security_hmac_signature(const BYTE* data, size_t length, BYTE* output, size_t out_len, |
894 | | rdpRdp* rdp) |
895 | 0 | { |
896 | 0 | BYTE buf[WINPR_SHA1_DIGEST_LENGTH] = { 0 }; |
897 | 0 | BYTE use_count_le[4] = { 0 }; |
898 | 0 | WINPR_HMAC_CTX* hmac = NULL; |
899 | 0 | BOOL result = FALSE; |
900 | |
|
901 | 0 | WINPR_ASSERT(rdp); |
902 | 0 | WINPR_ASSERT(output); |
903 | 0 | if (out_len < 8) |
904 | 0 | return FALSE; |
905 | | |
906 | 0 | security_UINT32_le(use_count_le, sizeof(use_count_le), rdp->encrypt_use_count); |
907 | |
|
908 | 0 | if (!(hmac = winpr_HMAC_New())) |
909 | 0 | return FALSE; |
910 | | |
911 | 0 | if (!winpr_HMAC_Init(hmac, WINPR_MD_SHA1, rdp->fips_sign_key, WINPR_SHA1_DIGEST_LENGTH)) |
912 | 0 | goto out; |
913 | | |
914 | 0 | if (!winpr_HMAC_Update(hmac, data, length)) |
915 | 0 | goto out; |
916 | | |
917 | 0 | if (!winpr_HMAC_Update(hmac, use_count_le, 4)) |
918 | 0 | goto out; |
919 | | |
920 | 0 | if (!winpr_HMAC_Final(hmac, buf, WINPR_SHA1_DIGEST_LENGTH)) |
921 | 0 | goto out; |
922 | | |
923 | 0 | memmove(output, buf, 8); |
924 | 0 | result = TRUE; |
925 | 0 | out: |
926 | 0 | winpr_HMAC_Free(hmac); |
927 | 0 | return result; |
928 | 0 | } |
929 | | |
930 | | BOOL security_fips_encrypt(BYTE* data, size_t length, rdpRdp* rdp) |
931 | 0 | { |
932 | 0 | BOOL rc = FALSE; |
933 | 0 | size_t olen = 0; |
934 | |
|
935 | 0 | if (!winpr_Cipher_Update(rdp->fips_encrypt, data, length, data, &olen)) |
936 | 0 | goto fail; |
937 | | |
938 | 0 | rdp->encrypt_use_count++; |
939 | 0 | rc = TRUE; |
940 | 0 | fail: |
941 | 0 | return rc; |
942 | 0 | } |
943 | | |
944 | | BOOL security_fips_decrypt(BYTE* data, size_t length, rdpRdp* rdp) |
945 | 0 | { |
946 | 0 | size_t olen = 0; |
947 | |
|
948 | 0 | if (!rdp || !rdp->fips_decrypt) |
949 | 0 | { |
950 | 0 | WLog_ERR(TAG, "rdp=%p, rdp->fips_decrypt=%p", rdp, rdp ? rdp->fips_decrypt : NULL); |
951 | 0 | return FALSE; |
952 | 0 | } |
953 | | |
954 | 0 | if (!winpr_Cipher_Update(rdp->fips_decrypt, data, length, data, &olen)) |
955 | 0 | return FALSE; |
956 | | |
957 | 0 | return TRUE; |
958 | 0 | } |
959 | | |
960 | | BOOL security_fips_check_signature(const BYTE* data, size_t length, const BYTE* sig, size_t sig_len, |
961 | | rdpRdp* rdp) |
962 | 0 | { |
963 | 0 | BYTE buf[WINPR_SHA1_DIGEST_LENGTH] = { 0 }; |
964 | 0 | BYTE use_count_le[4] = { 0 }; |
965 | 0 | WINPR_HMAC_CTX* hmac = NULL; |
966 | 0 | BOOL result = FALSE; |
967 | |
|
968 | 0 | security_UINT32_le(use_count_le, sizeof(use_count_le), rdp->decrypt_use_count++); |
969 | |
|
970 | 0 | if (!(hmac = winpr_HMAC_New())) |
971 | 0 | goto out; |
972 | | |
973 | 0 | if (!winpr_HMAC_Init(hmac, WINPR_MD_SHA1, rdp->fips_sign_key, WINPR_SHA1_DIGEST_LENGTH)) |
974 | 0 | goto out; |
975 | | |
976 | 0 | if (!winpr_HMAC_Update(hmac, data, length)) |
977 | 0 | goto out; |
978 | | |
979 | 0 | if (!winpr_HMAC_Update(hmac, use_count_le, 4)) |
980 | 0 | goto out; |
981 | | |
982 | 0 | if (!winpr_HMAC_Final(hmac, buf, WINPR_SHA1_DIGEST_LENGTH)) |
983 | 0 | goto out; |
984 | | |
985 | 0 | if ((sig_len >= 8) && (memcmp(sig, buf, 8) == 0)) |
986 | 0 | result = TRUE; |
987 | |
|
988 | 0 | out: |
989 | 0 | if (!result) |
990 | 0 | WLog_WARN(TAG, "signature check failed"); |
991 | 0 | winpr_HMAC_Free(hmac); |
992 | 0 | return result; |
993 | 0 | } |
994 | | |
995 | | BOOL security_lock(rdpRdp* rdp) |
996 | 0 | { |
997 | 0 | WINPR_ASSERT(rdp); |
998 | 0 | EnterCriticalSection(&rdp->critical); |
999 | 0 | return TRUE; |
1000 | 0 | } |
1001 | | |
1002 | | BOOL security_unlock(rdpRdp* rdp) |
1003 | 0 | { |
1004 | 0 | WINPR_ASSERT(rdp); |
1005 | 0 | LeaveCriticalSection(&rdp->critical); |
1006 | 0 | return TRUE; |
1007 | 0 | } |