From 1511f1959b2a48cb2d41550441df366276e3c5cb Mon Sep 17 00:00:00 2001 From: Ondrej Holy Date: Tue, 7 Nov 2017 14:57:12 +0100 Subject: [PATCH] FIPS mode support --- libfreerdp-core/certificate.c | 17 +++++++----- libfreerdp-core/connection.c | 3 +++ libfreerdp-core/crypto.c | 61 ++++++++++++++++++++++++++++++++++++++----- libfreerdp-core/crypto.h | 6 +++-- libfreerdp-core/license.c | 15 ++++++++--- libfreerdp-core/nego.c | 5 +++- libfreerdp-core/security.c | 33 +++++++++++++++++++---- libfreerdp-utils/args.c | 3 --- 8 files changed, 116 insertions(+), 27 deletions(-) diff --git a/libfreerdp-core/certificate.c b/libfreerdp-core/certificate.c index 801ed3609..1fd2cd216 100644 --- a/libfreerdp-core/certificate.c +++ b/libfreerdp-core/certificate.c @@ -276,15 +276,19 @@ static boolean certificate_process_server_public_key(rdpCertificate* certificate static boolean certificate_process_server_public_signature(rdpCertificate* certificate, uint8* sigdata, int sigdatalen, STREAM* s, uint32 siglen) { - uint8 md5hash[CRYPTO_MD5_DIGEST_LENGTH]; + /*uint8 md5hash[CRYPTO_MD5_DIGEST_LENGTH];*/ uint8 encsig[TSSK_KEY_LENGTH + 8]; uint8 sig[TSSK_KEY_LENGTH]; - CryptoMd5 md5ctx; + /*CryptoMd5 md5ctx;*/ int i, sum; - md5ctx = crypto_md5_init(); + /* Do not bother with validation of server proprietary certificate. The use of MD5 here is not allowed under FIPS. */ + /* Since the validation is not protecting against anything since the private/public keys are well known and documented in */ + /* MS-RDPBCGR section 5.3.3.1, we are not gaining any security by using MD5 for signature comparison. Rather then use MD5 */ + /* here we just dont do the validation to avoid its use. Historically, freerdp has been ignoring a failed validation anyways. */ + /*md5ctx = crypto_md5_init(); crypto_md5_update(md5ctx, sigdata, sigdatalen); - crypto_md5_final(md5ctx, md5hash); + crypto_md5_final(md5ctx, md5hash);*/ stream_read(s, encsig, siglen); @@ -304,11 +308,12 @@ static boolean certificate_process_server_public_signature(rdpCertificate* certi crypto_rsa_public_decrypt(encsig, siglen, TSSK_KEY_LENGTH, tssk_modulus, tssk_exponent, sig); /* Verify signature. */ - if (memcmp(md5hash, sig, sizeof(md5hash)) != 0) + /* Do not bother with validation of server proprietary certificate as described above. */ + /*if (memcmp(md5hash, sig, sizeof(md5hash)) != 0) { printf("certificate_process_server_public_signature: invalid signature\n"); //return false; - } + }*/ /* * Verify rest of decrypted data: diff --git a/libfreerdp-core/connection.c b/libfreerdp-core/connection.c index 7e1769ee4..7853c86bd 100644 --- a/libfreerdp-core/connection.c +++ b/libfreerdp-core/connection.c @@ -65,6 +65,9 @@ boolean rdp_client_connect(rdpRdp* rdp) uint32 selectedProtocol; rdpSettings* settings = rdp->settings; + if (FIPS_mode() == 1) + settings->nla_security = false; + nego_init(rdp->nego); nego_set_target(rdp->nego, settings->hostname, settings->port); nego_set_cookie(rdp->nego, settings->username); diff --git a/libfreerdp-core/crypto.c b/libfreerdp-core/crypto.c index a0e2ccb9d..e6115b867 100644 --- a/libfreerdp-core/crypto.c +++ b/libfreerdp-core/crypto.c @@ -37,34 +37,81 @@ void crypto_sha1_final(CryptoSha1 sha1, uint8* out_data) xfree(sha1); } -CryptoMd5 crypto_md5_init(void) +static CryptoMd5 crypto_md5_init_internal(boolean override_fips) { CryptoMd5 md5 = xmalloc(sizeof(*md5)); - MD5_Init(&md5->md5_ctx); + + EVP_MD_CTX_init(&md5->md5_ctx); + if (override_fips) + EVP_MD_CTX_set_flags(&md5->md5_ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); + if (EVP_DigestInit_ex(&md5->md5_ctx, EVP_md5(), NULL) != 1) + { + printf("EVP_DigestInit_ex failed\n"); + abort(); + } + return md5; } +CryptoMd5 crypto_md5_init(void) +{ + return crypto_md5_init_internal(false); +} + +CryptoMd5 crypto_md5_init_allow_fips(void) +{ + return crypto_md5_init_internal(true); +} + void crypto_md5_update(CryptoMd5 md5, const uint8* data, uint32 length) { - MD5_Update(&md5->md5_ctx, data, length); + EVP_DigestUpdate(&md5->md5_ctx, data, length); } void crypto_md5_final(CryptoMd5 md5, uint8* out_data) { - MD5_Final(out_data, &md5->md5_ctx); + EVP_DigestFinal_ex(&md5->md5_ctx, out_data, NULL); xfree(md5); } -CryptoRc4 crypto_rc4_init(const uint8* key, uint32 length) +static CryptoRc4 crypto_rc4_init_internal(const uint8* key, uint32 length, boolean override_fips) { CryptoRc4 rc4 = xmalloc(sizeof(*rc4)); - RC4_set_key(&rc4->rc4_key, length, key); + + EVP_CIPHER_CTX_init(&rc4->rc4_ctx); + if (EVP_EncryptInit_ex(&rc4->rc4_ctx, EVP_rc4(), NULL, NULL, NULL) != 1) + { + printf("EVP_EncryptInit_ex failed\n"); + abort(); + } + + if (override_fips) + EVP_CIPHER_CTX_set_flags(&rc4->rc4_ctx, EVP_CIPH_FLAG_NON_FIPS_ALLOW); + + EVP_CIPHER_CTX_set_key_length(&rc4->rc4_ctx, length); + if (EVP_EncryptInit_ex(&rc4->rc4_ctx, NULL, NULL, key, NULL) != 1) + { + printf("EVP_EncryptInit_ex failed\n"); + abort(); + } + return rc4; } +CryptoRc4 crypto_rc4_init(const uint8* key, uint32 length) +{ + return crypto_rc4_init_internal(key, length, false); +} + +CryptoRc4 crypto_rc4_init_allow_fips(const uint8* key, uint32 length) +{ + return crypto_rc4_init_internal(key, length, true); +} + void crypto_rc4(CryptoRc4 rc4, uint32 length, const uint8* in_data, uint8* out_data) { - RC4(&rc4->rc4_key, length, in_data, out_data); + int outputLength; + EVP_CipherUpdate(&rc4->rc4_ctx, out_data, &outputLength, in_data, length); } void crypto_rc4_free(CryptoRc4 rc4) diff --git a/libfreerdp-core/crypto.h b/libfreerdp-core/crypto.h index 15afca8a4..a595c3831 100644 --- a/libfreerdp-core/crypto.h +++ b/libfreerdp-core/crypto.h @@ -54,12 +54,12 @@ struct crypto_sha1_struct struct crypto_md5_struct { - MD5_CTX md5_ctx; + EVP_MD_CTX md5_ctx; }; struct crypto_rc4_struct { - RC4_KEY rc4_key; + EVP_CIPHER_CTX rc4_ctx; }; struct crypto_des3_struct @@ -86,11 +86,13 @@ void crypto_sha1_final(CryptoSha1 sha1, uint8* out_data); #define CRYPTO_MD5_DIGEST_LENGTH MD5_DIGEST_LENGTH typedef struct crypto_md5_struct* CryptoMd5; CryptoMd5 crypto_md5_init(void); +CryptoMd5 crypto_md5_init_allow_fips(void); void crypto_md5_update(CryptoMd5 md5, const uint8* data, uint32 length); void crypto_md5_final(CryptoMd5 md5, uint8* out_data); typedef struct crypto_rc4_struct* CryptoRc4; CryptoRc4 crypto_rc4_init(const uint8* key, uint32 length); +CryptoRc4 crypto_rc4_init_allow_fips(const uint8* key, uint32 length); void crypto_rc4(CryptoRc4 rc4, uint32 length, const uint8* in_data, uint8* out_data); void crypto_rc4_free(CryptoRc4 rc4); diff --git a/libfreerdp-core/license.c b/libfreerdp-core/license.c index 60b9f9366..7e5734069 100644 --- a/libfreerdp-core/license.c +++ b/libfreerdp-core/license.c @@ -295,7 +295,10 @@ void license_generate_hwid(rdpLicense* license) memset(license->hwid, 0, HWID_LENGTH); mac_address = license->rdp->transport->tcp->mac_address; - md5 = crypto_md5_init(); + /* Allow FIPS override for use of MD5 here, really this does not have to be MD5 as we are just taking a MD5 hash of the 6 bytes of 0's(macAddress) */ + /* and filling in the Data1-Data4 fields of the CLIENT_HARDWARE_ID structure(from MS-RDPELE section 2.2.2.3.1). This is for RDP licensing packets */ + /* which will already be encrypted under FIPS, so the use of MD5 here is not for sensitive data protection. */ + md5 = crypto_md5_init_allow_fips(); crypto_md5_update(md5, mac_address, 6); crypto_md5_final(md5, &license->hwid[HWID_PLATFORM_ID_LENGTH]); } @@ -354,7 +357,10 @@ void license_decrypt_platform_challenge(rdpLicense* license) license->platform_challenge->length = license->encrypted_platform_challenge->length; - rc4 = crypto_rc4_init(license->licensing_encryption_key, LICENSING_ENCRYPTION_KEY_LENGTH); + /* Allow FIPS override for use of RC4 here, this is only used for decrypting the MACData field of the */ + /* Server Platform Challenge packet (from MS-RDPELE section 2.2.2.4). This is for RDP licensing packets */ + /* which will already be encrypted under FIPS, so the use of RC4 here is not for sensitive data protection. */ + rc4 = crypto_rc4_init_allow_fips(license->licensing_encryption_key, LICENSING_ENCRYPTION_KEY_LENGTH); crypto_rc4(rc4, license->encrypted_platform_challenge->length, license->encrypted_platform_challenge->data, @@ -829,8 +835,11 @@ void license_send_platform_challenge_response_packet(rdpLicense* license) security_mac_data(license->mac_salt_key, buffer, length, mac_data); xfree(buffer); + /* Allow FIPS override for use of RC4 here, this is only used for encrypting the EncryptedHWID field of the */ + /* Client Platform Challenge Response packet (from MS-RDPELE section 2.2.2.5). This is for RDP licensing packets */ + /* which will already be encrypted under FIPS, so the use of RC4 here is not for sensitive data protection. */ buffer = (uint8*) xmalloc(HWID_LENGTH); - rc4 = crypto_rc4_init(license->licensing_encryption_key, LICENSING_ENCRYPTION_KEY_LENGTH); + rc4 = crypto_rc4_init_allow_fips(license->licensing_encryption_key, LICENSING_ENCRYPTION_KEY_LENGTH); crypto_rc4(rc4, HWID_LENGTH, license->hwid, buffer); crypto_rc4_free(rc4); diff --git a/libfreerdp-core/nego.c b/libfreerdp-core/nego.c index 7eb810bba..199870408 100644 --- a/libfreerdp-core/nego.c +++ b/libfreerdp-core/nego.c @@ -89,7 +89,10 @@ boolean nego_connect(rdpNego* nego) if(nego->selected_protocol == PROTOCOL_RDP) { nego->transport->settings->encryption = true; - nego->transport->settings->encryption_method = ENCRYPTION_METHOD_40BIT | ENCRYPTION_METHOD_128BIT | ENCRYPTION_METHOD_FIPS; + if (FIPS_mode() != 1) + nego->transport->settings->encryption_method = ENCRYPTION_METHOD_40BIT | ENCRYPTION_METHOD_128BIT | ENCRYPTION_METHOD_FIPS; + else + nego->transport->settings->encryption_method = ENCRYPTION_METHOD_FIPS; nego->transport->settings->encryption_level = ENCRYPTION_LEVEL_CLIENT_COMPATIBLE; } diff --git a/libfreerdp-core/security.c b/libfreerdp-core/security.c index d93c3b9a8..d93390de2 100644 --- a/libfreerdp-core/security.c +++ b/libfreerdp-core/security.c @@ -131,7 +131,10 @@ static void security_salted_hash(uint8* salt, uint8* input, int length, uint8* s crypto_sha1_final(sha1, sha1_digest); /* SaltedHash(Salt, Input, Salt1, Salt2) = MD5(S + SHA1_Digest) */ - md5 = crypto_md5_init(); + /* Allow FIPS override for use of MD5 here, this is used for creating hashes of the premaster_secret and master_secret */ + /* used for RDP licensing as described in MS-RDPELE. This is for RDP licensing packets */ + /* which will already be encrypted under FIPS, so the use of MD5 here is not for sensitive data protection. */ + md5 = crypto_md5_init_allow_fips(); crypto_md5_update(md5, salt, 48); /* Salt (48 bytes) */ crypto_md5_update(md5, sha1_digest, sizeof(sha1_digest)); /* SHA1_Digest */ crypto_md5_final(md5, output); @@ -182,10 +185,24 @@ void security_md5_16_32_32(uint8* in0, uint8* in1, uint8* in2, uint8* output) crypto_md5_final(md5, output); } +void security_md5_16_32_32_allow_fips(uint8* in0, uint8* in1, uint8* in2, uint8* output) +{ + CryptoMd5 md5; + + md5 = crypto_md5_init_allow_fips(); + crypto_md5_update(md5, in0, 16); + crypto_md5_update(md5, in1, 32); + crypto_md5_update(md5, in2, 32); + crypto_md5_final(md5, output); +} + void security_licensing_encryption_key(uint8* session_key_blob, uint8* client_random, uint8* server_random, uint8* output) { /* LicensingEncryptionKey = MD5(Second128Bits(SessionKeyBlob) + ClientRandom + ServerRandom)) */ - security_md5_16_32_32(&session_key_blob[16], client_random, server_random, output); + /* Allow FIPS use of MD5 here, this is just used for creating the licensing encryption key as described in MS-RDPELE. */ + /* This is for RDP licensing packets which will already be encrypted under FIPS, so the use of MD5 here is not for */ + /* sensitive data protection. */ + security_md5_16_32_32_allow_fips(&session_key_blob[16], client_random, server_random, output); } void security_uint32_le(uint8* output, uint32 value) @@ -216,7 +233,10 @@ void security_mac_data(uint8* mac_salt_key, uint8* data, uint32 length, uint8* o crypto_sha1_final(sha1, sha1_digest); /* MacData = MD5(MacSaltKey + pad2 + SHA1_Digest) */ - md5 = crypto_md5_init(); + /* Allow FIPS override for use of MD5 here, this is only used for creating the MACData field of the */ + /* Client Platform Challenge Response packet (from MS-RDPELE section 2.2.2.5). This is for RDP licensing packets */ + /* which will already be encrypted under FIPS, so the use of MD5 here is not for sensitive data protection. */ + md5 = crypto_md5_init_allow_fips(); crypto_md5_update(md5, mac_salt_key, 16); /* MacSaltKey */ crypto_md5_update(md5, pad2, sizeof(pad2)); /* pad2 */ crypto_md5_update(md5, sha1_digest, sizeof(sha1_digest)); /* SHA1_Digest */ @@ -400,9 +420,12 @@ boolean security_establish_keys(uint8* client_random, rdpRdp* rdp) security_md5_16_32_32(&session_key_blob[32], client_random, server_random, rdp->decrypt_key); } else { - security_md5_16_32_32(&session_key_blob[16], client_random, + /* Allow FIPS use of MD5 here, this is just used for generation of the SessionKeyBlob as described in MS-RDPELE. */ + /* This is for RDP licensing packets which will already be encrypted under FIPS, so the use of MD5 here is not */ + /* for sensitive data protection. */ + security_md5_16_32_32_allow_fips(&session_key_blob[16], client_random, server_random, rdp->decrypt_key); - security_md5_16_32_32(&session_key_blob[32], client_random, + security_md5_16_32_32_allow_fips(&session_key_blob[32], client_random, server_random, rdp->encrypt_key); } diff --git a/libfreerdp-utils/args.c b/libfreerdp-utils/args.c index d9b08c41f..333742536 100644 --- a/libfreerdp-utils/args.c +++ b/libfreerdp-utils/args.c @@ -550,9 +550,6 @@ int freerdp_parse_args(rdpSettings* settings, int argc, char** argv, settings->rdp_security = true; settings->tls_security = false; settings->nla_security = false; - settings->encryption = true; - settings->encryption_method = ENCRYPTION_METHOD_40BIT | ENCRYPTION_METHOD_128BIT | ENCRYPTION_METHOD_FIPS; - settings->encryption_level = ENCRYPTION_LEVEL_CLIENT_COMPATIBLE; } else if (strncmp("tls", argv[index], 1) == 0) /* TLS */ { -- 2.15.0