FIPs and .NET Core
Published Jun 10 2020 12:12 PM 8,584 Views
Microsoft

FIPs stands for Federal Information Processing Standards. It is all about cryptographic algorithms. Chances are, if you are not working on a healthcare or government related system – you’ve never even heard of FIPs, and that’s okay.

 

But what about those that do have to worry about FIPs?

 

Customer Scenario
This particular customer has a large healthcare system – a system that operates under mandates that requires FIPs compliance. Currently it is built on .NET Framework mostly 4.7.2. It consists of server-side NT Services that reach out to external systems to gather data along with a mix of Web Services (WCF & ASMX). They are currently FIPs compliant. They are in the process of migrating these systems to .NET Core – the Web Services will be transformed into .NET Core WebAPI micro-services and the Win32 services to .NET Core. The challenge? How to maintain FIPs compliance with the migration and refactoring process taking place. Although FIPs complaint… it’s something they haven’t had to concern themselves with in a while.

 

Customer Questions

  1. What is FIPs?
  2. What is FIPs “compliance” vs “certification” vs “verification”?
  3. What is “FIPs Policy” and Best Practices?
  4. What methods are available at runtime to detect “FIPs Policy”?
  5. What methods are available to enforce FIPs Policy – at runtime?
    1. Is it possible to (accidentally) bypass FIPs Policy enforcement?
  6. What is available to audit FIPs Policy adherence?

 

What is FIPs?

An implementation of an approved cryptographic algorithm is considered FIPS compliant only if it has been submitted for and has passed National Institute of Standards and Technology (NIST) validation.  There is a program called Cryptographic Module Validation Program (CMVP) which certifies cryptographic modules – for a full list of the modules in Windows 10 (by version) that have been certified go here. A particular implementation of an algorithm that has not been submitted cannot be considered FIPS-compliant even if it produces identical data as a validated implementation of the same algorithm. Turns out, that is a very subtle but important distinction.

 

What is FIPs “compliance” vs “certification” vs “verification”?

 

“FIPS Validated” translates to that the cryptographic module or your service or product that embeds the module has been validated (“certified”) by the CMVP as meeting the FIPS 140-2 requirements.  Basically, FIPS Validated means that a product has been reviewed, tested, and approved by an accredited (NIST approved) testing lab.  That seems to be a very time consuming and expensive process.

 

“FIPS Compliant” is an industry term for IT products that rely on FIPS 140 validated products for cryptographic functionality. In this case – the entire solution or product is not FIPS Validated, but the module it consumes are validated and the developer (and testers) have confirmed only the FIPs Validated modules are being used. Compliant does not mean a product that contains cryptographic modules is validated. Being FIPS compliant means only certain parts of a product have been tested and approved. Yes, that means there could be gaps in the security of the product.  Being FIPs compliant is the first step before FIPs Validation by NIST, and in some cases being FIPs Complaint alone may suffice for the customer’s implementation.

 

“FIPS Verified” – once an implementation has been validated by NIST, they issue a “FIPS Verified” certificate, which makes it eligible for US government procurement and use.

 

What is “FIPs Policy” and Best Practices?

When it comes to Windows and development… “FIPs Policy” has a very specific meaning. It’s referring to the specific “System cryptography: Use FIPS compliant algorithms for encryption, hashing, and signing” Group Policy setting located at:

 

Computer Configuration\Windows Settings\Security Settings\Local Policies\Security Options

 

policy.png

 

Setting this policy to Enabled will set the corresponding Registry key Enabled=1.

 

HKLM\System\CurrentControlSet\Control\Lsa\FIPSAlgorithmPolicy\Enabled

 

Caveat: Having this Policy set to “Enabled” may indeed be required as part of your journey towards “FIPs Compliant”… however – you should be aware that there are dramatic side-effect impacts of implementing this setting and in particular as you migrate towards .NET Core this setting will not give you the piece of mind you are seeking. Be warned!

 

There is an excellent article that explains our recommended best practices and the underlying reasons for those recommendations regarding this FIPs policy.

 

It boils down to… unless you are required (say government or healthcare requirement)… to leave this setting undefined.

 

But in this case – this customer scenario – we are indeed required to have that setting enabled. In addition – we must determine if our migrated solution is FIPs Compliant.

 

What methods are available at runtime to detect “FIPs Policy”? What methods are available to enforce FIPs Policy – at runtime? And finally, is it possible to (accidentally) bypass FIPs Policy enforcement?

 

Let’s combine these questions – as they are all very closely related once we explore.

Okay.. so now we have an idea of what FIPs is and what FIPs Policy is… various articles advise us that we should detect (at runtime in the code) if this FIPs Policy is enabled… and if so – enforce that. Okay… then how? (Asked my frustrated customer). From our new .NET Core implementation… (1) How do we determine if that policy is even enabled? (2) How can we tell what .NET Core algorithms are using underlying FIPs Validated modules?

This is where we journey down the rabbit hole.

Indeed… there is a property you can check at runtime to determine if this FIPs Policy is enabled, but I do NOT recommend using this CryptoConfig.AllowOnlyFipsAlgorithms.

 

I tested it on .NET Framework 4.7.2, 4.8 and .NET Core 3.1 – and what I found was not encouraging in any of those cases.

 

In order to explore the underlying implementation in each case, I stepped into the .NET Framework (and .NET Core) base class libraries. For the .NET Framework to step into the BCLs, I needed DotNet472ZDP for .NET Framework 4.7.2 and DotNet48ZDP2 for 4.8. You can download the zip files and expand them locally. Then you need to  uncheck “Enable Just my Code” and check “Enable .NET Source Stepping” and for Core go ahead and enable source server.

 

In this case I was exploring three different implementations in three different sources – but all had the same identical CS file names (for example Sha512Managed.cs) so what I discovered is Visual Studio caches the source when the names are identical. So while I thought I was examining the BCL for 4.8 and seeing different behavior than 4.7.2 I discovered it was still pointing to 4.7.2 source although targeting 4.8. The only way I found around that was to rename the old CS lib and force VS to prompt for the new source location.

 

source stepping.png

 

 

In .NET Framework 4.7.2 this returns the correct value but apparently this property is only set once –when the process loads. So if you have a long running process, such as a website, and this policy is updated the value will be wrong when you read it – which will lead to unexpected behavior as enforcement relies internally in the BCLs on this one property. The .NET Framework exceptions thrown – depend on this property. What that means is – you can set FIPS Policy to Enabled on a server with an NT Service and Website running – and neither will be FIPs compliant until they are restarted. Not great, but at least we have some degree of runtime errors thrown to protect against using a non-FIPs compliant algorithm.

 

            if (CryptoConfig.AllowOnlyFipsAlgorithms)
                throw new InvalidOperationException(Environment.GetResourceString("Cryptography_NonCompliantFIPSAlgorithm"));
            Contract.EndContractBlock();

 

In .NET Framework 4.8 this property still exists, and the value reading behaves identical to 4.7.2. It is set once and only once when the process loads. However – the difference is the setting is completely ignored. I examined the BCL to find out why:

 

if (CryptoConfig.AllowOnlyFipsAlgorithms && AppContextSwitches.UseLegacyFipsThrow)
                throw new InvalidOperationException(Environment.GetResourceString("Cryptography_NonCompliantFIPSAlgorithm"));
            Contract.EndContractBlock();

 

Ah… so it is referring to a context switch AppContextSwitches.UseLegacyFipsThrow that I had not found documented anywhere previously. If both that and the FIPs Policy are set… then and only then we get the exception. So once we set this property then we get an exception when the Policy is set to enabled:

 

AppContext.SetSwitch("Switch.System.Security.Cryptography.UseLegacyFipsThrow", true);

 

This was still susceptible to the same issue in 4.7.2 where if the process was still running – no error was thrown when the Policy was updated to enabled.

 

Then I tested in .NET Core 3.1. Yes, the CryptoConfig.AllowOnlyFipsAlgorithms property still exists in Core version.  I was not surprised to discover no exception was thrown when using non-FIPs compliant algorithms. We have documented that behavior change in .NET Core we are no longer throwing exceptions “Does not enforce the use of FIPS Approved algorithms or key sizes in .NET Core apps.”.  

 

However, I was a little surprised to discover the property did not return the correct value. It seems it is never set. Therefore… the developer cannot simply read this value to determine if FIPs Policy is enabled - and throw their own exceptions.

 

Instead we went with reading the registry directly and leveraging cache with expiration.

 

        public bool IsFipsPolicyEnabled()
        {
            RegistryKey uac = Registry.LocalMachine.OpenSubKey(@"System\CurrentControlSet\Control\Lsa\FIPSAlgorithmPolicy", true);
            if (uac == null)
            {
                return false;
            }
            return (bool)uac.GetValue("Enabled");
        }

 

So to summarize… there are built-in methods to enforce FIPs compliant behavior at runtime in .NET 4.7.2 by default (though restart your process after a policy change). Same for 4.8 but only with the added the UseFipsLegacyThrow context switch. None in .NET Core. So is it possible to (accidentally) bypass FIPs Policy enforcement? Absolutely! Without allot of work… that will happen by default in .NET Core.

 

But this leads to the next question… and a philosophical question really. Perhaps there is a very good reason for this shift in behavior. Runtime errors are always worse than compile time errors. What possible benefit is there to having a runtime exception thrown for a system that is using a non-FIPs compliant algorithm? Only perhaps if that same system needed to run in both a FIPs compliant and a non-FIPs compliant environment, perhaps. Typically, instead, those issues should be discovered and fixed before release.

 

Also, to make matters even more complex – FIPs compliance isn’t binary. There are cases of the FIPs compliant algorithms – within modules that are already certified – using non-fips compliant (eg SHA1) internally. So the compliance depends on the implementation / usage. In some cases it’s okay (such as for non-secret key hashing).

 

So when it comes to the runtime exceptions and checking for FIPs compliance… now that excellent article makes allot more sense.

 

So let’s look at identifying and correcting these at compile-time instead. That leads us to…

 

What is available to audit FIPs Policy adherence?

I installed the Microsoft.CodeAnalysis.FxCopAnalyzers 3.0 nuget package and set my project’s analysis options to “Entire Solution”. In particular – I was hopeful for the DoNotUseInsecureCryptographicAlgorithms check to cover for FIPs compliant checks. However, I quickly learned that check did not check for all of the non-FIPs compliant algorithms. The full list that it checks is here:

d.png

 

Notability missing, for example, is Sha512Managed. Okay.. let me back up a bit. Let’s review. How do we know what algorithms in .NET Core are FIPs compliant in the first place? I have not found a “blessed” list of such algorithms… probably we need to add official documentation for that. Let’s start with what we do have.

 

Based on the CMVP listing we know specific exports and algorithms in the BCRYPTPRIMITIVES.DLL are FIPs Validated. So based on that – I then stepped into each implementation to confirm that (1) it was calling into BCRYPTPRIMITIVES instead of a managed implementation and that (2) it was calling an algorithm that was certified. This was not easy. The same certified export was used for both FIPs approved and non-FIPs approved algorithms (eg BCryptCreateHash). And some .NET Core implementations of the same algorithm do not use the BCRYPTPRIMITIVES and therefore even though they are the same exact algorithm… they cannot be considered FIPs compliant.

 

de.png

 

 

 

*This is not any official list or statement*
As best I can tell from that exercise … these implementations are FIPs compliant:

HMACSHA1

MACTripleDES

SHA512CryptoServiceProvider

DESCryptoServiceProvider

TripleDESCryptoServiceProvider

DSACryptoServiceProvider

RSACryptoServiceProvider

 

And possibly SHA1CryptoServiceProvider depending on the usage. I could not locate any Roslyn Code Analysis that checked for all cryptographic uses outside that list – on a blocked-list type of approach.

 

So I wrote one – https://github.com/microsoft/Windows-AppConsult-Samples-DesktopBridge/tree/master/Blog-FIPs

 

Now, finally, I have not mentioned much about TLS. Not only is the local cryptographic algorithms used important.. but the protocols and cypher suites used must also be compliant. Fortunately… the Client (and Server) SCHANNEL protocol settings in the Registry were honored and supported and blocked TSL 1.0 and 1.1 in all three of my tests (4.7.2, 4.8 and .NET Core 3.1). In addition you can specify only compliant algorithms in your CipherSuites in either the Registry (SCHANNEL\CipherSuites) or in code. There is enough there to do a separate blog post… but really there is more details out there for that problem, and easier solutions.

 

 

See also

https://github.com/dotnet/runtime/issues/26048
https://docs.microsoft.com/en-us/dotnet/api/system.net.security.ciphersuitespolicy?view=netcore-3.1
https://github.com/dotnet/runtime/issues/26048

 

Version history
Last update:
‎Jun 10 2020 12:12 PM
Updated by: