Skip to content

Commit 95654bd

Browse files
authored
DGS-22964 Ensure all deps are strongly named (#2548)
* DGS-22964 Ensure all deps are strongly named * Minor cleanup * Upgrade Microsoft.Bcl.Cryptography
1 parent 8ec4129 commit 95654bd

File tree

21 files changed

+1511
-8
lines changed

21 files changed

+1511
-8
lines changed

src/Confluent.SchemaRegistry.Encryption/Confluent.SchemaRegistry.Encryption.csproj

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@
2828
</ItemGroup>
2929

3030
<ItemGroup Condition="'$(TargetFramework)' == 'net462'">
31-
<PackageReference Include="Microsoft.Bcl.Cryptography" Version="10.0.0-preview.6.25358.103" />
31+
<PackageReference Include="Microsoft.Bcl.Cryptography" Version="10.0.0" />
3232
</ItemGroup>
3333

3434
<ItemGroup>
3535
<PackageReference Include="Google.Protobuf" Version="3.26.1" />
36-
<PackageReference Include="HKDF.Standard" Version="2.0.0" />
37-
<PackageReference Include="Miscreant" Version="0.3.3" />
36+
<!-- HKDF.Standard and Miscreant packages are vendored (source copied) instead of referenced as NuGet packages -->
37+
<!-- because they are not strongly named. See Vendored/Miscreant and Vendored/HkdfStandard directories. -->
3838
</ItemGroup>
3939

4040
<ItemGroup>
@@ -45,6 +45,13 @@
4545
<ItemGroup>
4646
<Compile Include="..\Shared\SetEqualityComparer.cs" Link="Includes/SetEqualityComparer.cs"/>
4747
<Compile Include="..\Shared\DictionaryEqualityComparer.cs" Link="Includes/DictionaryEqualityComparer.cs"/>
48-
</ItemGroup>
48+
</ItemGroup>
49+
50+
<!-- Vendored source code for packages that are not strongly named -->
51+
<!-- The .NET SDK automatically includes all .cs files, so we only need to include the license files -->
52+
<ItemGroup>
53+
<None Include="Vendored\Miscreant\LICENSE.txt" Pack="true" PackagePath="Vendored\Miscreant\" />
54+
<None Include="Vendored\HkdfStandard\LICENSE" Pack="true" PackagePath="Vendored\HkdfStandard\" />
55+
</ItemGroup>
4956

5057
</Project>

src/Confluent.SchemaRegistry.Encryption/Cryptor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using System.Security.Cryptography;
44
using Google.Crypto.Tink;
55
using Google.Protobuf;
6-
using Miscreant;
6+
using Confluent.SchemaRegistry.Encryption.Vendored.Miscreant;
77

88
namespace Confluent.SchemaRegistry.Encryption
99
{

src/Confluent.SchemaRegistry.Encryption/KmsClients.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using System.Collections.Generic;
44
using System.IO;
55
using System.Security.Cryptography;
6-
using Miscreant;
6+
using Confluent.SchemaRegistry.Encryption.Vendored.Miscreant;
77

88
namespace Confluent.SchemaRegistry.Encryption
99
{

src/Confluent.SchemaRegistry.Encryption/LocalKmsClient.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using System;
2-
using HkdfStandard;
2+
using Confluent.SchemaRegistry.Encryption.Vendored.HkdfStandard;
33
using System.Security.Cryptography;
44
using System.Text;
55
using System.Threading.Tasks;
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
using System;
2+
using System.Security.Cryptography;
3+
4+
namespace Confluent.SchemaRegistry.Encryption.Vendored.HkdfStandard
5+
{
6+
/// <summary>
7+
/// Simplified HKDF implementation for use in the Confluent Schema Registry Encryption library.
8+
/// This is a subset of the original HKDF.Standard library, containing only the methods needed.
9+
/// Original source: https://github.com/andreimilto/HKDF.Standard
10+
/// </summary>
11+
public static class Hkdf
12+
{
13+
/// <summary>
14+
/// Derives a key from the provided input key material using HKDF.
15+
/// </summary>
16+
/// <param name="hashAlgorithmName">The hash algorithm to use.</param>
17+
/// <param name="ikm">The input key material.</param>
18+
/// <param name="outputLength">The desired length of the output key in bytes.</param>
19+
/// <param name="salt">Optional salt value (defaults to hash output length of zeros if null).</param>
20+
/// <param name="info">Optional context information (defaults to empty if null).</param>
21+
/// <returns>The derived key.</returns>
22+
public static byte[] DeriveKey(HashAlgorithmName hashAlgorithmName, byte[] ikm, int outputLength, byte[] salt = null, byte[] info = null)
23+
{
24+
if (ikm == null)
25+
throw new ArgumentNullException(nameof(ikm));
26+
if (outputLength <= 0)
27+
throw new ArgumentOutOfRangeException(nameof(outputLength));
28+
29+
// Extract
30+
byte[] prk = Extract(hashAlgorithmName, ikm, salt);
31+
32+
// Expand
33+
return Expand(hashAlgorithmName, prk, outputLength, info);
34+
}
35+
36+
/// <summary>
37+
/// HKDF Extract step - extracts a pseudorandom key from input key material.
38+
/// </summary>
39+
private static byte[] Extract(HashAlgorithmName hashAlgorithmName, byte[] ikm, byte[] salt)
40+
{
41+
int hashLength = GetHashLength(hashAlgorithmName);
42+
43+
// Use a salt of HashLen zeros if not provided
44+
if (salt == null || salt.Length == 0)
45+
{
46+
salt = new byte[hashLength];
47+
}
48+
49+
using (var hmac = CreateHMAC(hashAlgorithmName, salt))
50+
{
51+
return hmac.ComputeHash(ikm);
52+
}
53+
}
54+
55+
/// <summary>
56+
/// HKDF Expand step - expands the pseudorandom key to desired length.
57+
/// </summary>
58+
private static byte[] Expand(HashAlgorithmName hashAlgorithmName, byte[] prk, int outputLength, byte[] info)
59+
{
60+
int hashLength = GetHashLength(hashAlgorithmName);
61+
62+
if (prk.Length < hashLength)
63+
throw new ArgumentException("PRK must be at least HashLen bytes.", nameof(prk));
64+
65+
int n = (outputLength + hashLength - 1) / hashLength; // Ceiling division
66+
if (n > 255)
67+
throw new ArgumentOutOfRangeException(nameof(outputLength), "Output length too large.");
68+
69+
if (info == null)
70+
info = new byte[0];
71+
72+
byte[] okm = new byte[outputLength];
73+
byte[] t = new byte[0];
74+
int offset = 0;
75+
76+
using (var hmac = CreateHMAC(hashAlgorithmName, prk))
77+
{
78+
for (byte i = 1; i <= n; i++)
79+
{
80+
byte[] input = new byte[t.Length + info.Length + 1];
81+
Buffer.BlockCopy(t, 0, input, 0, t.Length);
82+
Buffer.BlockCopy(info, 0, input, t.Length, info.Length);
83+
input[input.Length - 1] = i;
84+
85+
t = hmac.ComputeHash(input);
86+
87+
int bytesToCopy = Math.Min(t.Length, outputLength - offset);
88+
Buffer.BlockCopy(t, 0, okm, offset, bytesToCopy);
89+
offset += bytesToCopy;
90+
}
91+
}
92+
93+
return okm;
94+
}
95+
96+
/// <summary>
97+
/// Gets the hash length in bytes for the specified algorithm.
98+
/// </summary>
99+
private static int GetHashLength(HashAlgorithmName hashAlgorithmName)
100+
{
101+
if (hashAlgorithmName == HashAlgorithmName.SHA1)
102+
return 20;
103+
if (hashAlgorithmName == HashAlgorithmName.SHA256)
104+
return 32;
105+
if (hashAlgorithmName == HashAlgorithmName.SHA384)
106+
return 48;
107+
if (hashAlgorithmName == HashAlgorithmName.SHA512)
108+
return 64;
109+
110+
throw new ArgumentOutOfRangeException(nameof(hashAlgorithmName), "Unsupported hash algorithm.");
111+
}
112+
113+
/// <summary>
114+
/// Creates an HMAC instance for the specified algorithm with the given key.
115+
/// </summary>
116+
private static HMAC CreateHMAC(HashAlgorithmName hashAlgorithmName, byte[] key)
117+
{
118+
if (hashAlgorithmName == HashAlgorithmName.SHA1)
119+
return new HMACSHA1(key);
120+
if (hashAlgorithmName == HashAlgorithmName.SHA256)
121+
return new HMACSHA256(key);
122+
if (hashAlgorithmName == HashAlgorithmName.SHA384)
123+
return new HMACSHA384(key);
124+
if (hashAlgorithmName == HashAlgorithmName.SHA512)
125+
return new HMACSHA512(key);
126+
127+
throw new ArgumentOutOfRangeException(nameof(hashAlgorithmName), "Unsupported hash algorithm.");
128+
}
129+
}
130+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020 Andrei Milto
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
using System;
2+
using System.Security.Cryptography;
3+
4+
namespace Confluent.SchemaRegistry.Encryption.Vendored.Miscreant
5+
{
6+
/// <summary>
7+
/// The Aead class provides authenticated encryption with associated
8+
/// data. This class provides a high-level interface to Miscreant's
9+
/// misuse-resistant encryption.
10+
/// </summary>
11+
public sealed class Aead : IDisposable
12+
{
13+
private readonly AesSiv siv;
14+
private bool disposed;
15+
16+
private Aead(AesSiv siv)
17+
{
18+
this.siv = siv;
19+
}
20+
21+
/// <summary>
22+
/// Generates a random nonce.
23+
/// </summary>
24+
/// <param name="size">Nonce size in bytes.</param>
25+
/// <returns>Generated nonce.</returns>
26+
public static byte[] GenerateNonce(int size)
27+
{
28+
if (size < Constants.BlockSize)
29+
{
30+
throw new CryptographicException("Nonce size is too small.");
31+
}
32+
33+
return Utils.GetRandomBytes(size);
34+
}
35+
36+
/// <summary>
37+
/// Generates a random 32-byte encryption key.
38+
/// </summary>
39+
/// <returns>Generated key.</returns>
40+
public static byte[] GenerateKey256()
41+
{
42+
return Utils.GetRandomBytes(Constants.AesSiv256KeySize);
43+
}
44+
45+
/// <summary>
46+
/// Generates a random 64-byte encryption key.
47+
/// </summary>
48+
/// <returns>Generated key.</returns>
49+
public static byte[] GenerateKey512()
50+
{
51+
return Utils.GetRandomBytes(Constants.AesSiv512KeySize);
52+
}
53+
54+
/// <summary>
55+
/// Initializes a new AEAD instance using the AES-CMAC-SIV algorithm.
56+
/// </summary>
57+
/// <param name="key">The secret key for AES-CMAC-SIV encryption.</param>
58+
/// <returns>An AEAD instance.</returns>
59+
public static Aead CreateAesCmacSiv(byte[] key)
60+
{
61+
return new Aead(AesSiv.CreateAesCmacSiv(key));
62+
}
63+
64+
/// <summary>
65+
/// Initializes a new AEAD instance using the AES-PMAC-SIV algorithm.
66+
/// </summary>
67+
/// <param name="key">The secret key for AES-PMAC-SIV encryption.</param>
68+
/// <returns>An AEAD instance.</returns>
69+
public static Aead CreateAesPmacSiv(byte[] key)
70+
{
71+
return new Aead(AesSiv.CreateAesPmacSiv(key));
72+
}
73+
74+
/// <summary>
75+
/// Seal encrypts and authenticates plaintext, authenticates
76+
/// the associated data, and returns the result.
77+
/// </summary>
78+
/// <param name="plaintext">The plaintext to encrypt.</param>
79+
/// <param name="nonce">The nonce for encryption.</param>
80+
/// <param name="data">Associated data to authenticate.</param>
81+
/// <returns>Concatenation of the authentication tag and the encrypted data.</returns>
82+
public byte[] Seal(byte[] plaintext, byte[] nonce = null, byte[] data = null)
83+
{
84+
return siv.Seal(plaintext, data, nonce);
85+
}
86+
87+
/// <summary>
88+
/// Open decrypts ciphertext, authenticates the decrypted plaintext
89+
/// and the associated data and, if successful, returns the result.
90+
/// In case of failed decryption, this method throws
91+
/// <see cref="CryptographicException"/>.
92+
/// </summary>
93+
/// <param name="ciphertext">The ciphertext to decrypt.</param>
94+
/// <param name="nonce">The nonce for encryption.</param>
95+
/// <param name="data">Associated data to authenticate.</param>
96+
/// <returns>The decrypted plaintext.</returns>
97+
/// <exception cref="CryptographicException">Thrown when the ciphertext is invalid.</exception>
98+
public byte[] Open(byte[] ciphertext, byte[] nonce = null, byte[] data = null)
99+
{
100+
return siv.Open(ciphertext, data, nonce);
101+
}
102+
103+
/// <summary>
104+
/// Disposes this object.
105+
/// </summary>
106+
public void Dispose()
107+
{
108+
if (!disposed)
109+
{
110+
siv.Dispose();
111+
disposed = true;
112+
}
113+
}
114+
}
115+
}

0 commit comments

Comments
 (0)