diff --git a/src/Nethermind/Directory.Packages.props b/src/Nethermind/Directory.Packages.props index 21ac26de7c4..a19efb10615 100644 --- a/src/Nethermind/Directory.Packages.props +++ b/src/Nethermind/Directory.Packages.props @@ -47,7 +47,7 @@ - + diff --git a/src/Nethermind/Nethermind.Core.Test/Crypto/BlsSignerTests.cs b/src/Nethermind/Nethermind.Core.Test/Crypto/BlsSignerTests.cs index 7662ce30187..8bc57d7c6b5 100644 --- a/src/Nethermind/Nethermind.Core.Test/Crypto/BlsSignerTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/Crypto/BlsSignerTests.cs @@ -21,7 +21,8 @@ public class BlsTests public void Calculate_signature() { byte[] expected = [0xa5, 0xa0, 0x0d, 0xe9, 0x9d, 0x8f, 0xee, 0x7e, 0x28, 0x81, 0x1b, 0x2c, 0x08, 0xe0, 0xa7, 0xfc, 0x00, 0xa1, 0x10, 0x0c, 0x3d, 0x0f, 0x80, 0x51, 0x9d, 0x43, 0x24, 0x67, 0x1c, 0x29, 0x36, 0xb1, 0xe5, 0xa5, 0x87, 0x7d, 0x46, 0x7a, 0x6d, 0xc6, 0xf5, 0x92, 0xb2, 0x40, 0x7b, 0xcb, 0x12, 0x61, 0x0c, 0x18, 0x8a, 0x6c, 0xdf, 0x57, 0xd1, 0x77, 0x92, 0x00, 0x0f, 0xf7, 0x56, 0xf8, 0x0e, 0xbe, 0xd8, 0x00, 0x88, 0xab, 0x22, 0x9a, 0xa7, 0xe2, 0xc3, 0x24, 0x09, 0xec, 0xfe, 0x5a, 0x8d, 0x44, 0x73, 0xe9, 0x12, 0xfa, 0x19, 0x9e, 0xee, 0xa1, 0x8f, 0x3c, 0x79, 0x8d, 0xc5, 0x28, 0x64, 0x7d]; - BlsSigner.Signature s = BlsSigner.Sign(new(SkBytes, Bls.ByteOrder.LittleEndian), MsgBytes); + Bls.SecretKey sk = new(SkBytes, Bls.ByteOrder.LittleEndian); + BlsSigner.Signature s = BlsSigner.Sign(sk, MsgBytes); s.Bytes.ToArray().Should().Equal(expected); } @@ -38,13 +39,23 @@ public void Verify_signature() [Test] public void Verify_aggregate_signature() { - Span skBytes = new byte[AggregateSignerCount * 32]; - Span publicKeys = new byte[AggregateSignerCount * BlsSigner.PkCompressedSz]; + BlsSigner.Signature agg = new(); + BlsSigner.Signature s = new(); + BlsSigner.AggregatedPublicKey aggregatedPublicKey = new(); + G1 pk = new(); + + Bls.SecretKey masterSk = new(SkBytes, Bls.ByteOrder.LittleEndian); - GenerateKeys(skBytes, publicKeys); + for (int i = 0; i < AggregateSignerCount; i++) + { + Bls.SecretKey sk = new(masterSk, (uint)i); + s.Sign(sk, MsgBytes); + agg.Aggregate(s); + pk.FromSk(sk); + aggregatedPublicKey.Aggregate(pk.ToAffine()); + } - BlsSigner.Signature s = BlsSigner.SignAggregate(skBytes, MsgBytes); - Assert.That(BlsSigner.VerifyAggregate(publicKeys, s, MsgBytes)); + Assert.That(BlsSigner.VerifyAggregate(aggregatedPublicKey, agg, MsgBytes)); } [Test] @@ -52,26 +63,39 @@ public void Rejects_bad_signature() { Bls.SecretKey sk = new(SkBytes, Bls.ByteOrder.LittleEndian); BlsSigner.Signature s = BlsSigner.Sign(sk, MsgBytes); - Span bytes = stackalloc byte[96]; - s.Bytes.CopyTo(bytes); - bytes[34] += 1; - BlsSigner.Signature bad = new(bytes); + Span badSig = stackalloc byte[96]; + s.Bytes.CopyTo(badSig); + badSig[34] += 1; G1 publicKey = new(); publicKey.FromSk(sk); - Assert.That(BlsSigner.Verify(publicKey.ToAffine(), bad, MsgBytes), Is.False); + Assert.That(BlsSigner.Verify(publicKey.ToAffine(), badSig, MsgBytes), Is.False); } [Test] public void Rejects_missing_aggregate_signature() { - Span skBytes = new byte[AggregateSignerCount * 32]; - Span publicKeys = new byte[AggregateSignerCount * BlsSigner.PkCompressedSz]; + BlsSigner.Signature agg = new(); + BlsSigner.Signature s = new(); + BlsSigner.AggregatedPublicKey aggregatedPublicKey = new(); + G1 pk = new(); - GenerateKeys(skBytes, publicKeys); + Bls.SecretKey masterSk = new(SkBytes, Bls.ByteOrder.LittleEndian); - BlsSigner.Signature s = BlsSigner.SignAggregate(skBytes[32..], MsgBytes); - Assert.That(BlsSigner.VerifyAggregate(publicKeys, s, MsgBytes), Is.False); + for (int i = 0; i < AggregateSignerCount; i++) + { + Bls.SecretKey sk = new(masterSk, (uint)i); + s.Sign(sk, MsgBytes); + if (i != 0) + { + // exclude one signature + agg.Aggregate(s); + } + pk.FromSk(sk); + aggregatedPublicKey.Aggregate(pk.ToAffine()); + } + + Assert.That(BlsSigner.VerifyAggregate(aggregatedPublicKey, agg, MsgBytes), Is.False); } [Test] @@ -84,19 +108,4 @@ public void Public_key_from_private_key() Assert.That(publicKey.Compress(), Is.EqualTo(expected)); } - - private void GenerateKeys(Span skBytes, Span publicKeyBytes) - { - Bls.SecretKey masterSk = new(SkBytes, Bls.ByteOrder.LittleEndian); - for (int i = 0; i < AggregateSignerCount; i++) - { - int offset = i * 32; - Bls.SecretKey sk = new(masterSk, (uint)i); - sk.ToBendian().CopyTo(skBytes[offset..(offset + 32)]); - - G1 publicKey = new(); - publicKey.FromSk(sk); - publicKey.Compress().CopyTo(publicKeyBytes[(i * BlsSigner.PkCompressedSz)..]); - } - } } diff --git a/src/Nethermind/Nethermind.Crypto/BlsSigner.cs b/src/Nethermind/Nethermind.Crypto/BlsSigner.cs index f0c4d672c7f..93f9e95aeec 100644 --- a/src/Nethermind/Nethermind.Crypto/BlsSigner.cs +++ b/src/Nethermind/Nethermind.Crypto/BlsSigner.cs @@ -23,96 +23,132 @@ public static class BlsSigner private const int InputLength = 64; [SkipLocalsInit] + // buf must be of size G2.Sz + public static Signature Sign(Span buf, Bls.SecretKey sk, ReadOnlySpan message) + { + if (buf.Length != G2.Sz) + { + throw new ArgumentException($"Signature buffer {nameof(buf)} must be of size {G2.Sz}."); + } + + G2 p = new(buf); + Signature s = new(p); + s.Sign(sk, message); + return s; + } + public static Signature Sign(Bls.SecretKey sk, ReadOnlySpan message) + => Sign(new long[G2.Sz], sk, message); + + public static bool Verify(G1Affine publicKey, Signature signature, ReadOnlySpan message) { - G2 p = new(stackalloc long[G2.Sz]); - p.HashTo(message, Cryptosuite); - p.SignWith(sk); - return new(p.Compress()); + int len = 2 * GT.Sz; + using ArrayPoolList buf = new(len, len); + + GT p1 = new(buf.AsSpan()[..GT.Sz]); + p1.MillerLoop(signature.Point, G1Affine.Generator(stackalloc long[G1Affine.Sz])); + + G2 m = new(stackalloc long[G2.Sz]); + m.HashTo(message, Cryptosuite); + GT p2 = new(buf.AsSpan()[GT.Sz..]); + p2.MillerLoop(m.ToAffine(), publicKey); + + return GT.FinalVerify(p1, p2); } [SkipLocalsInit] - public static Signature SignAggregate(ReadOnlySpan skBytes, ReadOnlySpan message) + public static bool Verify(G1Affine publicKey, ReadOnlySpan sigBytes, ReadOnlySpan message) { - if (skBytes.Length % 32 != 0) - { - throw new Bls.BlsException(Bls.ERROR.WRONGSIZE); - } - G2 p = new(stackalloc long[G2.Sz]); - G2 agg = new(stackalloc long[G2.Sz]); - agg.Zero(); + Signature s = new(p); - for (int i = 0; i < skBytes.Length; i += 32) + if (!p.TryDecode(sigBytes, out _)) { - Bls.SecretKey sk = new(skBytes.Slice(i, 32)); - p.HashTo(message, Cryptosuite); - p.SignWith(sk); - agg.Aggregate(p.ToAffine()); + return false; } - return new(agg.Compress()); + return Verify(publicKey, s, message); } - [SkipLocalsInit] - public static bool Verify(G1Affine publicKey, Signature signature, ReadOnlySpan message) + public static bool VerifyAggregate(AggregatedPublicKey aggregatedPublicKey, Signature signature, ReadOnlySpan message) + => Verify(aggregatedPublicKey.PublicKey, signature, message); + + public readonly ref struct Signature { - int len = 2 * GT.Sz; - using ArrayPoolList buf = new(len, len); - try - { - G2Affine sig = new(stackalloc long[G2Affine.Sz]); - sig.Decode(signature.Bytes); - GT p1 = new(buf.AsSpan()[..GT.Sz]); - p1.MillerLoop(sig, G1Affine.Generator(stackalloc long[G1Affine.Sz])); + public const int Sz = 96; + public readonly ReadOnlySpan Bytes { get => _point.Compress(); } + public readonly G2Affine Point { get => _point.ToAffine(); } + private readonly G2 _point; - G2 m = new(stackalloc long[G2.Sz]); - m.HashTo(message, Cryptosuite); - GT p2 = new(buf.AsSpan()[GT.Sz..]); - p2.MillerLoop(m.ToAffine(), publicKey); + public Signature() + { + _point = new(); + } - return GT.FinalVerify(p1, p2); + public Signature(G2 point) + { + _point = point; } - catch (Bls.BlsException) + + public void Decode(ReadOnlySpan bytes) + => _point.Decode(bytes); + + public void Sign(Bls.SecretKey sk, ReadOnlySpan message) { - // point not on curve - return false; + _point.HashTo(message, Cryptosuite); + _point.SignWith(sk); } + + public void Aggregate(Signature s) + => _point.Aggregate(s.Point); } - [SkipLocalsInit] - public static bool VerifyAggregate(ReadOnlySpan publicKeyBytes, Signature signature, ReadOnlySpan message) + public readonly ref struct AggregatedPublicKey { - if (publicKeyBytes.Length % PkCompressedSz != 0) + public G1Affine PublicKey { get => _point.ToAffine(); } + private readonly G1 _point; + + public AggregatedPublicKey() { - throw new Bls.BlsException(Bls.ERROR.WRONGSIZE); + _point = new(); } - G1Affine pk = new(stackalloc long[G1Affine.Sz]); - G1 agg = new(stackalloc long[G1.Sz]); - agg.Zero(); - - for (int i = 0; i < publicKeyBytes.Length; i += PkCompressedSz) + public AggregatedPublicKey(Span buf) { - pk.Decode(publicKeyBytes.Slice(i, PkCompressedSz)); - agg.Aggregate(pk); + if (buf.Length != G1.Sz) + { + throw new ArgumentException($"Public key buffer {nameof(buf)} must be of size {G1.Sz}."); + } + + _point = new(buf); } - return Verify(agg.ToAffine(), signature, message); - } + public void FromSk(Bls.SecretKey sk) + => _point.FromSk(sk); - // Compressed G2 point - public readonly ref struct Signature() - { - public readonly ReadOnlySpan Bytes; + public bool TryDecode(ReadOnlySpan publicKeyBytes, out Bls.ERROR err) + => _point.TryDecode(publicKeyBytes, out err); - public Signature(ReadOnlySpan s) : this() + public void Decode(ReadOnlySpan publicKeyBytes) + => _point.Decode(publicKeyBytes); + + public void Aggregate(G1Affine publicKey) + => _point.Aggregate(publicKey); + + public void Aggregate(AggregatedPublicKey aggregatedPublicKey) + => _point.Aggregate(aggregatedPublicKey.PublicKey); + + public bool TryAggregate(ReadOnlySpan publicKeyBytes, out Bls.ERROR err) { - if (s.Length != 96) + G1Affine pk = new(stackalloc long[G1Affine.Sz]); + + if (!pk.TryDecode(publicKeyBytes, out err)) { - throw new Bls.BlsException(Bls.ERROR.BADENCODING); + return false; } - Bytes = s; + + Aggregate(pk); + return true; } } } diff --git a/src/Nethermind/Nethermind.Shutter/Contracts/ValidatorRegistryContract.cs b/src/Nethermind/Nethermind.Shutter/Contracts/ValidatorRegistryContract.cs index 25f80b9cb10..fee095e8d4a 100644 --- a/src/Nethermind/Nethermind.Shutter/Contracts/ValidatorRegistryContract.cs +++ b/src/Nethermind/Nethermind.Shutter/Contracts/ValidatorRegistryContract.cs @@ -12,6 +12,7 @@ using System.Collections.Generic; using Nethermind.Core.Extensions; using Update = (byte[] Message, byte[] Signature); +using Nethermind.Crypto; namespace Nethermind.Shutter.Contracts; @@ -46,7 +47,7 @@ public bool IsRegistered(BlockHeader header, in Dictionary valida { Update update = GetUpdate(header, updates - i - 1); - if (update.Message.Length != 46 || update.Signature.Length != 96) + if (update.Message.Length != Message.Sz || update.Signature.Length != BlsSigner.Signature.Sz) { if (_logger.IsDebug) _logger.Debug("Registration message was wrong length."); continue; @@ -108,6 +109,7 @@ public bool IsRegistered(BlockHeader header, in Dictionary valida private readonly ref struct Message { + public const int Sz = 46; public readonly byte Version; public readonly ulong ChainId; public readonly ReadOnlySpan ContractAddress; @@ -117,7 +119,7 @@ private readonly ref struct Message public Message(Span encodedMessage) { - if (encodedMessage.Length != 46) + if (encodedMessage.Length != Sz) { throw new ArgumentException("Validator registry contract message was wrong length."); } diff --git a/src/Nethermind/Nethermind.Shutter/ShutterCrypto.cs b/src/Nethermind/Nethermind.Shutter/ShutterCrypto.cs index 540f147fe94..9afab67f631 100644 --- a/src/Nethermind/Nethermind.Shutter/ShutterCrypto.cs +++ b/src/Nethermind/Nethermind.Shutter/ShutterCrypto.cs @@ -219,12 +219,10 @@ public static bool CheckSlotDecryptionIdentitiesSignature( [SkipLocalsInit] public static bool CheckValidatorRegistrySignature(ReadOnlySpan pkBytes, ReadOnlySpan sigBytes, ReadOnlySpan msgBytes) { - BlsSigner.Signature sig = new(sigBytes); ValueHash256 h = ValueKeccak.Compute(msgBytes); - G1Affine pk = new(stackalloc long[G1Affine.Sz]); pk.Decode(pkBytes); - return BlsSigner.Verify(pk, sig, h.Bytes); + return BlsSigner.Verify(pk, sigBytes, h.Bytes); } public static EncryptedMessage Encrypt(ReadOnlySpan msg, G1 identity, G2 eonKey, ReadOnlySpan sigma)