Skip to content

Commit

Permalink
(GH-446) Enable FIPS Compliant Algorithms
Browse files Browse the repository at this point in the history
Chocolatey should function in organizations that require FIPS
compliant algorithms. Although most of what Chocolatey does with the
Crytpo provider is about hashing files and checksums, in the future
that could change, so choco needs to have a feature flip to use a
compliant algorithm.

Provide a helpful warning and error message when the exception
detected is about FIPS. This will help folks running into this error
have a helpful path forward to getting it enabled.

Unfortunately, enabling it by default could have unintended side
effects for existing choco installs that have been tracking current
package files, plus it does have a bit more of a performance
consideration (although still really fast) because it needs to use
P/Invoke methods to use the native Windows systems calls.

To turn it on, run the following command in 0.9.10+:
`choco feature enable -n useFipsCompliantChecksums`
  • Loading branch information
ferventcoder committed Jun 5, 2016
1 parent 21a8101 commit 8fe470b
Show file tree
Hide file tree
Showing 11 changed files with 81 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public abstract class FilesServiceSpecsBase : TinySpec

public override void Context()
{
HashProvider = new CryptoHashProvider(FileSystem, CryptoHashProviderType.Md5);
Service = new FilesService(new XmlService(FileSystem, HashProvider), FileSystem, new CryptoHashProvider(FileSystem, CryptoHashProviderType.Md5));
HashProvider = new CryptoHashProvider(FileSystem);
Service = new FilesService(new XmlService(FileSystem, HashProvider), FileSystem, HashProvider);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ namespace chocolatey.tests.integration.infrastructure.cryptography
using System.IO;
using System.Reflection;
using System.Security.Cryptography;
using chocolatey.infrastructure.app.configuration;
using Should;
using chocolatey.infrastructure.cryptography;
using chocolatey.infrastructure.filesystem;
Expand All @@ -34,7 +35,7 @@ public abstract class CrytpoHashProviderSpecsBase : TinySpec
public override void Context()
{
FileSystem = new DotNetFileSystem();
Provider = new CryptoHashProvider(FileSystem,CryptoHashProviderType.Md5);
Provider = new CryptoHashProvider(FileSystem);
ContextDirectory = FileSystem.combine_paths(FileSystem.get_directory_name(FileSystem.get_current_assembly_path()), "context");
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/chocolatey.tests/chocolatey.tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
<Compile Include="infrastructure\commands\CommandExecutorSpecs.cs" />
<Compile Include="infrastructure\commands\PowershellExecutorSpecs.cs" />
<Compile Include="infrastructure\configuration\ConfigSpecs.cs" />
<Compile Include="infrastructure\cryptography\CrytpoHashProvider.cs" />
<Compile Include="infrastructure\cryptography\CrytpoHashProviderSpecs.cs" />
<Compile Include="infrastructure\events\context\FakeEvent.cs" />
<Compile Include="infrastructure\events\context\FakeSubscriber.cs" />
<Compile Include="infrastructure\events\EventSubscriptionManagerSpecs.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public abstract class CrytpoHashProviderSpecsBase : TinySpec

public override void Context()
{
Provider = new CryptoHashProvider(FileSystem.Object, CryptoHashProviderType.Md5);
Provider = Provider = new CryptoHashProvider(FileSystem.Object);
}
}

Expand All @@ -59,7 +59,7 @@ public override void Because()
[Fact]
public void should_provide_the_correct_hash_based_on_a_checksum()
{
var expected = BitConverter.ToString(MD5.Create().ComputeHash(byteArray)).Replace("-", string.Empty);
var expected = BitConverter.ToString(SHA256.Create().ComputeHash(byteArray)).Replace("-", string.Empty);

result.ShouldEqual(expected);
}
Expand Down
1 change: 1 addition & 0 deletions src/chocolatey/infrastructure.app/ApplicationParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ public static class Features
public static readonly string FailOnInvalidOrMissingLicense = "failOnInvalidOrMissingLicense";
public static readonly string IgnoreInvalidOptionsSwitches = "ignoreInvalidOptionsSwitches";
public static readonly string UsePackageExitCodes = "usePackageExitCodes";
public static readonly string UseFipsCompliantChecksums = "useFipsCompliantChecksums";
}

public static class Messages
Expand Down
27 changes: 27 additions & 0 deletions src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ namespace chocolatey.infrastructure.app.builders
using adapters;
using attributes;
using configuration;
using cryptography;
using domain;
using extractors;
using filesystem;
using information;
Expand Down Expand Up @@ -81,6 +83,30 @@ public static void set_up_configuration(IList<string> args, ChocolateyConfigurat
set_licensed_options(config, license, configFileSettings);
// save all changes if there are any
set_config_file_settings(configFileSettings, xmlService);

if (!config.Features.UseFipsCompliantChecksums)
{
var hashprovider = container.GetInstance<IHashProvider>();
try
{
hashprovider.set_hash_algorithm(CryptoHashProviderType.Md5);
}
catch (Exception ex)
{
if (!config.CommandName.is_equal_to("feature"))
{
if (ex.InnerException != null && ex.InnerException.Message.contains("FIPS"))
{
"chocolatey".Log().Warn(ChocolateyLoggers.Important, @"
FIPS Mode detected - run 'choco feature enable -n {0}'
to use Chocolatey.".format_with(ApplicationParameters.Features.UseFipsCompliantChecksums));
throw new ApplicationException("When FIPS Mode is enabled, Chocolatey requires {0} feature also be enabled.".format_with(ApplicationParameters.Features.UseFipsCompliantChecksums));
}

throw;
}
}
}
}

private static ConfigFileSettings get_config_file_settings(IFileSystem fileSystem, IXmlService xmlService)
Expand Down Expand Up @@ -264,6 +290,7 @@ private static void set_feature_flags(ChocolateyConfiguration config, ConfigFile
config.Features.FailOnInvalidOrMissingLicense = set_feature_flag(ApplicationParameters.Features.FailOnInvalidOrMissingLicense, configFileSettings, defaultEnabled: false, description: "Fail On Invalid Or Missing License - allows knowing when a license is expired or not applied to a machine. Available in 0.9.10+.");
config.Features.IgnoreInvalidOptionsSwitches = set_feature_flag(ApplicationParameters.Features.IgnoreInvalidOptionsSwitches, configFileSettings, defaultEnabled: true, description: "Ignore Invalid Options/Switches - If a switch or option is passed that is not recognized, should choco fail? Available in 0.9.10+.");
config.Features.UsePackageExitCodes = set_feature_flag(ApplicationParameters.Features.UsePackageExitCodes, configFileSettings, defaultEnabled: true, description: "Use Package Exit Codes - Package scripts can provide exit codes. With this on, package exit codes will be what choco uses for exit when non-zero (this value can come from a dependency package). Chocolatey defines valid exit codes as 0, 1605, 1614, 1641, 3010. With this feature off, choco will exit with a 0 or a 1 (matching previous behavior). Available in 0.9.10+.");
config.Features.UseFipsCompliantChecksums = set_feature_flag(ApplicationParameters.Features.UseFipsCompliantChecksums, configFileSettings, defaultEnabled: false, description: "Use FIPS Compliant Checksums - Ensure checksumming done by choco uses FIPS compliant algorithms. Not recommended unless required by FIPS Mode. Enabling on an existing installation could have unintended consequences related to upgrades/uninstalls. Available in 0.9.10+.");
config.PromptForConfirmation = !set_feature_flag(ApplicationParameters.Features.AllowGlobalConfirmation, configFileSettings, defaultEnabled: false, description: "Prompt for confirmation in scripts or bypass.");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ public sealed class FeaturesConfiguration
public bool FailOnInvalidOrMissingLicense { get; set; }
public bool IgnoreInvalidOptionsSwitches { get; set; }
public bool UsePackageExitCodes { get; set; }
public bool UseFipsCompliantChecksums { get; set; }
}

//todo: retrofit other command configs this way
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
namespace chocolatey.infrastructure.app.registration
{
using System.Collections.Generic;
using configuration;
using infrastructure.events;
using infrastructure.tasks;
using NuGet;
Expand Down Expand Up @@ -60,7 +61,7 @@ public void RegisterComponents(Container container)
container.Register<IRegistryService, RegistryService>(Lifestyle.Singleton);
container.Register<IFilesService, FilesService>(Lifestyle.Singleton);
container.Register<IConfigTransformService, ConfigTransformService>(Lifestyle.Singleton);
container.Register<IHashProvider>(() => new CryptoHashProvider(container.GetInstance<IFileSystem>(), CryptoHashProviderType.Md5), Lifestyle.Singleton);
container.Register<IHashProvider>(() => new CryptoHashProvider(container.GetInstance<IFileSystem>()), Lifestyle.Singleton);
container.Register<ITemplateService, TemplateService>(Lifestyle.Singleton);
container.Register<IChocolateyConfigSettingsService, ChocolateyConfigSettingsService>(Lifestyle.Singleton);
container.Register<IChocolateyPackageService, ChocolateyPackageService>(Lifestyle.Singleton);
Expand Down
61 changes: 33 additions & 28 deletions src/chocolatey/infrastructure/cryptography/CryptoHashProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,44 +16,65 @@
namespace chocolatey.infrastructure.cryptography
{
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using adapters;
using app;
using filesystem;
using platforms;
using Environment = System.Environment;
using HashAlgorithm = adapters.HashAlgorithm;

public sealed class CryptoHashProvider : IHashProvider
{
private readonly IFileSystem _fileSystem;
private readonly IHashAlgorithm _hashAlgorithm;
private IHashAlgorithm _hashAlgorithm;
private const int ERROR_LOCK_VIOLATION = 33;
private const int ERROR_SHARING_VIOLATION = 32;

public CryptoHashProvider(IFileSystem fileSystem, CryptoHashProviderType providerType)
public void set_hash_algorithm(CryptoHashProviderType algorithmType)
{
_fileSystem = fileSystem;
_hashAlgorithm = get_hash_algorithm_static(algorithmType);
}

private static IHashAlgorithm get_hash_algorithm_static(CryptoHashProviderType algorithmType)
{

var fipsOnly = false;
try
{
fipsOnly = CryptoConfig.AllowOnlyFipsAlgorithms;
}
catch (Exception ex)
{
"chocolatey".Log().Debug("Unable to get FipsPolicy from CryptoConfig:{0} {1}".format_with(Environment.NewLine, ex.Message));
}

switch (providerType)
HashAlgorithm hashAlgorithm = null;
switch (algorithmType)
{
case CryptoHashProviderType.Md5:
_hashAlgorithm = new HashAlgorithm(MD5.Create());
hashAlgorithm = new HashAlgorithm(MD5.Create());
break;
case CryptoHashProviderType.Sha1:
_hashAlgorithm = new HashAlgorithm(SHA1.Create());
hashAlgorithm = new HashAlgorithm(fipsOnly ? new SHA1Cng() : SHA1.Create());
break;
case CryptoHashProviderType.Sha256:
_hashAlgorithm = new HashAlgorithm(SHA256.Create());
hashAlgorithm = new HashAlgorithm(fipsOnly ? new SHA256Cng() : SHA256.Create());
break;
case CryptoHashProviderType.Sha512:
_hashAlgorithm = new HashAlgorithm(SHA512.Create());
hashAlgorithm = new HashAlgorithm(fipsOnly ? new SHA512Cng() : SHA512.Create());
break;
}

return hashAlgorithm;
}

public CryptoHashProvider(IFileSystem fileSystem)
{
_fileSystem = fileSystem;
set_hash_algorithm(CryptoHashProviderType.Sha256);
}

public CryptoHashProvider(IFileSystem fileSystem, IHashAlgorithm hashAlgorithm)
Expand Down Expand Up @@ -96,27 +117,11 @@ private static bool file_is_locked(Exception exception)

return errorCode == ERROR_SHARING_VIOLATION || errorCode == ERROR_LOCK_VIOLATION;
}



public static string hash_value(string originalText, CryptoHashProviderType providerType)
{
HashAlgorithm hashAlgorithm = null;
switch (providerType)
{
case CryptoHashProviderType.Md5:
hashAlgorithm = new HashAlgorithm(MD5.Create());
break;
case CryptoHashProviderType.Sha1:
hashAlgorithm = new HashAlgorithm(SHA1.Create());
break;
case CryptoHashProviderType.Sha256:
hashAlgorithm = new HashAlgorithm(SHA256.Create());
break;
case CryptoHashProviderType.Sha512:
hashAlgorithm = new HashAlgorithm(SHA512.Create());
break;
}

IHashAlgorithm hashAlgorithm = get_hash_algorithm_static(providerType);
if (hashAlgorithm == null) return string.Empty;

var hash = hashAlgorithm.ComputeHash(Encoding.ASCII.GetBytes(originalText));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ namespace chocolatey.infrastructure.cryptography
{
public enum CryptoHashProviderType
{
Unknown,
Md5,
Sha1,
Sha256,
Sha512
Sha512,
}
}
8 changes: 8 additions & 0 deletions src/chocolatey/infrastructure/cryptography/IHashProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,19 @@

namespace chocolatey.infrastructure.cryptography
{
using adapters;

/// <summary>
/// A hash provider for hashing files
/// </summary>
public interface IHashProvider
{
/// <summary>
/// Changes the algorithm
/// </summary>
/// <param name="algorithmType">Type of the algorithm.</param>
void set_hash_algorithm(CryptoHashProviderType algorithmType);

/// <summary>
/// Returns a hash of the specified file path.
/// </summary>
Expand Down

0 comments on commit 8fe470b

Please sign in to comment.