Skip to content

Commit

Permalink
SNOW-1757860 Fips compliant GCM encryption
Browse files Browse the repository at this point in the history
  • Loading branch information
sfc-gh-knozderko committed Nov 14, 2024
1 parent 05da00a commit a346138
Show file tree
Hide file tree
Showing 16 changed files with 740 additions and 58 deletions.
149 changes: 105 additions & 44 deletions Snowflake.Data.Tests/UnitTests/GcmEncryptionProviderTest.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using NUnit.Framework;
using Org.BouncyCastle.Crypto;
using Snowflake.Data.Core;
using Snowflake.Data.Core.FileTransfer;
using Snowflake.Data.Tests.Util;

namespace Snowflake.Data.Tests.UnitTests
{
[TestFixture]
public class GcmEncryptionProviderTest
internal class GcmEncryptionProviderTest
{
private const string PlainText = "there is no rose without thorns";
private static readonly byte[] s_plainTextBytes = Encoding.UTF8.GetBytes(PlainText);
Expand All @@ -36,24 +36,26 @@ public class GcmEncryptionProviderTest
queryId = s_queryId,
smkId = SmkId
};
private static readonly FileTransferConfiguration s_fileTransferConfiguration = new FileTransferConfiguration
internal static readonly FileTransferConfiguration s_fileTransferConfiguration = new FileTransferConfiguration
{
TempDir = Path.GetTempPath(),
MaxBytesInMemory = FileTransferConfiguration.DefaultMaxBytesInMemory
};

[Test]
public void TestEncryptAndDecryptWithoutAad()
[TestCaseSource(nameof(EncryptionTestCases))]
public void TestEncryptAndDecryptWithoutAad(
IEncryptionProvider encryptionProvider,
IDecryptionProvider decryptionProvider)
{
// arrange
SFEncryptionMetadata encryptionMetadata = new SFEncryptionMetadata();

// act
using (var encryptedStream = GcmEncryptionProvider.Encrypt(
using (var encryptedStream = encryptionProvider.Encrypt(
s_encryptionMaterial,
encryptionMetadata, // this is output parameter
s_fileTransferConfiguration,
new MemoryStream(s_plainTextBytes),
s_plainTextBytes,
null,
null))
{
Expand All @@ -67,7 +69,7 @@ public void TestEncryptAndDecryptWithoutAad()
Assert.IsNull(encryptionMetadata.aad);

// act
using (var decryptedStream = GcmEncryptionProvider.Decrypt(new MemoryStream(encryptedContent), s_encryptionMaterial, encryptionMetadata, s_fileTransferConfiguration))
using (var decryptedStream = decryptionProvider.Decrypt(encryptedContent, s_encryptionMaterial, encryptionMetadata))
{
// assert
var decryptedText = ExtractContent(decryptedStream);
Expand All @@ -77,17 +79,19 @@ public void TestEncryptAndDecryptWithoutAad()
}

[Test]
public void TestEncryptAndDecryptWithEmptyAad()
[TestCaseSource(nameof(EncryptionTestCases))]
public void TestEncryptAndDecryptWithEmptyAad(
IEncryptionProvider encryptionProvider,
IDecryptionProvider decryptionProvider)
{
// arrange
SFEncryptionMetadata encryptionMetadata = new SFEncryptionMetadata();

// act
using (var encryptedStream = GcmEncryptionProvider.Encrypt(
using (var encryptedStream = encryptionProvider.Encrypt(
s_encryptionMaterial,
encryptionMetadata, // this is output parameter
s_fileTransferConfiguration,
new MemoryStream(s_plainTextBytes),
s_plainTextBytes,
s_emptyAadBytes,
s_emptyAadBytes))
{
Expand All @@ -101,7 +105,7 @@ public void TestEncryptAndDecryptWithEmptyAad()
Assert.AreEqual(s_emptyAadBase64, encryptionMetadata.aad);

// act
using (var decryptedStream = GcmEncryptionProvider.Decrypt(new MemoryStream(encryptedContent), s_encryptionMaterial, encryptionMetadata, s_fileTransferConfiguration))
using (var decryptedStream = decryptionProvider.Decrypt(encryptedContent, s_encryptionMaterial, encryptionMetadata))
{
// assert
var decryptedText = ExtractContent(decryptedStream);
Expand All @@ -111,17 +115,19 @@ public void TestEncryptAndDecryptWithEmptyAad()
}

[Test]
public void TestEncryptAndDecryptWithAad()
[TestCaseSource(nameof(EncryptionTestCases))]
public void TestEncryptAndDecryptWithAad(
IEncryptionProvider encryptionProvider,
IDecryptionProvider decryptionProvider)
{
// arrange
SFEncryptionMetadata encryptionMetadata = new SFEncryptionMetadata();

// act
using (var encryptedStream = GcmEncryptionProvider.Encrypt(
using (var encryptedStream = encryptionProvider.Encrypt(
s_encryptionMaterial,
encryptionMetadata, // this is output parameter
s_fileTransferConfiguration,
new MemoryStream(s_plainTextBytes),
s_plainTextBytes,
s_contentAadBytes,
s_keyAadBytes))
{
Expand All @@ -135,7 +141,7 @@ public void TestEncryptAndDecryptWithAad()
CollectionAssert.AreEqual(s_contentAadBase64, encryptionMetadata.aad);

// act
using (var decryptedStream = GcmEncryptionProvider.Decrypt(new MemoryStream(encryptedContent), s_encryptionMaterial, encryptionMetadata, s_fileTransferConfiguration))
using (var decryptedStream = decryptionProvider.Decrypt(encryptedContent, s_encryptionMaterial, encryptionMetadata))
{
// assert
var decryptedText = ExtractContent(decryptedStream);
Expand All @@ -145,15 +151,17 @@ public void TestEncryptAndDecryptWithAad()
}

[Test]
public void TestFailDecryptWithInvalidKeyAad()
[TestCaseSource(nameof(EncryptionTestCases))]
public void TestFailDecryptWithInvalidKeyAad(
IEncryptionProvider encryptionProvider,
IDecryptionProvider decryptionProvider)
{
// arrange
SFEncryptionMetadata encryptionMetadata = new SFEncryptionMetadata();
using (var encryptedStream = GcmEncryptionProvider.Encrypt(
using (var encryptedStream = encryptionProvider.Encrypt(
s_encryptionMaterial,
encryptionMetadata, // this is output parameter
s_fileTransferConfiguration,
new MemoryStream(s_plainTextBytes),
s_plainTextBytes,
null,
s_keyAadBytes))
{
Expand All @@ -166,24 +174,27 @@ public void TestFailDecryptWithInvalidKeyAad()
encryptionMetadata.keyAad = s_invalidAadBase64;

// act
var thrown = Assert.Throws<InvalidCipherTextException>(() =>
GcmEncryptionProvider.Decrypt(new MemoryStream(encryptedContent), s_encryptionMaterial, encryptionMetadata, s_fileTransferConfiguration));
var thrown = Assert.Catch<Exception>(() =>
decryptionProvider.Decrypt(encryptedContent, s_encryptionMaterial, encryptionMetadata));

// assert
Assert.AreEqual("mac check in GCM failed", thrown.Message);
Assert.NotNull(thrown);
Assert.AreEqual(decryptionProvider.ExpectedExceptionMessage(), thrown.Message);
}
}

[Test]
public void TestFailDecryptWithInvalidContentAad()
[TestCaseSource(nameof(EncryptionTestCases))]
public void TestFailDecryptWithInvalidContentAad(
IEncryptionProvider encryptionProvider,
IDecryptionProvider decryptionProvider)
{
// arrange
SFEncryptionMetadata encryptionMetadata = new SFEncryptionMetadata();
using (var encryptedStream = GcmEncryptionProvider.Encrypt(
using (var encryptedStream = encryptionProvider.Encrypt(
s_encryptionMaterial,
encryptionMetadata, // this is output parameter
s_fileTransferConfiguration,
new MemoryStream(s_plainTextBytes),
s_plainTextBytes,
s_contentAadBytes,
null))
{
Expand All @@ -196,24 +207,27 @@ public void TestFailDecryptWithInvalidContentAad()
encryptionMetadata.aad = s_invalidAadBase64;

// act
var thrown = Assert.Throws<InvalidCipherTextException>(() =>
GcmEncryptionProvider.Decrypt(new MemoryStream(encryptedContent), s_encryptionMaterial, encryptionMetadata, s_fileTransferConfiguration));
var thrown = Assert.Catch<Exception>(() =>
decryptionProvider.Decrypt(encryptedContent, s_encryptionMaterial, encryptionMetadata));

// assert
Assert.AreEqual("mac check in GCM failed", thrown.Message);
Assert.NotNull(thrown);
Assert.AreEqual(decryptionProvider.ExpectedExceptionMessage(), thrown.Message);
}
}

[Test]
public void TestFailDecryptWhenMissingAad()
[TestCaseSource(nameof(EncryptionTestCases))]
public void TestFailDecryptWhenMissingAad(
IEncryptionProvider encryptionProvider,
IDecryptionProvider decryptionProvider)
{
// arrange
SFEncryptionMetadata encryptionMetadata = new SFEncryptionMetadata();
using (var encryptedStream = GcmEncryptionProvider.Encrypt(
using (var encryptedStream = encryptionProvider.Encrypt(
s_encryptionMaterial,
encryptionMetadata, // this is output parameter
s_fileTransferConfiguration,
new MemoryStream(s_plainTextBytes),
s_plainTextBytes,
s_contentAadBytes,
s_keyAadBytes))
{
Expand All @@ -227,16 +241,20 @@ public void TestFailDecryptWhenMissingAad()
encryptionMetadata.aad = null;

// act
var thrown = Assert.Throws<InvalidCipherTextException>(() =>
GcmEncryptionProvider.Decrypt(new MemoryStream(encryptedContent), s_encryptionMaterial, encryptionMetadata,s_fileTransferConfiguration));
var thrown = Assert.Catch<Exception>(() =>
decryptionProvider.Decrypt(encryptedContent, s_encryptionMaterial, encryptionMetadata));

// assert
Assert.AreEqual("mac check in GCM failed", thrown.Message);
Assert.NotNull(thrown);
Assert.AreEqual(decryptionProvider.ExpectedExceptionMessage(), thrown.Message);
}
}

[Test]
public void TestEncryptAndDecryptFile()
[TestCaseSource(nameof(EncryptionTestCases))]
public void TestEncryptAndDecryptFile(
IEncryptionProvider encryptionProvider,
IDecryptionProvider decryptionProvider)
{
// arrange
SFEncryptionMetadata encryptionMetadata = new SFEncryptionMetadata();
Expand All @@ -247,8 +265,8 @@ public void TestEncryptAndDecryptFile()
CreateFile(plainTextFilePath, PlainText);

// act
using (var encryptedStream = GcmEncryptionProvider.EncryptFile(plainTextFilePath, s_encryptionMaterial, encryptionMetadata,
s_fileTransferConfiguration, s_contentAadBytes, s_keyAadBytes))
using (var encryptedStream = encryptionProvider.EncryptFile(plainTextFilePath, s_encryptionMaterial, encryptionMetadata,
s_contentAadBytes, s_keyAadBytes))
{
CreateFile(encryptedFilePath, encryptedStream);
}
Expand All @@ -262,8 +280,7 @@ public void TestEncryptAndDecryptFile()

// act
string result;
using (var decryptedStream = GcmEncryptionProvider.DecryptFile(encryptedFilePath, s_encryptionMaterial, encryptionMetadata,
s_fileTransferConfiguration))
using (var decryptedStream = decryptionProvider.DecryptFile(encryptedFilePath, s_encryptionMaterial, encryptionMetadata))
{
decryptedStream.Position = 0;
var resultBytes = new byte[decryptedStream.Length];
Expand Down Expand Up @@ -314,5 +331,49 @@ private byte[] ExtractContentBytes(Stream stream)
stream.CopyTo(memoryStream);
return memoryStream.ToArray();
}

internal static IEnumerable<object[]> EncryptionTestCases()
{
yield return new object[]
{
BouncyCastleEncryption.Instance,
BouncyCastleDecryption.Instance
};
// tests for BouncyCastle fips
yield return new object[]
{
BouncyCastleFipsEncryption.Instance,
BouncyCastleFipsDecryption.Instance
};
yield return new object[]
{
BouncyCastleEncryption.Instance,
BouncyCastleFipsDecryption.Instance
};
yield return new object[]
{
BouncyCastleFipsEncryption.Instance,
BouncyCastleDecryption.Instance
};

#if NETSTANDARD2_1
// tests for AesGcm
yield return new object[]
{
AesGcmEncryption.Instance,
AesGcmDecryption.Instance
};
yield return new object[]
{
BouncyCastleEncryption.Instance,
AesGcmDecryption.Instance
};
yield return new object[]
{
AesGcmEncryption.Instance,
BouncyCastleDecryption.Instance
};
#endif
}
}
}
37 changes: 37 additions & 0 deletions Snowflake.Data.Tests/Util/Encryption/AesGcmDecryption.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#if NETSTANDARD2_1
using System.IO;
using Snowflake.Data.Core;
using Snowflake.Data.Core.FileTransfer;

namespace Snowflake.Data.Tests.Util
{
internal class AesGcmDecryption : IDecryptionProvider
{
public static readonly AesGcmDecryption Instance = new AesGcmDecryption();

public Stream DecryptFile(
string inFile,
PutGetEncryptionMaterial encryptionMaterial,
SFEncryptionMetadata encryptionMetadata)
{
return AesGcmEncryptionProvider.DecryptFile(
inFile,
encryptionMaterial,
encryptionMetadata);
}

public Stream Decrypt(
byte[] inputBytes,
PutGetEncryptionMaterial encryptionMaterial,
SFEncryptionMetadata encryptionMetadata)
{
return AesGcmEncryptionProvider.Decrypt(
inputBytes,
encryptionMaterial,
encryptionMetadata);
}

public string ExpectedExceptionMessage() => "The computed authentication tag did not match the input authentication tag.";
}
}
#endif
44 changes: 44 additions & 0 deletions Snowflake.Data.Tests/Util/Encryption/AesGcmEncryption.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#if NETSTANDARD2_1
using System.IO;
using Snowflake.Data.Core;
using Snowflake.Data.Core.FileTransfer;

namespace Snowflake.Data.Tests.Util
{
internal class AesGcmEncryption : IEncryptionProvider
{
public static readonly AesGcmEncryption Instance = new AesGcmEncryption();

public Stream EncryptFile(
string inFile,
PutGetEncryptionMaterial encryptionMaterial,
SFEncryptionMetadata encryptionMetadata,
byte[] contentAad,
byte[] keyAad
)
{
return AesGcmEncryptionProvider.EncryptFile(
inFile,
encryptionMaterial,
encryptionMetadata,
contentAad,
keyAad);
}

public Stream Encrypt(
PutGetEncryptionMaterial encryptionMaterial,
SFEncryptionMetadata encryptionMetadata,
byte[] inputBytes,
byte[] contentAad,
byte[] keyAad)
{
return AesGcmEncryptionProvider.Encrypt(
encryptionMaterial,
encryptionMetadata,
inputBytes,
contentAad,
keyAad);
}
}
}
#endif
Loading

0 comments on commit a346138

Please sign in to comment.