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