/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, size_t session_len, |
205 | | const BYTE* client_random, size_t client_len, const BYTE* server_random, |
206 | | size_t server_len, BYTE* output, size_t out_len) |
207 | 0 | { |
208 | | /* MacSaltKey = First128Bits(SessionKeyBlob) */ |
209 | 0 | WINPR_ASSERT(out_len >= 16); |
210 | 0 | WINPR_ASSERT(session_len >= 16); |
211 | 0 | WINPR_UNUSED(client_random); |
212 | 0 | WINPR_UNUSED(client_len); |
213 | 0 | WINPR_UNUSED(server_random); |
214 | 0 | WINPR_UNUSED(server_len); |
215 | 0 | memcpy(output, session_key_blob, 16); |
216 | 0 | } |
217 | | |
218 | | static BOOL security_md5_16_32_32(const BYTE* in0, const BYTE* in1, const BYTE* in2, BYTE* output, |
219 | | size_t out_len) |
220 | 0 | { |
221 | 0 | WINPR_DIGEST_CTX* md5 = NULL; |
222 | 0 | BOOL result = FALSE; |
223 | |
|
224 | 0 | WINPR_ASSERT(WINPR_MD5_DIGEST_LENGTH <= out_len); |
225 | | |
226 | 0 | if (!(md5 = winpr_Digest_New())) |
227 | 0 | return FALSE; |
228 | | |
229 | 0 | if (!winpr_Digest_Init(md5, WINPR_MD_MD5)) |
230 | 0 | goto out; |
231 | | |
232 | 0 | if (!winpr_Digest_Update(md5, in0, 16)) |
233 | 0 | goto out; |
234 | | |
235 | 0 | if (!winpr_Digest_Update(md5, in1, 32)) |
236 | 0 | goto out; |
237 | | |
238 | 0 | if (!winpr_Digest_Update(md5, in2, 32)) |
239 | 0 | goto out; |
240 | | |
241 | 0 | if (!winpr_Digest_Final(md5, output, out_len)) |
242 | 0 | goto out; |
243 | | |
244 | 0 | result = TRUE; |
245 | 0 | out: |
246 | 0 | winpr_Digest_Free(md5); |
247 | 0 | return result; |
248 | 0 | } |
249 | | |
250 | | static BOOL security_md5_16_32_32_Allow_FIPS(const BYTE* in0, const BYTE* in1, const BYTE* in2, |
251 | | BYTE* output, size_t out_len) |
252 | 0 | { |
253 | 0 | WINPR_DIGEST_CTX* md5 = NULL; |
254 | 0 | BOOL result = FALSE; |
255 | |
|
256 | 0 | WINPR_ASSERT(out_len >= WINPR_MD5_DIGEST_LENGTH); |
257 | | |
258 | 0 | if (!(md5 = winpr_Digest_New())) |
259 | 0 | return FALSE; |
260 | 0 | if (!winpr_Digest_Init_Allow_FIPS(md5, WINPR_MD_MD5)) |
261 | 0 | goto out; |
262 | 0 | if (!winpr_Digest_Update(md5, in0, 16)) |
263 | 0 | goto out; |
264 | 0 | if (!winpr_Digest_Update(md5, in1, 32)) |
265 | 0 | goto out; |
266 | 0 | if (!winpr_Digest_Update(md5, in2, 32)) |
267 | 0 | goto out; |
268 | 0 | if (!winpr_Digest_Final(md5, output, out_len)) |
269 | 0 | goto out; |
270 | | |
271 | 0 | result = TRUE; |
272 | 0 | out: |
273 | 0 | winpr_Digest_Free(md5); |
274 | 0 | return result; |
275 | 0 | } |
276 | | |
277 | | BOOL security_licensing_encryption_key(const BYTE* session_key_blob, size_t session_len, |
278 | | const BYTE* client_random, size_t client_len, |
279 | | const BYTE* server_random, size_t server_len, BYTE* output, |
280 | | size_t out_len) |
281 | 0 | { |
282 | 0 | if (session_len < 16) |
283 | 0 | return FALSE; |
284 | 0 | if (client_len < 32) |
285 | 0 | return FALSE; |
286 | 0 | if (server_len < 32) |
287 | 0 | return FALSE; |
288 | | /* LicensingEncryptionKey = MD5(Second128Bits(SessionKeyBlob) + ClientRandom + ServerRandom)) |
289 | | * Allow FIPS use of MD5 here, this is just used for creating the licensing encryption key as |
290 | | * described in MS-RDPELE. This is for RDP licensing packets which will already be encrypted |
291 | | * under FIPS, so the use of MD5 here is not for sensitive data protection. */ |
292 | 0 | return security_md5_16_32_32_Allow_FIPS(&session_key_blob[16], client_random, server_random, |
293 | 0 | output, out_len); |
294 | 0 | } |
295 | | |
296 | | static void security_UINT32_le(BYTE* output, size_t out_len, UINT32 value) |
297 | 0 | { |
298 | 0 | WINPR_ASSERT(output); |
299 | 0 | WINPR_ASSERT(out_len >= 4); |
300 | 0 | output[0] = (value)&0xFF; |
301 | 0 | output[1] = (value >> 8) & 0xFF; |
302 | 0 | output[2] = (value >> 16) & 0xFF; |
303 | 0 | output[3] = (value >> 24) & 0xFF; |
304 | 0 | } |
305 | | |
306 | | BOOL security_mac_data(const BYTE* mac_salt_key, size_t mac_salt_key_length, const BYTE* data, |
307 | | size_t length, BYTE* output, size_t output_length) |
308 | 0 | { |
309 | 0 | WINPR_DIGEST_CTX* sha1 = NULL; |
310 | 0 | WINPR_DIGEST_CTX* md5 = NULL; |
311 | 0 | BYTE length_le[4] = { 0 }; |
312 | 0 | BYTE sha1_digest[WINPR_SHA1_DIGEST_LENGTH] = { 0 }; |
313 | 0 | BOOL result = FALSE; |
314 | |
|
315 | 0 | WINPR_ASSERT(length <= UINT32_MAX); |
316 | 0 | WINPR_ASSERT(mac_salt_key_length == WINPR_MD5_DIGEST_LENGTH); |
317 | 0 | WINPR_ASSERT(output_length == WINPR_MD5_DIGEST_LENGTH); |
318 | | |
319 | | /* MacData = MD5(MacSaltKey + pad2 + SHA1(MacSaltKey + pad1 + length + data)) */ |
320 | 0 | security_UINT32_le(length_le, sizeof(length_le), 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 | | |
393 | 0 | security_UINT32_le(length_le, sizeof(length_le), length); /* length must be little-endian */ |
394 | | |
395 | | /* SHA1_Digest = SHA1(MACKeyN + pad1 + length + data) */ |
396 | 0 | if (!(sha1 = winpr_Digest_New())) |
397 | 0 | goto out; |
398 | | |
399 | 0 | if (!winpr_Digest_Init(sha1, WINPR_MD_SHA1)) |
400 | 0 | goto out; |
401 | | |
402 | 0 | if (!winpr_Digest_Update(sha1, rdp->sign_key, rdp->rc4_key_len)) /* MacKeyN */ |
403 | 0 | goto out; |
404 | | |
405 | 0 | if (!winpr_Digest_Update(sha1, pad1, sizeof(pad1))) /* pad1 */ |
406 | 0 | goto out; |
407 | | |
408 | 0 | if (!winpr_Digest_Update(sha1, length_le, sizeof(length_le))) /* length */ |
409 | 0 | goto out; |
410 | | |
411 | 0 | if (!winpr_Digest_Update(sha1, data, length)) /* data */ |
412 | 0 | goto out; |
413 | | |
414 | 0 | if (!winpr_Digest_Final(sha1, sha1_digest, sizeof(sha1_digest))) |
415 | 0 | goto out; |
416 | | |
417 | | /* MACSignature = First64Bits(MD5(MACKeyN + pad2 + SHA1_Digest)) */ |
418 | 0 | if (!(md5 = winpr_Digest_New())) |
419 | 0 | goto out; |
420 | | |
421 | 0 | if (!winpr_Digest_Init(md5, WINPR_MD_MD5)) |
422 | 0 | goto out; |
423 | | |
424 | 0 | if (!winpr_Digest_Update(md5, rdp->sign_key, rdp->rc4_key_len)) /* MacKeyN */ |
425 | 0 | goto out; |
426 | | |
427 | 0 | if (!winpr_Digest_Update(md5, pad2, sizeof(pad2))) /* pad2 */ |
428 | 0 | goto out; |
429 | | |
430 | 0 | if (!winpr_Digest_Update(md5, sha1_digest, sizeof(sha1_digest))) /* SHA1_Digest */ |
431 | 0 | goto out; |
432 | | |
433 | 0 | if (!winpr_Digest_Final(md5, md5_digest, sizeof(md5_digest))) |
434 | 0 | goto out; |
435 | | |
436 | 0 | memcpy(output, md5_digest, 8); |
437 | 0 | result = TRUE; |
438 | 0 | out: |
439 | 0 | if (!result) |
440 | 0 | WLog_WARN(TAG, "security mac generation failed"); |
441 | 0 | winpr_Digest_Free(sha1); |
442 | 0 | winpr_Digest_Free(md5); |
443 | 0 | return result; |
444 | 0 | } |
445 | | |
446 | | BOOL security_salted_mac_signature(rdpRdp* rdp, const BYTE* data, UINT32 length, BOOL encryption, |
447 | | BYTE* output, size_t out_len) |
448 | 0 | { |
449 | 0 | WINPR_DIGEST_CTX* sha1 = NULL; |
450 | 0 | WINPR_DIGEST_CTX* md5 = NULL; |
451 | 0 | BYTE length_le[4] = { 0 }; |
452 | 0 | BYTE use_count_le[4] = { 0 }; |
453 | 0 | BYTE md5_digest[WINPR_MD5_DIGEST_LENGTH] = { 0 }; |
454 | 0 | BYTE sha1_digest[WINPR_SHA1_DIGEST_LENGTH] = { 0 }; |
455 | 0 | BOOL result = FALSE; |
456 | |
|
457 | 0 | WINPR_ASSERT(rdp); |
458 | 0 | WINPR_ASSERT(data || (length == 0)); |
459 | 0 | WINPR_ASSERT(output); |
460 | 0 | WINPR_ASSERT(out_len >= 8); |
461 | | |
462 | 0 | security_UINT32_le(length_le, sizeof(length_le), length); /* length must be little-endian */ |
463 | |
|
464 | 0 | if (encryption) |
465 | 0 | { |
466 | 0 | security_UINT32_le(use_count_le, sizeof(use_count_le), rdp->encrypt_checksum_use_count); |
467 | 0 | } |
468 | 0 | else |
469 | 0 | { |
470 | | /* |
471 | | * We calculate checksum on plain text, so we must have already |
472 | | * decrypt it, which means decrypt_checksum_use_count is off by one. |
473 | | */ |
474 | 0 | security_UINT32_le(use_count_le, sizeof(use_count_le), |
475 | 0 | rdp->decrypt_checksum_use_count - 1u); |
476 | 0 | } |
477 | | |
478 | | /* SHA1_Digest = SHA1(MACKeyN + pad1 + length + data) */ |
479 | 0 | if (!(sha1 = winpr_Digest_New())) |
480 | 0 | goto out; |
481 | | |
482 | 0 | if (!winpr_Digest_Init(sha1, WINPR_MD_SHA1)) |
483 | 0 | goto out; |
484 | | |
485 | 0 | if (!winpr_Digest_Update(sha1, rdp->sign_key, rdp->rc4_key_len)) /* MacKeyN */ |
486 | 0 | goto out; |
487 | | |
488 | 0 | if (!winpr_Digest_Update(sha1, pad1, sizeof(pad1))) /* pad1 */ |
489 | 0 | goto out; |
490 | | |
491 | 0 | if (!winpr_Digest_Update(sha1, length_le, sizeof(length_le))) /* length */ |
492 | 0 | goto out; |
493 | | |
494 | 0 | if (!winpr_Digest_Update(sha1, data, length)) /* data */ |
495 | 0 | goto out; |
496 | | |
497 | 0 | if (!winpr_Digest_Update(sha1, use_count_le, sizeof(use_count_le))) /* encryptionCount */ |
498 | 0 | goto out; |
499 | | |
500 | 0 | if (!winpr_Digest_Final(sha1, sha1_digest, sizeof(sha1_digest))) |
501 | 0 | goto out; |
502 | | |
503 | | /* MACSignature = First64Bits(MD5(MACKeyN + pad2 + SHA1_Digest)) */ |
504 | 0 | if (!(md5 = winpr_Digest_New())) |
505 | 0 | goto out; |
506 | | |
507 | 0 | if (!winpr_Digest_Init(md5, WINPR_MD_MD5)) |
508 | 0 | goto out; |
509 | | |
510 | 0 | if (!winpr_Digest_Update(md5, rdp->sign_key, rdp->rc4_key_len)) /* MacKeyN */ |
511 | 0 | goto out; |
512 | | |
513 | 0 | if (!winpr_Digest_Update(md5, pad2, sizeof(pad2))) /* pad2 */ |
514 | 0 | goto out; |
515 | | |
516 | 0 | if (!winpr_Digest_Update(md5, sha1_digest, sizeof(sha1_digest))) /* SHA1_Digest */ |
517 | 0 | goto out; |
518 | | |
519 | 0 | if (!winpr_Digest_Final(md5, md5_digest, sizeof(md5_digest))) |
520 | 0 | goto out; |
521 | | |
522 | 0 | memcpy(output, md5_digest, 8); |
523 | 0 | result = TRUE; |
524 | 0 | out: |
525 | 0 | if (!result) |
526 | 0 | WLog_WARN(TAG, "security mac signature generation failed"); |
527 | |
|
528 | 0 | winpr_Digest_Free(sha1); |
529 | 0 | winpr_Digest_Free(md5); |
530 | 0 | return result; |
531 | 0 | } |
532 | | |
533 | | static BOOL security_A(const BYTE* master_secret, size_t master_len, const BYTE* client_random, |
534 | | size_t client_len, const BYTE* server_random, size_t server_len, |
535 | | BYTE* output, size_t out_len) |
536 | 0 | { |
537 | 0 | WINPR_ASSERT(out_len >= 32); |
538 | | |
539 | 0 | return security_premaster_hash(A, sizeof(A), master_secret, master_len, client_random, |
540 | 0 | client_len, server_random, server_len, &output[0], 16) && |
541 | 0 | security_premaster_hash(BB, sizeof(BB), master_secret, master_len, client_random, |
542 | 0 | client_len, server_random, server_len, &output[16], 16) && |
543 | 0 | security_premaster_hash(CCC, sizeof(CCC), master_secret, master_len, client_random, |
544 | 0 | client_len, server_random, server_len, &output[32], |
545 | 0 | out_len - 32); |
546 | 0 | } |
547 | | |
548 | | static BOOL security_X(const BYTE* master_secret, size_t master_len, const BYTE* client_random, |
549 | | size_t client_len, const BYTE* server_random, size_t server_len, |
550 | | BYTE* output, size_t out_len) |
551 | 0 | { |
552 | 0 | const BYTE X[] = { 'X' }; |
553 | 0 | const BYTE YY[] = { 'Y', 'Y' }; |
554 | 0 | const BYTE ZZZ[] = { 'Z', 'Z', 'Z' }; |
555 | |
|
556 | 0 | WINPR_ASSERT(out_len >= 32); |
557 | | |
558 | 0 | return security_premaster_hash(X, sizeof(X), master_secret, master_len, client_random, |
559 | 0 | client_len, server_random, server_len, &output[0], 16) && |
560 | 0 | security_premaster_hash(YY, sizeof(YY), master_secret, master_len, client_random, |
561 | 0 | client_len, server_random, server_len, &output[16], 16) && |
562 | 0 | security_premaster_hash(ZZZ, sizeof(ZZZ), master_secret, master_len, client_random, |
563 | 0 | client_len, server_random, server_len, &output[32], |
564 | 0 | out_len - 32); |
565 | 0 | } |
566 | | |
567 | | static void fips_expand_key_bits(const BYTE* in, size_t in_len, BYTE* out, size_t out_len) |
568 | 0 | { |
569 | 0 | BYTE buf[21] = { 0 }; |
570 | |
|
571 | 0 | WINPR_ASSERT(in); |
572 | 0 | WINPR_ASSERT(in_len >= sizeof(buf)); |
573 | | |
574 | 0 | WINPR_ASSERT(out); |
575 | 0 | WINPR_ASSERT(out_len > 24); |
576 | | |
577 | | /* reverse every byte in the key */ |
578 | 0 | for (size_t i = 0; i < sizeof(buf); i++) |
579 | 0 | buf[i] = fips_reverse_table[in[i]]; |
580 | | |
581 | | /* insert a zero-bit after every 7th bit */ |
582 | 0 | size_t b = 0; |
583 | 0 | for (size_t i = 0; i < 24; i++, b += 7) |
584 | 0 | { |
585 | 0 | const size_t p = b / 8; |
586 | 0 | const size_t r = b % 8; |
587 | |
|
588 | 0 | WINPR_ASSERT(p < sizeof(buf)); |
589 | 0 | if (r <= 1) |
590 | 0 | { |
591 | 0 | out[i] = (buf[p] << r) & 0xfe; |
592 | 0 | } |
593 | 0 | else |
594 | 0 | { |
595 | 0 | WINPR_ASSERT(p + 1 < sizeof(buf)); |
596 | | /* c is accumulator */ |
597 | 0 | BYTE c = (BYTE)(buf[p] << r) & 0xFF; |
598 | 0 | c |= buf[p + 1] >> (8 - r); |
599 | 0 | out[i] = c & 0xfe; |
600 | 0 | } |
601 | 0 | } |
602 | | |
603 | | /* reverse every byte */ |
604 | | /* alter lsb so the byte has odd parity */ |
605 | 0 | for (size_t i = 0; i < 24; i++) |
606 | 0 | out[i] = fips_oddparity_table[fips_reverse_table[out[i]]]; |
607 | 0 | } |
608 | | |
609 | | BOOL security_establish_keys(rdpRdp* rdp) |
610 | 0 | { |
611 | 0 | BYTE pre_master_secret[48] = { 0 }; |
612 | 0 | BYTE master_secret[48] = { 0 }; |
613 | 0 | BYTE session_key_blob[48] = { 0 }; |
614 | 0 | BYTE salt[] = { 0xD1, 0x26, 0x9E }; /* 40 bits: 3 bytes, 56 bits: 1 byte */ |
615 | 0 | BOOL status = FALSE; |
616 | |
|
617 | 0 | WINPR_ASSERT(rdp); |
618 | 0 | const rdpSettings* settings = rdp->settings; |
619 | 0 | WINPR_ASSERT(settings); |
620 | | |
621 | 0 | const BYTE* server_random = freerdp_settings_get_pointer(settings, FreeRDP_ServerRandom); |
622 | 0 | const BYTE* client_random = freerdp_settings_get_pointer(settings, FreeRDP_ClientRandom); |
623 | 0 | WINPR_ASSERT(client_random); |
624 | 0 | WINPR_ASSERT(server_random); |
625 | | |
626 | 0 | const UINT32 ClientRandomLength = |
627 | 0 | freerdp_settings_get_uint32(settings, FreeRDP_ClientRandomLength); |
628 | 0 | const UINT32 ServerRandomLength = |
629 | 0 | freerdp_settings_get_uint32(settings, FreeRDP_ServerRandomLength); |
630 | 0 | WINPR_ASSERT(ClientRandomLength == 32); |
631 | 0 | WINPR_ASSERT(ServerRandomLength == 32); |
632 | | |
633 | 0 | if (settings->EncryptionMethods == ENCRYPTION_METHOD_FIPS) |
634 | 0 | { |
635 | 0 | BYTE client_encrypt_key_t[WINPR_SHA1_DIGEST_LENGTH + 1] = { 0 }; |
636 | 0 | BYTE client_decrypt_key_t[WINPR_SHA1_DIGEST_LENGTH + 1] = { 0 }; |
637 | 0 | WINPR_DIGEST_CTX* sha1 = winpr_Digest_New(); |
638 | 0 | if (!sha1) |
639 | 0 | return FALSE; |
640 | | |
641 | 0 | if (!winpr_Digest_Init(sha1, WINPR_MD_SHA1) || |
642 | 0 | !winpr_Digest_Update(sha1, client_random + 16, 16) || |
643 | 0 | !winpr_Digest_Update(sha1, server_random + 16, 16) || |
644 | 0 | !winpr_Digest_Final(sha1, client_encrypt_key_t, sizeof(client_encrypt_key_t))) |
645 | 0 | { |
646 | 0 | winpr_Digest_Free(sha1); |
647 | 0 | return FALSE; |
648 | 0 | } |
649 | | |
650 | 0 | client_encrypt_key_t[20] = client_encrypt_key_t[0]; |
651 | |
|
652 | 0 | if (!winpr_Digest_Init(sha1, WINPR_MD_SHA1) || |
653 | 0 | !winpr_Digest_Update(sha1, client_random, 16) || |
654 | 0 | !winpr_Digest_Update(sha1, server_random, 16) || |
655 | 0 | !winpr_Digest_Final(sha1, client_decrypt_key_t, sizeof(client_decrypt_key_t))) |
656 | 0 | { |
657 | 0 | winpr_Digest_Free(sha1); |
658 | 0 | return FALSE; |
659 | 0 | } |
660 | | |
661 | 0 | client_decrypt_key_t[20] = client_decrypt_key_t[0]; |
662 | |
|
663 | 0 | if (!winpr_Digest_Init(sha1, WINPR_MD_SHA1) || |
664 | 0 | !winpr_Digest_Update(sha1, client_decrypt_key_t, WINPR_SHA1_DIGEST_LENGTH) || |
665 | 0 | !winpr_Digest_Update(sha1, client_encrypt_key_t, WINPR_SHA1_DIGEST_LENGTH) || |
666 | 0 | !winpr_Digest_Final(sha1, rdp->fips_sign_key, WINPR_SHA1_DIGEST_LENGTH)) |
667 | 0 | { |
668 | 0 | winpr_Digest_Free(sha1); |
669 | 0 | return FALSE; |
670 | 0 | } |
671 | | |
672 | 0 | winpr_Digest_Free(sha1); |
673 | |
|
674 | 0 | if (settings->ServerMode) |
675 | 0 | { |
676 | 0 | fips_expand_key_bits(client_encrypt_key_t, sizeof(client_encrypt_key_t), |
677 | 0 | rdp->fips_decrypt_key, sizeof(rdp->fips_decrypt_key)); |
678 | 0 | fips_expand_key_bits(client_decrypt_key_t, sizeof(client_decrypt_key_t), |
679 | 0 | rdp->fips_encrypt_key, sizeof(rdp->fips_encrypt_key)); |
680 | 0 | } |
681 | 0 | else |
682 | 0 | { |
683 | 0 | fips_expand_key_bits(client_encrypt_key_t, sizeof(client_encrypt_key_t), |
684 | 0 | rdp->fips_encrypt_key, sizeof(rdp->fips_encrypt_key)); |
685 | 0 | fips_expand_key_bits(client_decrypt_key_t, sizeof(client_decrypt_key_t), |
686 | 0 | rdp->fips_decrypt_key, sizeof(rdp->fips_decrypt_key)); |
687 | 0 | } |
688 | 0 | } |
689 | | |
690 | 0 | memcpy(pre_master_secret, client_random, 24); |
691 | 0 | memcpy(pre_master_secret + 24, server_random, 24); |
692 | |
|
693 | 0 | if (!security_A(pre_master_secret, sizeof(pre_master_secret), client_random, ClientRandomLength, |
694 | 0 | server_random, ServerRandomLength, master_secret, sizeof(master_secret)) || |
695 | 0 | !security_X(master_secret, sizeof(master_secret), client_random, ClientRandomLength, |
696 | 0 | server_random, ServerRandomLength, session_key_blob, sizeof(session_key_blob))) |
697 | 0 | { |
698 | 0 | return FALSE; |
699 | 0 | } |
700 | | |
701 | 0 | memcpy(rdp->sign_key, session_key_blob, 16); |
702 | |
|
703 | 0 | if (settings->ServerMode) |
704 | 0 | { |
705 | 0 | status = security_md5_16_32_32(&session_key_blob[16], client_random, server_random, |
706 | 0 | rdp->encrypt_key, sizeof(rdp->encrypt_key)); |
707 | 0 | status &= security_md5_16_32_32(&session_key_blob[32], client_random, server_random, |
708 | 0 | rdp->decrypt_key, sizeof(rdp->decrypt_key)); |
709 | 0 | } |
710 | 0 | else |
711 | 0 | { |
712 | | /* Allow FIPS use of MD5 here, this is just used for generation of the SessionKeyBlob as |
713 | | * described in MS-RDPELE. */ |
714 | | /* This is for RDP licensing packets which will already be encrypted under FIPS, so the use |
715 | | * of MD5 here is not */ |
716 | | /* for sensitive data protection. */ |
717 | 0 | status = |
718 | 0 | security_md5_16_32_32_Allow_FIPS(&session_key_blob[16], client_random, server_random, |
719 | 0 | rdp->decrypt_key, sizeof(rdp->decrypt_key)); |
720 | 0 | status &= |
721 | 0 | security_md5_16_32_32_Allow_FIPS(&session_key_blob[32], client_random, server_random, |
722 | 0 | rdp->encrypt_key, sizeof(rdp->encrypt_key)); |
723 | 0 | } |
724 | |
|
725 | 0 | if (!status) |
726 | 0 | return FALSE; |
727 | | |
728 | 0 | if (settings->EncryptionMethods == ENCRYPTION_METHOD_40BIT) |
729 | 0 | { |
730 | 0 | memcpy(rdp->sign_key, salt, 3); |
731 | 0 | memcpy(rdp->decrypt_key, salt, 3); |
732 | 0 | memcpy(rdp->encrypt_key, salt, 3); |
733 | 0 | rdp->rc4_key_len = 8; |
734 | 0 | } |
735 | 0 | else if (settings->EncryptionMethods == ENCRYPTION_METHOD_56BIT) |
736 | 0 | { |
737 | 0 | memcpy(rdp->sign_key, salt, 1); |
738 | 0 | memcpy(rdp->decrypt_key, salt, 1); |
739 | 0 | memcpy(rdp->encrypt_key, salt, 1); |
740 | 0 | rdp->rc4_key_len = 8; |
741 | 0 | } |
742 | 0 | else if (settings->EncryptionMethods == ENCRYPTION_METHOD_128BIT) |
743 | 0 | { |
744 | 0 | rdp->rc4_key_len = 16; |
745 | 0 | } |
746 | |
|
747 | 0 | if (!security_lock(rdp)) |
748 | 0 | return FALSE; |
749 | 0 | memcpy(rdp->decrypt_update_key, rdp->decrypt_key, 16); |
750 | 0 | memcpy(rdp->encrypt_update_key, rdp->encrypt_key, 16); |
751 | 0 | rdp->decrypt_use_count = 0; |
752 | 0 | rdp->decrypt_checksum_use_count = 0; |
753 | 0 | rdp->encrypt_use_count = 0; |
754 | 0 | rdp->encrypt_checksum_use_count = 0; |
755 | |
|
756 | 0 | return security_unlock(rdp); |
757 | 0 | } |
758 | | |
759 | | static BOOL security_key_update(BYTE* key, BYTE* update_key, size_t key_len, rdpRdp* rdp) |
760 | 0 | { |
761 | 0 | BYTE sha1h[WINPR_SHA1_DIGEST_LENGTH] = { 0 }; |
762 | 0 | WINPR_DIGEST_CTX* sha1 = NULL; |
763 | 0 | WINPR_DIGEST_CTX* md5 = NULL; |
764 | 0 | WINPR_RC4_CTX* rc4 = NULL; |
765 | 0 | BYTE salt[] = { 0xD1, 0x26, 0x9E }; /* 40 bits: 3 bytes, 56 bits: 1 byte */ |
766 | 0 | BOOL result = FALSE; |
767 | 0 | WLog_DBG(TAG, "updating RDP key"); |
768 | |
|
769 | 0 | if (!(sha1 = winpr_Digest_New())) |
770 | 0 | goto out; |
771 | | |
772 | 0 | if (!winpr_Digest_Init(sha1, WINPR_MD_SHA1)) |
773 | 0 | goto out; |
774 | | |
775 | 0 | if (!winpr_Digest_Update(sha1, update_key, key_len)) |
776 | 0 | goto out; |
777 | | |
778 | 0 | if (!winpr_Digest_Update(sha1, pad1, sizeof(pad1))) |
779 | 0 | goto out; |
780 | | |
781 | 0 | if (!winpr_Digest_Update(sha1, key, key_len)) |
782 | 0 | goto out; |
783 | | |
784 | 0 | if (!winpr_Digest_Final(sha1, sha1h, sizeof(sha1h))) |
785 | 0 | goto out; |
786 | | |
787 | 0 | if (!(md5 = winpr_Digest_New())) |
788 | 0 | goto out; |
789 | | |
790 | 0 | if (!winpr_Digest_Init(md5, WINPR_MD_MD5)) |
791 | 0 | goto out; |
792 | | |
793 | 0 | if (!winpr_Digest_Update(md5, update_key, key_len)) |
794 | 0 | goto out; |
795 | | |
796 | 0 | if (!winpr_Digest_Update(md5, pad2, sizeof(pad2))) |
797 | 0 | goto out; |
798 | | |
799 | 0 | if (!winpr_Digest_Update(md5, sha1h, sizeof(sha1h))) |
800 | 0 | goto out; |
801 | | |
802 | 0 | if (!winpr_Digest_Final(md5, key, WINPR_MD5_DIGEST_LENGTH)) |
803 | 0 | goto out; |
804 | | |
805 | 0 | if (!(rc4 = winpr_RC4_New(key, key_len))) |
806 | 0 | goto out; |
807 | | |
808 | 0 | if (!winpr_RC4_Update(rc4, key_len, key, key)) |
809 | 0 | goto out; |
810 | | |
811 | 0 | if (rdp->settings->EncryptionMethods == ENCRYPTION_METHOD_40BIT) |
812 | 0 | memcpy(key, salt, 3); |
813 | 0 | else if (rdp->settings->EncryptionMethods == ENCRYPTION_METHOD_56BIT) |
814 | 0 | memcpy(key, salt, 1); |
815 | |
|
816 | 0 | result = TRUE; |
817 | 0 | out: |
818 | 0 | winpr_Digest_Free(sha1); |
819 | 0 | winpr_Digest_Free(md5); |
820 | 0 | winpr_RC4_Free(rc4); |
821 | 0 | return result; |
822 | 0 | } |
823 | | |
824 | | BOOL security_encrypt(BYTE* data, size_t length, rdpRdp* rdp) |
825 | 0 | { |
826 | 0 | BOOL rc = FALSE; |
827 | |
|
828 | 0 | WINPR_ASSERT(rdp); |
829 | 0 | if (!rdp->rc4_encrypt_key) |
830 | 0 | { |
831 | 0 | WLog_ERR(TAG, "rdp->rc4_encrypt_key=%p", rdp->rc4_encrypt_key); |
832 | 0 | goto fail; |
833 | 0 | } |
834 | | |
835 | 0 | if (rdp->encrypt_use_count >= 4096) |
836 | 0 | { |
837 | 0 | if (!security_key_update(rdp->encrypt_key, rdp->encrypt_update_key, rdp->rc4_key_len, rdp)) |
838 | 0 | goto fail; |
839 | | |
840 | 0 | if (!rdp_reset_rc4_encrypt_keys(rdp)) |
841 | 0 | goto fail; |
842 | 0 | } |
843 | | |
844 | 0 | if (!winpr_RC4_Update(rdp->rc4_encrypt_key, length, data, data)) |
845 | 0 | goto fail; |
846 | | |
847 | 0 | rdp->encrypt_use_count++; |
848 | 0 | rdp->encrypt_checksum_use_count++; |
849 | 0 | rc = TRUE; |
850 | 0 | fail: |
851 | 0 | return rc; |
852 | 0 | } |
853 | | |
854 | | BOOL security_decrypt(BYTE* data, size_t length, rdpRdp* rdp) |
855 | 0 | { |
856 | 0 | BOOL rc = FALSE; |
857 | |
|
858 | 0 | WINPR_ASSERT(data || (length == 0)); |
859 | 0 | WINPR_ASSERT(rdp); |
860 | | |
861 | 0 | if (!rdp->rc4_decrypt_key) |
862 | 0 | { |
863 | 0 | WLog_ERR(TAG, "rdp->rc4_decrypt_key=%p", rdp->rc4_decrypt_key); |
864 | 0 | goto fail; |
865 | 0 | } |
866 | | |
867 | 0 | if (rdp->decrypt_use_count >= 4096) |
868 | 0 | { |
869 | 0 | if (!security_key_update(rdp->decrypt_key, rdp->decrypt_update_key, rdp->rc4_key_len, rdp)) |
870 | 0 | goto fail; |
871 | | |
872 | 0 | if (!rdp_reset_rc4_decrypt_keys(rdp)) |
873 | 0 | goto fail; |
874 | 0 | } |
875 | | |
876 | 0 | if (!winpr_RC4_Update(rdp->rc4_decrypt_key, length, data, data)) |
877 | 0 | goto fail; |
878 | | |
879 | 0 | rdp->decrypt_use_count += 1; |
880 | 0 | rdp->decrypt_checksum_use_count++; |
881 | 0 | rc = TRUE; |
882 | 0 | fail: |
883 | 0 | if (!rc) |
884 | 0 | WLog_WARN(TAG, "Failed to decrypt security"); |
885 | 0 | return rc; |
886 | 0 | } |
887 | | |
888 | | BOOL security_hmac_signature(const BYTE* data, size_t length, BYTE* output, size_t out_len, |
889 | | rdpRdp* rdp) |
890 | 0 | { |
891 | 0 | BYTE buf[WINPR_SHA1_DIGEST_LENGTH] = { 0 }; |
892 | 0 | BYTE use_count_le[4] = { 0 }; |
893 | 0 | WINPR_HMAC_CTX* hmac = NULL; |
894 | 0 | BOOL result = FALSE; |
895 | |
|
896 | 0 | WINPR_ASSERT(rdp); |
897 | 0 | WINPR_ASSERT(output); |
898 | 0 | WINPR_ASSERT(out_len >= 8); |
899 | | |
900 | 0 | security_UINT32_le(use_count_le, sizeof(use_count_le), rdp->encrypt_use_count); |
901 | |
|
902 | 0 | if (!(hmac = winpr_HMAC_New())) |
903 | 0 | return FALSE; |
904 | | |
905 | 0 | if (!winpr_HMAC_Init(hmac, WINPR_MD_SHA1, rdp->fips_sign_key, WINPR_SHA1_DIGEST_LENGTH)) |
906 | 0 | goto out; |
907 | | |
908 | 0 | if (!winpr_HMAC_Update(hmac, data, length)) |
909 | 0 | goto out; |
910 | | |
911 | 0 | if (!winpr_HMAC_Update(hmac, use_count_le, 4)) |
912 | 0 | goto out; |
913 | | |
914 | 0 | if (!winpr_HMAC_Final(hmac, buf, WINPR_SHA1_DIGEST_LENGTH)) |
915 | 0 | goto out; |
916 | | |
917 | 0 | memmove(output, buf, 8); |
918 | 0 | result = TRUE; |
919 | 0 | out: |
920 | 0 | winpr_HMAC_Free(hmac); |
921 | 0 | return result; |
922 | 0 | } |
923 | | |
924 | | BOOL security_fips_encrypt(BYTE* data, size_t length, rdpRdp* rdp) |
925 | 0 | { |
926 | 0 | BOOL rc = FALSE; |
927 | 0 | size_t olen = 0; |
928 | |
|
929 | 0 | if (!winpr_Cipher_Update(rdp->fips_encrypt, data, length, data, &olen)) |
930 | 0 | goto fail; |
931 | | |
932 | 0 | rdp->encrypt_use_count++; |
933 | 0 | rc = TRUE; |
934 | 0 | fail: |
935 | 0 | return rc; |
936 | 0 | } |
937 | | |
938 | | BOOL security_fips_decrypt(BYTE* data, size_t length, rdpRdp* rdp) |
939 | 0 | { |
940 | 0 | size_t olen = 0; |
941 | |
|
942 | 0 | if (!rdp || !rdp->fips_decrypt) |
943 | 0 | { |
944 | 0 | WLog_ERR(TAG, "rdp=%p, rdp->fips_decrypt=%p", rdp, rdp ? rdp->fips_decrypt : NULL); |
945 | 0 | return FALSE; |
946 | 0 | } |
947 | | |
948 | 0 | if (!winpr_Cipher_Update(rdp->fips_decrypt, data, length, data, &olen)) |
949 | 0 | return FALSE; |
950 | | |
951 | 0 | return TRUE; |
952 | 0 | } |
953 | | |
954 | | BOOL security_fips_check_signature(const BYTE* data, size_t length, const BYTE* sig, size_t sig_len, |
955 | | rdpRdp* rdp) |
956 | 0 | { |
957 | 0 | BYTE buf[WINPR_SHA1_DIGEST_LENGTH] = { 0 }; |
958 | 0 | BYTE use_count_le[4] = { 0 }; |
959 | 0 | WINPR_HMAC_CTX* hmac = NULL; |
960 | 0 | BOOL result = FALSE; |
961 | |
|
962 | 0 | security_UINT32_le(use_count_le, sizeof(use_count_le), rdp->decrypt_use_count++); |
963 | |
|
964 | 0 | if (!(hmac = winpr_HMAC_New())) |
965 | 0 | goto out; |
966 | | |
967 | 0 | if (!winpr_HMAC_Init(hmac, WINPR_MD_SHA1, rdp->fips_sign_key, WINPR_SHA1_DIGEST_LENGTH)) |
968 | 0 | goto out; |
969 | | |
970 | 0 | if (!winpr_HMAC_Update(hmac, data, length)) |
971 | 0 | goto out; |
972 | | |
973 | 0 | if (!winpr_HMAC_Update(hmac, use_count_le, 4)) |
974 | 0 | goto out; |
975 | | |
976 | 0 | if (!winpr_HMAC_Final(hmac, buf, WINPR_SHA1_DIGEST_LENGTH)) |
977 | 0 | goto out; |
978 | | |
979 | 0 | if ((sig_len >= 8) && (memcmp(sig, buf, 8) == 0)) |
980 | 0 | result = TRUE; |
981 | |
|
982 | 0 | out: |
983 | 0 | if (!result) |
984 | 0 | WLog_WARN(TAG, "signature check failed"); |
985 | 0 | winpr_HMAC_Free(hmac); |
986 | 0 | return result; |
987 | 0 | } |
988 | | |
989 | | BOOL security_lock(rdpRdp* rdp) |
990 | 2.85k | { |
991 | 2.85k | WINPR_ASSERT(rdp); |
992 | 2.85k | EnterCriticalSection(&rdp->critical); |
993 | 2.85k | return TRUE; |
994 | 2.85k | } |
995 | | |
996 | | BOOL security_unlock(rdpRdp* rdp) |
997 | 0 | { |
998 | 0 | WINPR_ASSERT(rdp); |
999 | 0 | LeaveCriticalSection(&rdp->critical); |
1000 | 0 | return TRUE; |
1001 | 0 | } |