From 1511f1959b2a48cb2d41550441df366276e3c5cb Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
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