Skip to content

Commit

Permalink
Replaced System.Security.Cryptograpy with Portable BouncyCastle
Browse files Browse the repository at this point in the history
  • Loading branch information
maxirmx committed Jul 9, 2024
1 parent e6c30a0 commit 9411d47
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 90 deletions.
34 changes: 21 additions & 13 deletions dkgLibrary/poly/Share.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,11 @@
// scheme allows a committer to commit to a secret sharing polynomial so that
// a verifier can check the claimed evaluations of the committed polynomial.

using SHA256 = System.Security.Cryptography.SHA256;

using System.Runtime.CompilerServices;
using dkg.group;
using Org.BouncyCastle.Asn1.Ocsp;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;


[assembly: InternalsVisibleTo("dkgLibraryTests")]

Expand Down Expand Up @@ -71,14 +70,14 @@ public virtual void UnmarshalBinary(Stream s)

public virtual byte[] GetBytes()
{
using MemoryStream stream = new MemoryStream();
using MemoryStream stream = new();
MarshalBinary(stream);
return stream.ToArray();
}

public virtual void SetBytes(byte[] bytes)
{
using MemoryStream stream = new MemoryStream(bytes);
using MemoryStream stream = new(bytes);
UnmarshalBinary(stream);
}

Expand All @@ -94,6 +93,7 @@ public override int GetHashCode()
return I * 31;
}
}

}

public class ShareComparer : IComparer<Share>
Expand Down Expand Up @@ -132,10 +132,14 @@ public bool Equals(PriShare? other)
// Returns the hash representation of this share
public byte[] Hash()
{
var h = SHA256.HashData(V.GetBytes());
var iBytes = BitConverter.GetBytes(I);
h = [.. h, .. iBytes];
return h;
Sha256Digest digest = new();
byte[] vBytes = V.GetBytes();
digest.BlockUpdate(vBytes, 0, vBytes.Length);
byte[] iBytes = BitConverter.GetBytes(I);
digest.BlockUpdate(iBytes, 0, iBytes.Length);
byte[] result = new byte[digest.GetDigestSize()];
digest.DoFinal(result, 0);
return result;
}

public override void MarshalBinary(Stream s)
Expand Down Expand Up @@ -201,10 +205,14 @@ public bool Equals(PubShare? other)
// Hash returns the hash representation of this share.
public byte[] Hash()
{
var h = SHA256.HashData(V.GetBytes());
var iBytes = BitConverter.GetBytes(I);
h = [.. h, .. iBytes];
return h;
Sha256Digest digest = new();
byte[] vBytes = V.GetBytes();
digest.BlockUpdate(vBytes, 0, vBytes.Length);
byte[] iBytes = BitConverter.GetBytes(I);
digest.BlockUpdate(iBytes, 0, iBytes.Length);
byte[] result = new byte[digest.GetDigestSize()];
digest.DoFinal(result, 0);
return result;
}
public override void MarshalBinary(Stream s)
{
Expand Down
66 changes: 55 additions & 11 deletions dkgLibrary/util/DhHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,19 @@

using dkg.group;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using System.Security.Cryptography;
using System.Text;

namespace dkg.util
{
public static class DhHelper
{
public const int sharedKeySize = 32;
public const int nonceSizeInBytes = 12;
public const int tagSizeInBytes = 16;
public const int SharedKeySize = 32;
public const int NonceSizeInBytes = 12;
public const int TagSizeInBytes = 16;

// dhExchange computes the shared key from a private key and a public key
public static IPoint DhExchange(IScalar ownPrivate, IPoint remotePublic)
Expand All @@ -45,12 +46,12 @@ public static IPoint DhExchange(IScalar ownPrivate, IPoint remotePublic)
}

// CreateAEAD returns the AEAD cipher to be used to encrypt a share
public static AesGcm CreateAEAD(IPoint preSharedKey, byte[] hkdfContext)
public static GcmBlockCipher CreateAEAD(bool mode, IPoint preSharedKey, byte[] hkdfContext, byte[] nonce)
{
var sharedKey = CreateHKDF(preSharedKey.GetBytes(), hkdfContext);
var aes = Aes.Create();
aes.Key = sharedKey;
return new AesGcm(aes.Key, tagSizeInBytes);
var cipher = new GcmBlockCipher(new AesEngine());
cipher.Init(mode, new AeadParameters(new KeyParameter(sharedKey), TagSizeInBytes * 8, nonce));
return cipher;
}

public static byte[] CreateHKDF(byte[] preSharedKey, byte[] hkdfContext)
Expand All @@ -62,8 +63,8 @@ public static byte[] CreateHKDF(byte[] preSharedKey, byte[] hkdfContext)
hkdf.Init(new HkdfParameters(preSharedKey, hkdfContext, null));

// Generate shared key
byte[] sharedKey = new byte[sharedKeySize];
hkdf.GenerateBytes(sharedKey, 0, sharedKeySize);
byte[] sharedKey = new byte[SharedKeySize];
hkdf.GenerateBytes(sharedKey, 0, SharedKeySize);

return sharedKey;
}
Expand All @@ -79,7 +80,50 @@ public static byte[] Context(IPoint publicKey, IPoint[] verifiers)
{
vrf.MarshalBinary(strm);
}
return SHA256.HashData(strm.ToArray());
// Use BouncyCastle's SHA256 implementation
var digest = new Sha256Digest();
var result = new byte[digest.GetDigestSize()];
var data = strm.ToArray();
digest.BlockUpdate(data, 0, data.Length);
digest.DoFinal(result, 0);
return result;
}
public static void Encrypt(GcmBlockCipher cipher, byte[] plaintext, out byte[] encrypted, out byte[] tag)
{
// Allocate space for the ciphertext, which may be slightly larger than the plaintext
encrypted = new byte[cipher.GetOutputSize(plaintext.Length)];

// Process the plaintext bytes through the cipher
int len = cipher.ProcessBytes(plaintext, 0, plaintext.Length, encrypted, 0);
len += cipher.DoFinal(encrypted, len);


tag = new byte[TagSizeInBytes];

// Extract the tag from the end of the encrypted array
Array.Copy(encrypted, len - TagSizeInBytes, tag, 0, TagSizeInBytes);

// Resize the encrypted array to exclude the tag
byte[] tempEncrypted = new byte[len - TagSizeInBytes];
Array.Copy(encrypted, 0, tempEncrypted, 0, len - TagSizeInBytes);
encrypted = tempEncrypted;
}

public static void Decrypt(GcmBlockCipher cipher, byte[] encrypted, byte[] tag, out byte[] decrypted)
{
// Combine the encrypted data and tag to conform with how GCMBlockCipher expects its input
byte[] encryptedWithTag = new byte[encrypted.Length + tag.Length];
Array.Copy(encrypted, 0, encryptedWithTag, 0, encrypted.Length);
Array.Copy(tag, 0, encryptedWithTag, encrypted.Length, tag.Length);

// Allocate space for decryption
decrypted = new byte[cipher.GetOutputSize(encryptedWithTag.Length)];


// Decrypt the data
int len = cipher.ProcessBytes(encryptedWithTag, 0, encryptedWithTag.Length, decrypted, 0);
cipher.DoFinal(decrypted, len);
}
}
}

12 changes: 4 additions & 8 deletions dkgLibrary/util/ECElGamal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,9 @@ public static (IPoint C1, IPoint C2) Encrypt(IGroup G, IPoint publicKey, byte[]
return (C1, C2);
}

public static byte[] DecryptData(IGroup G, IScalar privateKey, (IPoint C1, IPoint C2) cipher)
public static byte[] DecryptData(IScalar privateKey, (IPoint C1, IPoint C2) cipher)
{
Secp256k1Point? M = cipher.C2.Sub(cipher.C1.Mul(privateKey)) as Secp256k1Point;
if (M == null)
{
throw new InvalidCastException("Decryption failed: cypher type does not match");
}
Secp256k1Point? M = cipher.C2.Sub(cipher.C1.Mul(privateKey)) as Secp256k1Point ?? throw new InvalidCastException("Decryption failed: cypher type does not match");
byte[] plainBytes = M.ExtractData();
return plainBytes;
}
Expand All @@ -75,9 +71,9 @@ public static (IPoint C1, IPoint C2) Encrypt(IGroup G, IPoint publicKey, string
return Encrypt(G, publicKey, Encoding.UTF8.GetBytes(plaintext));
}

public static string DecryptString(IGroup G, IScalar privateKey, (IPoint C1, IPoint C2) ciphertext)
public static string DecryptString(IScalar privateKey, (IPoint C1, IPoint C2) ciphertext)
{
byte[] plaintextBytes = DecryptData(G, privateKey, ciphertext);
byte[] plaintextBytes = DecryptData(privateKey, ciphertext);
return Encoding.UTF8.GetString(plaintextBytes);
}
}
Expand Down
15 changes: 9 additions & 6 deletions dkgLibrary/util/RandomStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,20 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

using System.Security.Cryptography;
using Org.BouncyCastle.Security;
using System;
using System.IO;

namespace dkg.util
{
// Class RandomStream implements a view random number generator as a stream
// Class RandomStream implements a secure random number generator as a stream
public class RandomStream : Stream
{
private RandomNumberGenerator _rng;
private readonly SecureRandom _rng;

public RandomStream()
{
_rng = RandomNumberGenerator.Create();
_rng = new SecureRandom();
}

public override bool CanRead => true;
Expand All @@ -58,8 +60,9 @@ public override void Flush()

public override int Read(byte[] buffer, int offset, int count)
{
var data = new byte[count];
_rng.GetBytes(data);
// BouncyCastle's SecureRandom directly supports filling a byte array
byte[] data = new byte[count];
_rng.NextBytes(data);
Array.Copy(data, 0, buffer, offset, count);
return count;
}
Expand Down
16 changes: 12 additions & 4 deletions dkgLibrary/util/Schnorr.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@
//

using dkg.group;

using SHA256 = System.Security.Cryptography.SHA256;
using Org.BouncyCastle.Crypto.Digests;

namespace dkg.util
{
Expand Down Expand Up @@ -108,13 +107,22 @@ public static void Verify(IGroup g, IPoint publicKey, byte[] msg, byte[] sig)

private static IScalar Hash(IGroup g, IPoint publicPoint, IPoint r, byte[] msg)
{
var b = new MemoryStream();
using var b = new MemoryStream();

r.MarshalBinary(b);
publicPoint.MarshalBinary(b);
BinaryWriter w = new(b);
w.Write(msg);
var hash = SHA256.HashData(b.ToArray());

// Use BouncyCastle's SHA-256 implementation
var digest = new Sha256Digest();
var msgBytes = b.ToArray();
digest.BlockUpdate(msgBytes, 0, msgBytes.Length);
byte[] hash = new byte[digest.GetDigestSize()];
digest.DoFinal(hash, 0);

return g.Scalar().SetBytes(hash);

}
}
}
13 changes: 8 additions & 5 deletions dkgLibrary/vss/Dealer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,16 @@ public EncryptedDeal EncryptedDeal(int i)

// AES128-GCM
var pre = DhHelper.DhExchange(dhSecret, vPub);
var gcm = DhHelper.CreateAEAD(pre, HkdfContext) ?? throw new Exception("EncryptedDeal: error creating new AEAD");
var nonce = new byte[DhHelper.nonceSizeInBytes];
var nonce = new byte[DhHelper.NonceSizeInBytes];
using (var randomStream = new RandomStream())
{
randomStream.Read(nonce, 0, nonce.Length);
}
var gcm = DhHelper.CreateAEAD(true, pre, HkdfContext, nonce) ?? throw new Exception("EncryptedDeal: error creating new AEAD");


var deal = Deals[i].GetBytes();
byte[] encrypted = new byte[deal.Length];
byte[] tag = new byte[gcm.TagSizeInBytes ?? 16];
gcm.Encrypt(nonce, deal, encrypted, tag);
DhHelper.Encrypt(gcm, deal, out byte[] encrypted, out byte[] tag);

return new EncryptedDeal(dhPublicBuff, signature, nonce, encrypted, tag);
}
Expand Down
7 changes: 3 additions & 4 deletions dkgLibrary/vss/Verifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,10 @@ public Verifier(IGroup group, IScalar longterm, IPoint dealerKey, IPoint[] verif
var dhKey = G.Point();
dhKey.UnmarshalBinary(new MemoryStream(encrypted.DHKey));
var pre = DhHelper.DhExchange(LongTermKey, dhKey);
var gcm = DhHelper.CreateAEAD(pre, HkdfContext);
var nonce = encrypted.Nonce;
var gcm = DhHelper.CreateAEAD(false, pre, HkdfContext, nonce);

byte[] decrypted = new byte[encrypted.Cipher.Length];

gcm.Decrypt(encrypted.Nonce, encrypted.Cipher, encrypted.Tag, decrypted);
DhHelper.Decrypt(gcm, encrypted.Cipher, encrypted.Tag, out byte[] decrypted);

var deal = new Deal();
deal.UnmarshalBinary(new MemoryStream(decrypted));
Expand Down
12 changes: 10 additions & 2 deletions dkgLibrary/vss/VssTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
// POSSIBILITY OF SUCH DAMAGE.

using dkg.group;
using SHA256 = System.Security.Cryptography.SHA256;
using Org.BouncyCastle.Crypto.Digests;


namespace dkg.vss
Expand All @@ -45,7 +45,15 @@ public static byte[] CreateSessionId(IPoint publicKey, IPoint[] verifiers, IPoin
cmt.MarshalBinary(strm);
}
strm.Write(BitConverter.GetBytes((uint)t));
return SHA256.HashData(strm.ToArray());

// Use BouncyCastle's SHA-256 implementation
Sha256Digest digest = new();
byte[] inputBytes = strm.ToArray();
digest.BlockUpdate(inputBytes, 0, inputBytes.Length);
byte[] result = new byte[digest.GetDigestSize()];
digest.DoFinal(result, 0);

return result;
}

// MinimumT returns a safe value of T that balances secrecy and robustness.
Expand Down
12 changes: 6 additions & 6 deletions dkgLibraryTests/AnEndToEndExample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ internal class ExampleEndtoEnd
[Test]
public void EndToEndExample()
{
IGroup g = new Secp256k1Group();
var g = new Secp256k1Group();
// The number of nodes for this test
int n = 7;

Expand Down Expand Up @@ -112,8 +112,8 @@ public void EndToEndExample()
Assert.Multiple(() =>
{
Assert.That(node.Dkg!.Certified(), Is.True);
Assert.That(node.Dkg!.QualifiedShares().Count, Is.EqualTo(n));
Assert.That(node.Dkg!.QUAL().Count, Is.EqualTo(n));
Assert.That(node.Dkg!.QualifiedShares(), Has.Count.EqualTo(n));
Assert.That(node.Dkg!.QUAL(), Has.Count.EqualTo(n));
});
}

Expand Down Expand Up @@ -143,7 +143,7 @@ public void EndToEndExample()
var cipher = ECElGamalEncryption.Encrypt(g, publicKey!, message);
IScalar secretKey = PriPoly.RecoverSecret(g, shares, n);

var decryptedMessage = ECElGamalEncryption.DecryptString(g, secretKey, cipher);
var decryptedMessage = ECElGamalEncryption.DecryptString(secretKey, cipher);
Assert.That(decryptedMessage, Is.EqualTo(message));


Expand Down Expand Up @@ -213,8 +213,8 @@ public void EndToEndExample()
var p = g.Scalar();
var Q = g.Base().Mul(p);

var partials = new List<IPoint>();
pubShares = new List<PubShare>();
List<IPoint> partials = [];
pubShares = [];

for (int i = 0; i < n; i++)
{
Expand Down
Loading

0 comments on commit 9411d47

Please sign in to comment.