Skip to content

Commit 49ec82e

Browse files
k-karunamatsakiv
andauthored
Feat/remove libsodium (#71)
* Remove libsodium dependencies * Added crypto tests; * Impemented ConvertEd25519PublicKeyToCurve25519PublicKey; * Removed hardcoded PUBLIC_KEY_BYTES value; * SealedPublicKeyBox * Fix README.md * Fix SealedPublicKeyBox keys * Decreased BYTES_MIN param to 0 in GenericHash; * 1.0.18 --------- Co-authored-by: Igor Matsak <[email protected]>
1 parent 4922512 commit 49ec82e

35 files changed

+1597
-1163
lines changed

Beacon.Sdk.Tests/Beacon.Sdk.Tests.csproj

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4"/>
11-
<PackageReference Include="MSTest.TestAdapter" Version="2.2.7"/>
12-
<PackageReference Include="MSTest.TestFramework" Version="2.2.3"/>
13-
<PackageReference Include="coverlet.collector" Version="3.0.2"/>
10+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
11+
<PackageReference Include="MSTest.TestAdapter" Version="3.0.2" />
12+
<PackageReference Include="MSTest.TestFramework" Version="3.0.2" />
13+
<PackageReference Include="coverlet.collector" Version="3.2.0">
14+
<PrivateAssets>all</PrivateAssets>
15+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
16+
</PackageReference>
17+
<PackageReference Include="xunit" Version="2.4.2" />
1418
</ItemGroup>
1519

1620
<ItemGroup>
17-
<ProjectReference Include="..\Beacon.Sdk\Beacon.Sdk.csproj"/>
21+
<ProjectReference Include="..\Beacon.Sdk\Beacon.Sdk.csproj" />
1822
</ItemGroup>
1923

2024
</Project>

Beacon.Sdk.Tests/CryptoTests.cs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
namespace Beacon.Sdk.Tests
2+
{
3+
using Core.Infrastructure.Cryptography;
4+
using Core.Infrastructure.Cryptography.NaCl;
5+
using Core.Infrastructure.Cryptography.BouncyCastle;
6+
using Microsoft.VisualStudio.TestTools.UnitTesting;
7+
using System.Text;
8+
9+
[TestClass]
10+
public class CryptoTests
11+
{
12+
private byte[] randomPubKeyBytes { get; } =
13+
{
14+
0x58, 0x96, 0xB4, 0xDE, 0x36, 0x93, 0x5F, 0x5, 0x79, 0x4E, 0xB8, 0x7, 0x3F, 0xB1, 0x7F, 0xE6, 0xAD,
15+
0x64, 0xFF, 0xE3, 0x48, 0x60, 0x81, 0x1C, 0x9D, 0xDF, 0xB9, 0xE4, 0xF3, 0x2F, 0xF7, 0x5B,
16+
};
17+
18+
private byte[] convertedPubKeyBytes { get; } =
19+
{
20+
0x50, 0x94, 0xD3, 0xF3, 0x88, 0xFD, 0x19, 0xF3, 0x3D, 0xBF, 0xA7, 0x3A, 0x6C, 0x9E, 0xCD, 0x80, 0x7C,
21+
0x1C, 0x92, 0x74, 0xE3, 0xA0, 0x71, 0x8F, 0xEC, 0xB8, 0xDD, 0x8D, 0x3E, 0x78, 0x66, 0x15
22+
};
23+
24+
[TestMethod]
25+
public void TestSodiumConvertEd25519PublicKeyToCurve25519PublicKey()
26+
{
27+
var actual = PublicKeyAuth.ConvertEd25519PublicKeyToCurve25519PublicKey(randomPubKeyBytes);
28+
CollectionAssert.AreEqual(convertedPubKeyBytes, actual);
29+
}
30+
31+
[TestMethod]
32+
public void TestConvertEd25519PublicKeyToCurve25519PublicKey()
33+
{
34+
var actual = new byte[32];
35+
MontgomeryCurve25519.EdwardsToMontgomery(actual, randomPubKeyBytes);
36+
CollectionAssert.AreEqual(convertedPubKeyBytes, actual);
37+
}
38+
39+
[TestMethod]
40+
public void CanEncryptAndDecryptBySecretBox()
41+
{
42+
var key = SecureRandom.GetRandomBytes(32);
43+
var nonce = SecureRandom.GetRandomBytes(24);
44+
var message = Encoding.UTF8.GetBytes("Test message for secret box");
45+
46+
var cipher = SecretBox.Create(message, nonce, key);
47+
48+
var decrypted = SecretBox.Open(cipher, nonce, key);
49+
50+
CollectionAssert.AreEqual(message, decrypted);
51+
}
52+
53+
[TestMethod]
54+
public void CanEncryptAndDecryptBySealedSecretBox()
55+
{
56+
var seed = SecureRandom.GetRandomBytes(32);
57+
58+
var ed25519keyPair = PublicKeyAuth.GenerateKeyPair(seed);
59+
60+
var curve25519sk = Ed25519Extensions.ConvertEd25519SecretKeyToCurve25519SecretKey(ed25519keyPair.PrivateKey);
61+
var curve25519pk = Ed25519Extensions.ConvertEd25519PublicKeyToCurve25519PublicKey(ed25519keyPair.PublicKey);
62+
63+
var message = Encoding.UTF8.GetBytes("Test message for secret box");
64+
65+
var cipher = SealedPublicKeyBox.Create(message, curve25519pk);
66+
67+
var decrypted = SealedPublicKeyBox.Open(cipher, curve25519sk, curve25519pk);
68+
69+
CollectionAssert.AreEqual(message, decrypted);
70+
}
71+
}
72+
}

Beacon.Sdk/Beacon.Sdk.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,19 @@
1414
<Authors>Mikhail Tatarenko</Authors>
1515
<Product>Beacon.Sdk</Product>
1616
<Description>Beacon .NET SDK for Tezos wallet / dApps developers.</Description>
17-
<Version>1.0.16</Version>
17+
<Version>1.0.18</Version>
1818
<Copyright>Copyright © Baking Bad 2019-2022</Copyright>
1919
<Nullable>enable</Nullable>
20-
<TargetFramework>netstandard2.0</TargetFramework>
20+
<TargetFramework>netstandard2.1</TargetFramework>
2121
</PropertyGroup>
2222

2323
<ItemGroup>
2424
<PackageReference Include="LiteDB" Version="4.1.4" />
2525
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
2626
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
27+
<PackageReference Include="NaCl.Net" Version="0.1.13" />
2728
<PackageReference Include="Netezos" Version="2.4.6" />
2829
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
29-
<PackageReference Include="libsodium" Version="1.0.18.2" />
3030
<PackageReference Include="Matrix.Sdk" Version="1.0.7" />
3131
</ItemGroup>
3232
</Project>

Beacon.Sdk/Core/Domain/Entities/PeerFactory.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
namespace Beacon.Sdk.Core.Domain.Entities
22
{
3-
using System;
4-
using Infrastructure.Cryptography.Libsodium;
3+
using global::Beacon.Sdk.Core.Infrastructure.Cryptography;
54
using Interfaces;
65
using Netezos.Encoding;
76
using Utils;
@@ -17,10 +16,7 @@ public PeerFactory(ICryptographyService cryptographyService)
1716

1817
public static byte[] Hash(byte[] message, int bufferLength)
1918
{
20-
var buffer = new byte[bufferLength];
21-
Sodium.CryptoGenericHash(buffer, bufferLength, message, (ulong)message.Length, Array.Empty<byte>(), 0);
22-
23-
return buffer;
19+
return GenericHash.Hash(message, bufferLength);
2420
}
2521

2622
public Peer Create(HexString hexPublicKey, string name, string version, string relayServer, bool isActive = false)

Beacon.Sdk/Core/Domain/Interfaces/Data/ISessionKeyPairRepository.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace Beacon.Sdk.Core.Domain.Interfaces.Data
44
{
5-
using Infrastructure.Cryptography.Libsodium;
65
using Utils;
76

87
public interface ISessionKeyPairRepository

Beacon.Sdk/Core/Domain/Interfaces/ICryptographyService.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace Beacon.Sdk.Core.Domain.Interfaces
44
{
5-
using Infrastructure.Cryptography.Libsodium;
65
using Utils;
76

87
public interface ICryptographyService

Beacon.Sdk/Core/Domain/P2P/ChannelOpening/ChannelOpeningMessageBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public ChannelOpeningMessageBuilder(
3232
public void BuildRecipientId(string relayServer, HexString hexPublicKey)
3333
{
3434
byte[] publicKeyByteArray = hexPublicKey.ToByteArray();
35-
byte[] hash = GenericHash.Hash(publicKeyByteArray, null, publicKeyByteArray.Length)!;
35+
byte[] hash = GenericHash.Hash(publicKeyByteArray, publicKeyByteArray.Length)!;
3636

3737
if (!HexString.TryParse(hash, out HexString hexHash))
3838
throw new InvalidOperationException("Can not parse hash");

Beacon.Sdk/Core/Domain/Services/P2PMessageService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
namespace Beacon.Sdk.Core.Domain.Services
22
{
33
using System.Text;
4-
using Infrastructure.Cryptography.Libsodium;
4+
using global::Beacon.Sdk.Core.Infrastructure.Cryptography;
55
using Interfaces;
66
using Interfaces.Data;
77
using Netezos.Encoding;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using Org.BouncyCastle.Crypto.Digests;
2+
using System;
3+
4+
namespace Beacon.Sdk.Core.Infrastructure.Cryptography.BouncyCastle
5+
{
6+
using NaCl;
7+
8+
public static class Ed25519Extensions
9+
{
10+
private const int PUBLIC_KEY_BYTES = 32;
11+
private const int SCALAR_BYTES = 32;
12+
private const int SECRET_KEY_BYTES = 32 + 32;
13+
14+
/// <summary>Converts the ed25519 public key to curve25519 public key.</summary>
15+
/// <param name="ed25519PublicKey">Ed25519 public key.</param>
16+
/// <returns>The curve25519 public key.</returns>
17+
/// <exception cref="ArgumentOutOfRangeException"></exception>
18+
public static byte[] ConvertEd25519PublicKeyToCurve25519PublicKey(byte[] ed25519PublicKey)
19+
{
20+
if (ed25519PublicKey == null || ed25519PublicKey.Length != PUBLIC_KEY_BYTES)
21+
throw new ArgumentOutOfRangeException(nameof(ed25519PublicKey), ed25519PublicKey?.Length ?? 0, $"ed25519PublicKey must be {PUBLIC_KEY_BYTES} bytes in length.");
22+
23+
var result = new byte[PUBLIC_KEY_BYTES];
24+
MontgomeryCurve25519.EdwardsToMontgomery(result, ed25519PublicKey);
25+
return result;
26+
}
27+
28+
/// <summary>Converts the ed25519 secret key to curve25519 secret key.</summary>
29+
/// <param name="ed25519SecretKey">Ed25519 secret key.</param>
30+
/// <returns>The curve25519 secret key.</returns>
31+
/// <exception cref="ArgumentOutOfRangeException"></exception>
32+
public static byte[] ConvertEd25519SecretKeyToCurve25519SecretKey(byte[] ed25519SecretKey)
33+
{
34+
// key can be appended with the public key or not (both are allowed)
35+
if (ed25519SecretKey == null || (ed25519SecretKey.Length != PUBLIC_KEY_BYTES && ed25519SecretKey.Length != SECRET_KEY_BYTES))
36+
throw new ArgumentOutOfRangeException(nameof(ed25519SecretKey), ed25519SecretKey?.Length ?? 0, $"ed25519SecretKey must be either {PUBLIC_KEY_BYTES} or {SECRET_KEY_BYTES} bytes in length.");
37+
38+
var sha512 = new Sha512Digest();
39+
40+
byte[] h = new byte[sha512.GetDigestSize()];
41+
42+
sha512.BlockUpdate(ed25519SecretKey, 0, SCALAR_BYTES);
43+
sha512.DoFinal(h, 0);
44+
45+
PruneScalar(h);
46+
47+
return h.AsSpan(0, 32).ToArray();
48+
}
49+
50+
private static void PruneScalar(byte[] n)
51+
{
52+
n[0] &= 0xF8;
53+
n[SCALAR_BYTES - 1] &= 0x7F;
54+
n[SCALAR_BYTES - 1] |= 0x40;
55+
}
56+
}
57+
}

Beacon.Sdk/Core/Infrastructure/Cryptography/CryptographyService.cs

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,12 @@ namespace Beacon.Sdk.Core.Infrastructure.Cryptography
44
using System.Linq;
55
using System.Text;
66
using Domain.Interfaces;
7-
using Libsodium;
87
using Utils;
9-
using Sodium = Libsodium.Sodium;
108

119
public class CryptographyService : ICryptographyService
1210
{
13-
private static readonly int MacBytes = Sodium.CryptoBoxMacBytes();
14-
private static readonly int NonceBytes = Sodium.CryptoBoxNonceBytes();
11+
private static readonly int MacBytes = 16;
12+
private static readonly int NonceBytes = 24;
1513

1614
public SessionKeyPair CreateClientSessionKeyPair(byte[] clientPublicKey, byte[] serverPrivateKey)
1715
{
@@ -20,7 +18,9 @@ public SessionKeyPair CreateClientSessionKeyPair(byte[] clientPublicKey, byte[]
2018
byte[] serverSecretKeyCurve = PublicKeyAuth.ConvertEd25519SecretKeyToCurve25519SecretKey(serverPrivateKey)!;
2119
byte[] clientPublicKeyCurve = PublicKeyAuth.ConvertEd25519PublicKeyToCurve25519PublicKey(clientPublicKey)!;
2220

23-
return KeyExchange.CreateClientSessionKeyPair(serverPublicKeyCurve, serverSecretKeyCurve,
21+
return KeyExchange.CreateClientSessionKeyPair(
22+
serverPublicKeyCurve,
23+
serverSecretKeyCurve,
2424
clientPublicKeyCurve);
2525
}
2626

@@ -31,25 +31,19 @@ public SessionKeyPair CreateServerSessionKeyPair(byte[] clientPublicKey, byte[]
3131
byte[] serverSecretKeyCurve = PublicKeyAuth.ConvertEd25519SecretKeyToCurve25519SecretKey(serverPrivateKey)!;
3232
byte[] clientPublicKeyCurve = PublicKeyAuth.ConvertEd25519PublicKeyToCurve25519PublicKey(clientPublicKey)!;
3333

34-
return KeyExchange.CreateServerSessionKeyPair(serverPublicKeyCurve, serverSecretKeyCurve,
34+
return KeyExchange.CreateServerSessionKeyPair(
35+
serverPublicKeyCurve,
36+
serverSecretKeyCurve,
3537
clientPublicKeyCurve);
3638
}
3739

38-
public byte[] Hash(byte[] input) => GenericHash.Hash(input, null, input.Length);
40+
public byte[] Hash(byte[] input) => GenericHash.Hash(input, input.Length);
3941

40-
public byte[] Hash(byte[] message, int bufferLength)
41-
{
42-
var buffer = new byte[bufferLength];
43-
44-
Sodium.Initialize();
45-
Sodium.CryptoGenericHash(buffer, bufferLength, message, (ulong)message.Length, Array.Empty<byte>(), 0);
46-
47-
return buffer;
48-
}
42+
public byte[] Hash(byte[] message, int resultLength) => GenericHash.Hash(message, resultLength);
4943

5044
public KeyPair GenerateEd25519KeyPair(string seed)
5145
{
52-
byte[] hash = GenericHash.Hash(seed, (byte[]?)null, 32);
46+
byte[] hash = GenericHash.Hash(seed, 32);
5347

5448
return PublicKeyAuth.GenerateKeyPair(hash);
5549
}
@@ -67,14 +61,6 @@ public HexString Encrypt(string input, byte[] key)
6761
return hexPayload;
6862
}
6963

70-
// public static class ArrayExtensions
71-
// {
72-
// public byte this[Range input]
73-
// {
74-
// get;
75-
// set;
76-
// }
77-
// }
7864
public string Decrypt(HexString hexInput, byte[] key)
7965
{
8066
byte[] bytes = hexInput.ToByteArray();
@@ -136,7 +122,7 @@ public byte[] GenerateLoginDigest()
136122
long now = DateTimeOffset.UtcNow.ToUnixTimeSeconds() * 1000;
137123
var message = $"login:{now / 1000 / (5 * 60)}";
138124

139-
return GenericHash.Hash(message, (byte[]?)null, 32);
125+
return GenericHash.Hash(message, 32);
140126
}
141127

142128
public string GenerateHexSignature(byte[] loginDigest, byte[] secretKey)
@@ -148,7 +134,7 @@ public string GenerateHexSignature(byte[] loginDigest, byte[] secretKey)
148134

149135
public string GenerateHexId(byte[] publicKey)
150136
{
151-
byte[] hash = GenericHash.Hash(publicKey, null, publicKey.Length);
137+
byte[] hash = GenericHash.Hash(publicKey, publicKey.Length);
152138

153139
return ToHexString(hash);
154140
}

0 commit comments

Comments
 (0)