diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000..9c97586
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,4 @@
+# Code owners are automatically requested for review on every pull request.
+# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
+
+* @Marchhill
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index c963c6e..2710934 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -124,7 +124,11 @@ jobs:
needs: [build-linux, build-macos, build-windows]
env:
BUILD_CONFIG: release
- runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ runs-on: ${{ matrix.os }}
steps:
- name: Check out blst-bindings repository
uses: actions/checkout@v5
@@ -136,6 +140,7 @@ jobs:
- name: Move artifacts
working-directory: src/Nethermind.Crypto.Bls/runtimes
+ shell: bash
run: |
mv -f linux-arm64/libblst.so linux-arm64/native/libblst.so
mv -f linux-x64/libblst.so linux-x64/native/libblst.so
@@ -151,20 +156,18 @@ jobs:
run: dotnet restore
- name: Build
- working-directory: src/Nethermind.Crypto.Bls
- run: >
- dotnet build -c ${{ env.BUILD_CONFIG }} --no-restore
- ${{ inputs.preview && format('-p:VersionSuffix=preview.{0}', github.run_number) || '' }}
+ working-directory: src
+ run: dotnet build -c ${{ env.BUILD_CONFIG }} --no-restore ${{ inputs.preview && format('-p:VersionSuffix=preview.{0}', github.run_number) || '' }}
- name: Test
working-directory: src/Nethermind.Crypto.Test
run: dotnet test -c ${{ env.BUILD_CONFIG }} --no-restore
- name: Publish
- if: ${{ inputs.publish }}
+ if: ${{ inputs.publish && matrix.os == 'ubuntu-latest' }}
working-directory: src/Nethermind.Crypto.Bls
run: |
- dotnet pack -c ${{ env.BUILD_CONFIG }} --no-build
+ dotnet pack -c ${{ env.BUILD_CONFIG }} --no-build ${{ inputs.preview && format('-p:VersionSuffix=preview.{0}', github.run_number) || '' }}
dotnet nuget push bin/${{ env.BUILD_CONFIG }}/*.nupkg \
-k ${{ github.event.inputs.feed == 'Production' && secrets.NUGET_API_KEY || secrets.NUGETTEST_API_KEY }} \
-s ${{ github.event.inputs.feed == 'Production' && 'https://api.nuget.org/v3/index.json' || 'https://apiint.nugettest.org/v3/index.json' }}
diff --git a/README.md b/README.md
index 580adbf..1dd5ede 100644
--- a/README.md
+++ b/README.md
@@ -4,3 +4,17 @@
[](https://www.nuget.org/packages/Nethermind.Crypto.Bls)
C# bindings for the [Supranational blst library](https://github.com/supranational/blst), supporting operations on the BLS12-381 curve and BLS signatures.
+
+## Development
+
+Run the tests:
+
+```sh
+dotnet test src -c release
+```
+
+Run the benchmarks (optionally filtered by class, e.g. `--filter '*MsmBenchmarks*'`):
+
+```sh
+dotnet run --project src/Nethermind.Crypto.Bls.Bench -c release
+```
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 71092e5..a2e8cc5 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -13,7 +13,7 @@
Nethermind
Demerzel Solutions Limited
$(Commit.Substring(0, 8))
- 1.0.5
+ 1.1.0
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index bf30432..ad3bba8 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -3,6 +3,7 @@
true
+
diff --git a/src/Nethermind.Crypto.Bls.Bench/BlsBenchmarks.cs b/src/Nethermind.Crypto.Bls.Bench/BlsBenchmarks.cs
new file mode 100644
index 0000000..8429cae
--- /dev/null
+++ b/src/Nethermind.Crypto.Bls.Bench/BlsBenchmarks.cs
@@ -0,0 +1,394 @@
+// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
+// SPDX-License-Identifier: MIT
+
+using System;
+using BenchmarkDotNet.Attributes;
+using Nethermind.Crypto;
+
+namespace Nethermind.Crypto.Bench;
+
+using G1 = Bls.P1;
+using G2 = Bls.P2;
+using G1Affine = Bls.P1Affine;
+using G2Affine = Bls.P2Affine;
+using GT = Bls.PT;
+
+public static class BenchmarkData
+{
+ public static readonly byte[] Dst = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"u8.ToArray();
+
+ public static byte[] RandomScalar(Random rng)
+ {
+ byte[] scalar = new byte[32];
+ rng.NextBytes(scalar);
+ scalar[31] &= 0x1f; // keep the little-endian value below the group order
+ return scalar;
+ }
+}
+
+[MemoryDiagnoser]
+public class KeyBenchmarks
+{
+ private readonly byte[] _ikm = new byte[32];
+ private readonly byte[] _sk = new byte[32];
+ private readonly long[] _pk = new long[G1.Sz];
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ Random rng = new(42);
+ rng.NextBytes(_ikm);
+ new Bls.SecretKey(_sk).Keygen(_ikm);
+ }
+
+ [Benchmark]
+ public long Keygen()
+ {
+ new Bls.SecretKey(_sk).Keygen(_ikm);
+ return _sk[0];
+ }
+
+ [Benchmark]
+ public long SkToPkG1()
+ {
+ new G1(_pk).FromSk(new Bls.SecretKey(_sk));
+ return _pk[0];
+ }
+}
+
+[MemoryDiagnoser]
+public class SignatureBenchmarks
+{
+ private readonly byte[] _msg = new byte[32];
+ private readonly byte[] _sk = new byte[32];
+ private readonly long[] _pkAffine = new long[G1Affine.Sz];
+ private readonly long[] _sigAffine = new long[G2Affine.Sz];
+ private readonly long[] _hash = new long[G2.Sz];
+ private readonly long[] _scratch = new long[G2.Sz];
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ Random rng = new(42);
+ rng.NextBytes(_msg);
+
+ byte[] ikm = new byte[32];
+ rng.NextBytes(ikm);
+ Bls.SecretKey sk = new(_sk);
+ sk.Keygen(ikm);
+
+ new G1Affine(new G1(sk)).Point.CopyTo(_pkAffine);
+ G2 sig = new G2(_hash).HashTo(_msg, BenchmarkData.Dst);
+ new G2Affine(sig.Dup().SignWith(sk)).Point.CopyTo(_sigAffine);
+ }
+
+ [Benchmark]
+ public long HashToG2()
+ {
+ new G2(_scratch).HashTo(_msg, BenchmarkData.Dst);
+ return _scratch[0];
+ }
+
+ [Benchmark]
+ public long SignPrehashed()
+ {
+ _hash.CopyTo(_scratch.AsSpan());
+ new G2(_scratch).SignWith(new Bls.SecretKey(_sk));
+ return _scratch[0];
+ }
+
+ [Benchmark]
+ public bool Verify()
+ {
+ Bls.Pairing ctx = new(true, BenchmarkData.Dst);
+ ctx.Aggregate(new G1Affine(_pkAffine), new G2Affine(_sigAffine), _msg);
+ ctx.Commit();
+ return ctx.FinalVerify();
+ }
+}
+
+[MemoryDiagnoser]
+public class G1Benchmarks
+{
+ private readonly byte[] _scalar = new byte[32];
+ private readonly long[] _point = new long[G1.Sz];
+ private readonly long[] _affine = new long[G1Affine.Sz];
+ private readonly long[] _scratch = new long[G1.Sz];
+ private byte[] _compressed = [];
+ private byte[] _serialized = [];
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ Random rng = new(42);
+ BenchmarkData.RandomScalar(rng).CopyTo(_scalar.AsSpan());
+
+ G1 p = G1.Generator(_point).Mult(_scalar);
+ new G1Affine(p).Point.CopyTo(_affine);
+ _compressed = p.Compress();
+ _serialized = p.Serialize();
+ }
+
+ [Benchmark]
+ public long Mult()
+ {
+ _point.CopyTo(_scratch.AsSpan());
+ new G1(_scratch).Mult(_scalar);
+ return _scratch[0];
+ }
+
+ [Benchmark]
+ public long Add()
+ {
+ _point.CopyTo(_scratch.AsSpan());
+ new G1(_scratch).Add(new G1Affine(_affine));
+ return _scratch[0];
+ }
+
+ [Benchmark]
+ public bool DecodeCompressedValidated()
+ => new G1Affine(_scratch).TryDecode(_compressed, out _);
+
+ [Benchmark]
+ public bool DecodeUncompressedValidated()
+ => new G1Affine(_scratch).TryDecode(_serialized, out _);
+
+ [Benchmark]
+ public bool DecodeUncompressedRaw()
+ => new G1(_scratch).TryDecode(_serialized, out _);
+
+ [Benchmark]
+ public bool SubgroupCheck()
+ => new G1(_point).InGroup();
+
+ [Benchmark]
+ public byte[] Compress()
+ => new G1(_point).Compress();
+}
+
+[MemoryDiagnoser]
+public class G2Benchmarks
+{
+ private readonly byte[] _scalar = new byte[32];
+ private readonly long[] _point = new long[G2.Sz];
+ private readonly long[] _affine = new long[G2Affine.Sz];
+ private readonly long[] _scratch = new long[G2.Sz];
+ private byte[] _compressed = [];
+ private byte[] _serialized = [];
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ Random rng = new(42);
+ BenchmarkData.RandomScalar(rng).CopyTo(_scalar.AsSpan());
+
+ G2 p = G2.Generator(_point).Mult(_scalar);
+ new G2Affine(p).Point.CopyTo(_affine);
+ _compressed = p.Compress();
+ _serialized = p.Serialize();
+ }
+
+ [Benchmark]
+ public long Mult()
+ {
+ _point.CopyTo(_scratch.AsSpan());
+ new G2(_scratch).Mult(_scalar);
+ return _scratch[0];
+ }
+
+ [Benchmark]
+ public long Add()
+ {
+ _point.CopyTo(_scratch.AsSpan());
+ new G2(_scratch).Add(new G2Affine(_affine));
+ return _scratch[0];
+ }
+
+ [Benchmark]
+ public bool DecodeCompressedValidated()
+ => new G2Affine(_scratch).TryDecode(_compressed, out _);
+
+ [Benchmark]
+ public bool DecodeUncompressedValidated()
+ => new G2Affine(_scratch).TryDecode(_serialized, out _);
+
+ [Benchmark]
+ public bool DecodeUncompressedRaw()
+ => new G2(_scratch).TryDecode(_serialized, out _);
+
+ [Benchmark]
+ public bool SubgroupCheck()
+ => new G2(_point).InGroup();
+
+ [Benchmark]
+ public byte[] Compress()
+ => new G2(_point).Compress();
+}
+
+[MemoryDiagnoser]
+public class PairingBenchmarks
+{
+ private readonly long[] _p = new long[G1Affine.Sz];
+ private readonly long[] _q = new long[G2Affine.Sz];
+ private readonly long[] _lines = new long[68 * 6 * 6];
+ private readonly long[] _millerOut = new long[GT.Sz];
+ private readonly long[] _scratch = new long[GT.Sz];
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ Random rng = new(42);
+ G1Affine p = new(G1.Generator().Mult(BenchmarkData.RandomScalar(rng)));
+ G2Affine q = new(G2.Generator().Mult(BenchmarkData.RandomScalar(rng)));
+ p.Point.CopyTo(_p);
+ q.Point.CopyTo(_q);
+ q.PrecomputeLines(_lines);
+ new GT(_millerOut).MillerLoop(q, p);
+ }
+
+ [Benchmark]
+ public long MillerLoop()
+ {
+ new GT(_scratch).MillerLoop(new G2Affine(_q), new G1Affine(_p));
+ return _scratch[0];
+ }
+
+ [Benchmark]
+ public long MillerLoopWithPrecomputedLines()
+ {
+ new GT(_scratch).MillerLoopLines(_lines, new G1Affine(_p));
+ return _scratch[0];
+ }
+
+ [Benchmark]
+ public long PrecomputeLines()
+ {
+ new G2Affine(_q).PrecomputeLines(_lines);
+ return _lines[0];
+ }
+
+ [Benchmark]
+ public long FinalExp()
+ {
+ _millerOut.CopyTo(_scratch.AsSpan());
+ new GT(_scratch).FinalExp();
+ return _scratch[0];
+ }
+
+ [Benchmark]
+ public bool FinalVerify()
+ => GT.FinalVerify(new GT(_millerOut), new GT(_millerOut));
+}
+
+[MemoryDiagnoser]
+public class MsmBenchmarks
+{
+ [Params(16, 128, 512)]
+ public int Npoints;
+
+ private long[] _g1Points = [];
+ private long[] _g2Points = [];
+ private long[] _g1Affines = [];
+ private long[] _g2Affines = [];
+ private byte[] _scalars = [];
+ private readonly long[] _g1Result = new long[G1.Sz];
+ private readonly long[] _g2Result = new long[G2.Sz];
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ Random rng = new(42);
+ _g1Points = new long[Npoints * G1.Sz];
+ _g2Points = new long[Npoints * G2.Sz];
+ _g1Affines = new long[Npoints * G1Affine.Sz];
+ _g2Affines = new long[Npoints * G2Affine.Sz];
+ _scalars = new byte[Npoints * 32];
+
+ for (int i = 0; i < Npoints; i++)
+ {
+ byte[] scalar = BenchmarkData.RandomScalar(rng);
+ G1 p = G1.Generator(_g1Points.AsSpan(i * G1.Sz)).Mult(scalar);
+ G2 q = G2.Generator(_g2Points.AsSpan(i * G2.Sz)).Mult(scalar);
+ new G1Affine(p).Point.CopyTo(_g1Affines.AsSpan(i * G1Affine.Sz));
+ new G2Affine(q).Point.CopyTo(_g2Affines.AsSpan(i * G2Affine.Sz));
+ BenchmarkData.RandomScalar(rng).CopyTo(_scalars.AsSpan(i * 32));
+ }
+ }
+
+ [Benchmark]
+ public long G1MultiMult()
+ {
+ new G1(_g1Result).MultiMult(_g1Points, _scalars, Npoints);
+ return _g1Result[0];
+ }
+
+ [Benchmark]
+ public long G2MultiMult()
+ {
+ new G2(_g2Result).MultiMult(_g2Points, _scalars, Npoints);
+ return _g2Result[0];
+ }
+
+ [Benchmark]
+ public long G1MultiMultAffine()
+ {
+ new G1(_g1Result).MultiMultAffine(_g1Affines, _scalars, Npoints);
+ return _g1Result[0];
+ }
+
+ [Benchmark]
+ public long G2MultiMultAffine()
+ {
+ new G2(_g2Result).MultiMultAffine(_g2Affines, _scalars, Npoints);
+ return _g2Result[0];
+ }
+}
+
+[MemoryDiagnoser]
+public class PairingCheckBenchmarks
+{
+ [Params(2, 8, 16)]
+ public int Npairs;
+
+ private long[] _qAffines = [];
+ private long[] _pAffines = [];
+ private readonly long[] _acc = new long[GT.Sz];
+ private readonly long[] _tmp = new long[GT.Sz];
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ Random rng = new(42);
+ _qAffines = new long[Npairs * G2Affine.Sz];
+ _pAffines = new long[Npairs * G1Affine.Sz];
+
+ for (int i = 0; i < Npairs; i++)
+ {
+ G1Affine p = new(G1.Generator().Mult(BenchmarkData.RandomScalar(rng)));
+ G2Affine q = new(G2.Generator().Mult(BenchmarkData.RandomScalar(rng)));
+ p.Point.CopyTo(_pAffines.AsSpan(i * G1Affine.Sz));
+ q.Point.CopyTo(_qAffines.AsSpan(i * G2Affine.Sz));
+ }
+ }
+
+ [Benchmark(Baseline = true)]
+ public bool SequentialMillerLoops()
+ {
+ GT acc = GT.One(_acc);
+ for (int i = 0; i < Npairs; i++)
+ {
+ GT t = new(_tmp);
+ t.MillerLoop(new G2Affine(_qAffines.AsSpan(i * G2Affine.Sz)), new G1Affine(_pAffines.AsSpan(i * G1Affine.Sz)));
+ acc.Mul(t);
+ }
+ return acc.FinalExp().IsOne();
+ }
+
+ [Benchmark]
+ public bool BatchedMillerLoopN()
+ {
+ GT acc = new(_acc);
+ acc.MillerLoopN(_qAffines, _pAffines, Npairs);
+ return acc.FinalExp().IsOne();
+ }
+}
diff --git a/src/Nethermind.Crypto.Bls.Bench/Nethermind.Crypto.Bls.Bench.csproj b/src/Nethermind.Crypto.Bls.Bench/Nethermind.Crypto.Bls.Bench.csproj
new file mode 100644
index 0000000..1b9f2a0
--- /dev/null
+++ b/src/Nethermind.Crypto.Bls.Bench/Nethermind.Crypto.Bls.Bench.csproj
@@ -0,0 +1,14 @@
+
+
+ Exe
+ false
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Nethermind.Crypto.Bls.Bench/Program.cs b/src/Nethermind.Crypto.Bls.Bench/Program.cs
new file mode 100644
index 0000000..5f9092c
--- /dev/null
+++ b/src/Nethermind.Crypto.Bls.Bench/Program.cs
@@ -0,0 +1,9 @@
+// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
+// SPDX-License-Identifier: MIT
+
+using BenchmarkDotNet.Running;
+
+BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
+
+public partial class Program
+{ }
diff --git a/src/Nethermind.Crypto.Bls.slnx b/src/Nethermind.Crypto.Bls.slnx
index 87cc7d9..e01f9cf 100644
--- a/src/Nethermind.Crypto.Bls.slnx
+++ b/src/Nethermind.Crypto.Bls.slnx
@@ -3,6 +3,7 @@
+
diff --git a/src/Nethermind.Crypto.Bls/Bls.cs b/src/Nethermind.Crypto.Bls/Bls.cs
index adfa432..fd769b5 100644
--- a/src/Nethermind.Crypto.Bls/Bls.cs
+++ b/src/Nethermind.Crypto.Bls/Bls.cs
@@ -7,13 +7,14 @@
// SPDX-License-Identifier: Apache-2.0
using System;
-using System.Text;
+using System.Buffers;
+using System.IO;
using System.Numerics;
-using System.Runtime.InteropServices;
using System.Reflection;
-using System.IO;
-using size_t = nuint;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+using size_t = nuint;
namespace Nethermind.Crypto;
@@ -53,7 +54,7 @@ private static nint LoadLibrary(string libraryName, Assembly assembly, DllImport
}
var arch = RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant();
- var path = Path.Combine("runtimes", $"{platform}-{arch}", "native", libraryName);
+ var path = Path.Combine(AppContext.BaseDirectory, "runtimes", $"{platform}-{arch}", "native", libraryName);
return NativeLibrary.Load(path, assembly, searchPath);
}
@@ -71,11 +72,11 @@ public enum ERROR
public class BlsException(ERROR err) : ApplicationException
{
- private readonly ERROR code = err;
+ public ERROR Error { get; } = err;
public override string Message
{
- get => code switch
+ get => Error switch
{
ERROR.BADENCODING => "bad encoding",
ERROR.POINTNOTONCURVE => "point not on curve",
@@ -96,55 +97,60 @@ public enum ByteOrder
}
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_keygen(Span key, ReadOnlySpan IKM, size_t IKM_len,
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_keygen(Span key, ReadOnlySpan IKM, size_t IKM_len,
ReadOnlySpan info, size_t info_len);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_keygen_v3(Span key, ReadOnlySpan IKM, size_t IKM_len,
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_keygen_v3(Span key, ReadOnlySpan IKM, size_t IKM_len,
ReadOnlySpan info, size_t info_len);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_keygen_v4_5(Span key, ReadOnlySpan IKM, size_t IKM_len,
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_keygen_v4_5(Span key, ReadOnlySpan IKM, size_t IKM_len,
ReadOnlySpan salt, size_t salt_len,
ReadOnlySpan info, size_t info_len);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_keygen_v5(Span key, ReadOnlySpan IKM, size_t IKM_len,
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_keygen_v5(Span key, ReadOnlySpan IKM, size_t IKM_len,
ReadOnlySpan salt, size_t salt_len,
ReadOnlySpan info, size_t info_len);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_derive_master_eip2333(Span key,
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_derive_master_eip2333(Span key,
ReadOnlySpan IKM, size_t IKM_len);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_derive_child_eip2333(Span key,
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_derive_child_eip2333(Span key,
ReadOnlySpan master, uint child_index);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_scalar_from_bendian(Span ret, ReadOnlySpan key);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_scalar_from_bendian(Span ret, ReadOnlySpan key);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_bendian_from_scalar(Span ret, ReadOnlySpan key);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_bendian_from_scalar(Span ret, ReadOnlySpan key);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[return: MarshalAs(UnmanagedType.Bool)]
- static private partial bool blst_sk_check(ReadOnlySpan key);
+ private static partial bool blst_sk_check(ReadOnlySpan key);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_scalar_from_lendian(Span key, ReadOnlySpan inp);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_scalar_from_lendian(Span key, ReadOnlySpan inp);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_lendian_from_scalar(Span key, ReadOnlySpan inp);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_lendian_from_scalar(Span key, ReadOnlySpan inp);
public readonly ref struct SecretKey
{
public readonly ReadOnlySpan Key { get => _key; }
private readonly Span _key;
+ ///
+ /// Wraps caller-provided storage as the key without copying or validation. The bytes are
+ /// interpreted in blst's internal little-endian scalar form; to parse an encoded key use
+ /// .
+ ///
[OverloadResolutionPriority(1)]
public SecretKey(Span key)
{
@@ -171,18 +177,21 @@ public SecretKey(scoped ReadOnlySpan inp, ByteOrder order = ByteOrder.BigE
public readonly void Keygen(scoped ReadOnlySpan IKM, string info = "")
{
+ ValidateIkm(IKM);
ReadOnlySpan info_bytes = Encoding.UTF8.GetBytes(info);
blst_keygen(_key, IKM, (size_t)IKM.Length,
info_bytes, (size_t)info_bytes.Length);
}
public readonly void KeygenV3(scoped ReadOnlySpan IKM, string info = "")
{
+ ValidateIkm(IKM);
ReadOnlySpan info_bytes = Encoding.UTF8.GetBytes(info);
blst_keygen_v3(_key, IKM, (size_t)IKM.Length,
info_bytes, (size_t)info_bytes.Length);
}
public readonly void KeygenV45(scoped ReadOnlySpan IKM, string salt, string info = "")
{
+ ValidateIkm(IKM);
ReadOnlySpan salt_bytes = Encoding.UTF8.GetBytes(salt);
ReadOnlySpan info_bytes = Encoding.UTF8.GetBytes(info);
blst_keygen_v4_5(_key, IKM, (size_t)IKM.Length,
@@ -191,15 +200,28 @@ public readonly void KeygenV45(scoped ReadOnlySpan IKM, string salt, strin
}
public readonly void KeygenV5(scoped ReadOnlySpan IKM, scoped ReadOnlySpan salt, string info = "")
{
+ ValidateIkm(IKM);
ReadOnlySpan info_bytes = Encoding.UTF8.GetBytes(info);
blst_keygen_v5(_key, IKM, (size_t)IKM.Length,
salt, (size_t)salt.Length,
info_bytes, (size_t)info_bytes.Length);
}
+
+ // blst silently returns an all-zero key when given less than 32 bytes of key material
+ private static void ValidateIkm(ReadOnlySpan IKM)
+ {
+ if (IKM.Length < 32)
+ {
+ throw new ArgumentException("Insufficient key material. Expected at least 32 bytes.", nameof(IKM));
+ }
+ }
public readonly void KeygenV5(scoped ReadOnlySpan IKM, string salt, string info = "")
=> KeygenV5(IKM, Encoding.UTF8.GetBytes(salt), info);
public readonly void DeriveMasterEip2333(scoped ReadOnlySpan IKM)
- => blst_derive_master_eip2333(_key, IKM, (size_t)IKM.Length);
+ {
+ ValidateIkm(IKM);
+ blst_derive_master_eip2333(_key, IKM, (size_t)IKM.Length);
+ }
public SecretKey(SecretKey master, uint childIndex)
{
_key = new byte[32];
@@ -250,37 +272,42 @@ public readonly byte[] ToLendian()
}
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_scalar_from_be_bytes(Span ret, ReadOnlySpan inp,
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_scalar_from_be_bytes(Span ret, ReadOnlySpan inp,
size_t inp_len);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_scalar_from_le_bytes(Span ret, ReadOnlySpan inp,
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_scalar_from_le_bytes(Span ret, ReadOnlySpan inp,
size_t inp_len);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[return: MarshalAs(UnmanagedType.Bool)]
- static private partial bool blst_sk_add_n_check(Span ret, ReadOnlySpan a,
+ private static partial bool blst_sk_add_n_check(Span ret, ReadOnlySpan a,
ReadOnlySpan b);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[return: MarshalAs(UnmanagedType.Bool)]
- static private partial bool blst_sk_sub_n_check(Span ret, ReadOnlySpan a,
+ private static partial bool blst_sk_sub_n_check(Span ret, ReadOnlySpan a,
ReadOnlySpan b);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[return: MarshalAs(UnmanagedType.Bool)]
- static private partial bool blst_sk_mul_n_check(Span ret, ReadOnlySpan a,
+ private static partial bool blst_sk_mul_n_check(Span ret, ReadOnlySpan a,
ReadOnlySpan b);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_sk_inverse(Span ret, ReadOnlySpan a);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_sk_inverse(Span ret, ReadOnlySpan a);
public readonly ref struct Scalar
{
public readonly ReadOnlySpan Val { get => _val; }
private readonly Span _val;
+ ///
+ /// Wraps caller-provided storage as the scalar without copying or validation. The bytes are
+ /// interpreted in blst's internal little-endian form; to parse an encoded value use
+ /// .
+ ///
[OverloadResolutionPriority(1)]
public Scalar(Span val)
{
@@ -378,50 +405,42 @@ public readonly Scalar Inverse()
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial size_t blst_p1_affine_sizeof();
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial size_t blst_p1_affine_sizeof();
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial ERROR blst_p1_deserialize(Span ret, ReadOnlySpan inp);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial ERROR blst_p1_deserialize(Span ret, ReadOnlySpan inp);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_p1_affine_serialize(Span ret, ReadOnlySpan inp);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_p1_affine_serialize(Span ret, ReadOnlySpan inp);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_p1_affine_compress(Span ret, ReadOnlySpan inp);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_p1_affine_compress(Span ret, ReadOnlySpan inp);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_p1_to_affine(Span ret, ReadOnlySpan inp);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_p1_to_affine(Span ret, ReadOnlySpan inp);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[return: MarshalAs(UnmanagedType.Bool)]
- static private partial bool blst_p1_affine_on_curve(ReadOnlySpan point);
+ private static partial bool blst_p1_affine_on_curve(ReadOnlySpan point);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[return: MarshalAs(UnmanagedType.Bool)]
- static private partial bool blst_p1_affine_in_g1(ReadOnlySpan point);
+ private static partial bool blst_p1_affine_in_g1(ReadOnlySpan point);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[return: MarshalAs(UnmanagedType.Bool)]
- static private partial bool blst_p1_affine_is_inf(ReadOnlySpan point);
+ private static partial bool blst_p1_affine_is_inf(ReadOnlySpan point);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[return: MarshalAs(UnmanagedType.Bool)]
- static private partial bool blst_p1_affine_is_equal(ReadOnlySpan a, ReadOnlySpan b);
+ private static partial bool blst_p1_affine_is_equal(ReadOnlySpan a, ReadOnlySpan b);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial IntPtr blst_p1_generator();
-
- // [LibraryImport(LibraryName)]
- // [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- // static private partial ERROR blst_core_verify_pk_in_g2(ReadOnlySpan pk, ReadOnlySpan sig,
- // [MarshalAs(UnmanagedType.Bool)] bool hash_or_encode,
- // ReadOnlySpan msg, size_t msg_len,
- // ReadOnlySpan dst, size_t dst_len,
- // ReadOnlySpan aug, size_t aug_len);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial nint blst_p1_generator();
public readonly ref struct P1Affine
{
@@ -483,6 +502,18 @@ public void Decode(scoped ReadOnlySpan inp)
}
}
+ /// Decodes raw affine coordinates without any validation, as in .
+ public void Decode(ReadOnlySpan fp1, ReadOnlySpan fp2)
+ {
+ if (fp1.Length != 48 || fp2.Length != 48)
+ {
+ throw new ArgumentException("Invalid input length. Expected 48 bytes for each field element.");
+ }
+
+ blst_fp_from_bendian(_point, fp1);
+ blst_fp_from_bendian(_point[6..], fp2);
+ }
+
public P1Affine(P1 jacobian) : this()
=> blst_p1_to_affine(_point, jacobian.Point);
@@ -510,123 +541,107 @@ public bool IsInf()
public bool IsEqual(P1Affine p)
=> blst_p1_affine_is_equal(_point, p._point);
- // ERROR core_verify(P2_Affine pk, bool hash_or_encode,
- // #pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
- // byte[] msg, string DST = "", byte[] aug = null)
- // #pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
- // {
- // byte[] dst = Encoding.UTF8.GetBytes(DST);
- // return blst_core_verify_pk_in_g2(pk.point, point,
- // hash_or_encode,
- // msg, (size_t)msg.Length,
- // dst, (size_t)dst.Length,
- // aug, (size_t)(aug != null ? aug.Length : 0));
- // }
-
public static P1Affine Generator()
{
long[] res = new long[Sz];
return Generator(res);
}
- public unsafe static P1Affine Generator(Span p)
+ public static unsafe P1Affine Generator(Span p)
{
- int s = (int)blst_p1_affine_sizeof();
- fixed (long* dest = p)
- {
- Buffer.MemoryCopy((byte*)blst_p1_generator(), dest, s, s);
- }
- return new(p);
+ P1Affine res = new(p);
+ new ReadOnlySpan((void*)blst_p1_generator(), Sz).CopyTo(res._point);
+ return res;
}
}
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_fp_from_bendian(Span ret, ReadOnlySpan a);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_fp_from_bendian(Span ret, ReadOnlySpan a);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial size_t blst_p1_sizeof();
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial size_t blst_p1_sizeof();
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_p1_serialize(Span ret, ReadOnlySpan inp);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_p1_serialize(Span ret, ReadOnlySpan inp);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_p1_compress(Span ret, ReadOnlySpan inp);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_p1_compress(Span ret, ReadOnlySpan inp);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_p1_from_affine(Span ret, ReadOnlySpan inp);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_p1_from_affine(Span ret, ReadOnlySpan inp);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[return: MarshalAs(UnmanagedType.Bool)]
- static private partial bool blst_p1_on_curve(ReadOnlySpan point);
+ private static partial bool blst_p1_on_curve(ReadOnlySpan point);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[return: MarshalAs(UnmanagedType.Bool)]
- static private partial bool blst_p1_in_g1(ReadOnlySpan point);
+ private static partial bool blst_p1_in_g1(ReadOnlySpan point);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[return: MarshalAs(UnmanagedType.Bool)]
- static private partial bool blst_p1_is_inf(ReadOnlySpan point);
+ private static partial bool blst_p1_is_inf(ReadOnlySpan point);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
[return: MarshalAs(UnmanagedType.Bool)]
- static private partial bool blst_p1_is_equal(ReadOnlySpan a, ReadOnlySpan b);
+ private static partial bool blst_p1_is_equal(ReadOnlySpan a, ReadOnlySpan b);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_sk_to_pk_in_g1(Span ret, ReadOnlySpan SK);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_sk_to_pk_in_g1(Span ret, ReadOnlySpan SK);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_map_to_g1(Span ret, ReadOnlySpan u, ReadOnlySpan v);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_map_to_g1(Span ret, ReadOnlySpan u, ReadOnlySpan v);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_encode_to_g1(Span ret, ReadOnlySpan msg, size_t msg_len,
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_encode_to_g1(Span ret, ReadOnlySpan msg, size_t msg_len,
ReadOnlySpan dst, size_t dst_len,
ReadOnlySpan aug, size_t aug_len);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_hash_to_g1(Span ret, ReadOnlySpan msg, size_t msg_len,
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_hash_to_g1(Span ret, ReadOnlySpan msg, size_t msg_len,
ReadOnlySpan dst, size_t dst_len,
ReadOnlySpan aug, size_t aug_len);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_sign_pk_in_g2(Span ret, ReadOnlySpan hash, ReadOnlySpan SK);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_sign_pk_in_g2(Span ret, ReadOnlySpan hash, ReadOnlySpan SK);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_p1_mult(Span ret, ReadOnlySpan a,
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_p1_mult(Span ret, ReadOnlySpan a,
ReadOnlySpan scalar, size_t nbits);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_p1_cneg(Span ret, [MarshalAs(UnmanagedType.Bool)] bool cbit);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_p1_cneg(Span ret, [MarshalAs(UnmanagedType.Bool)] bool cbit);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_p1_add_or_double(Span ret, ReadOnlySpan a, ReadOnlySpan b);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_p1_add_or_double(Span ret, ReadOnlySpan a, ReadOnlySpan b);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_p1_add_or_double_affine(Span ret, ReadOnlySpan a,
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_p1_add_or_double_affine(Span ret, ReadOnlySpan a,
ReadOnlySpan b);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_p1_double(Span ret, ReadOnlySpan a);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_p1_double(Span ret, ReadOnlySpan a);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial size_t blst_p1s_mult_pippenger_scratch_sizeof(size_t npoints);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial size_t blst_p1s_mult_pippenger_scratch_sizeof(size_t npoints);
// void blst_p1s_mult_pippenger(blst_p1 *ret, const blst_p1_affine *const points[],
// size_t npoints, const byte *const scalars[],
// size_t nbits, limb_t *scratch);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static unsafe partial void blst_p1s_mult_pippenger(Span ret, long** points,
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static unsafe partial void blst_p1s_mult_pippenger(Span ret, long** points,
size_t npoints, byte** scalars, size_t nbits, long* scratch);
// void blst_p1s_to_affine(blst_p1_affine dst[], const blst_p1 *const points[],
// size_t npoints);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static unsafe partial void blst_p1s_to_affine(Span dst, long** points, size_t npoints);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static unsafe partial void blst_p1s_to_affine(Span dst, long** points, size_t npoints);
public readonly ref struct P1
{
@@ -667,6 +682,13 @@ public P1(scoped ReadOnlySpan inp) : this()
public readonly void Zero()
=> _point.Clear();
+ ///
+ /// Decodes a 48-byte compressed or 96-byte uncompressed point.
+ /// Unlike , the uncompressed path reads the two field
+ /// elements as-is with no validation (no canonicality, flag-bit, curve or subgroup checks),
+ /// so that callers implementing protocols such as EIP-2537 can validate separately.
+ /// Call / where validation is required.
+ ///
public bool TryDecode(scoped ReadOnlySpan inp, out ERROR err)
{
int len = inp.Length;
@@ -697,6 +719,7 @@ public bool TryDecode(scoped ReadOnlySpan inp, out ERROR err)
return true;
}
+ /// Decodes raw affine coordinates without any validation, as in .
public void Decode(ReadOnlySpan fp1, ReadOnlySpan fp2)
{
if (fp1.Length != 48 || fp2.Length != 48)
@@ -744,11 +767,16 @@ public readonly bool IsInf()
public readonly bool IsEqual(P1 p)
=> blst_p1_is_equal(_point, p._point);
- public readonly P1 MapTo(ReadOnlySpan fp)
+ public readonly P1 MapTo(scoped ReadOnlySpan fp)
{
- long[] u = new long[6];
+ if (fp.Length != 48)
+ {
+ throw new ArgumentException("Invalid input length. Expected 48 bytes.", nameof(fp));
+ }
+
+ Span u = stackalloc long[6];
blst_fp_from_bendian(u, fp);
- blst_map_to_g1(_point, u, null);
+ blst_map_to_g1(_point, u, default);
return this;
}
public readonly P1 HashTo(scoped ReadOnlySpan msg, ReadOnlySpan DST = default, ReadOnlySpan aug = default)
@@ -822,45 +850,94 @@ public readonly P1 Mult(in BigInteger scalar)
return this;
}
- private readonly void PrepareMult(ref Scalar scalar)
+ ///
+ /// Multi-scalar multiplication over Jacobian points stored
+ /// contiguously in with 32-byte little-endian scalars stored
+ /// contiguously in . Points at infinity must be filtered out
+ /// by the caller.
+ ///
+ public readonly unsafe P1 MultiMult(scoped ReadOnlySpan rawPoints, scoped ReadOnlySpan rawScalars, int npoints)
{
- byte[] val = PrepareMult(new(scalar.ToBendian(), true, true));
- scalar.FromBendian(val);
- }
+ ArgumentOutOfRangeException.ThrowIfNegative(npoints);
+ if (rawPoints.Length < npoints * Sz)
+ {
+ throw new ArgumentException($"Insufficient points for the given count. Expected {npoints * Sz} longs.", nameof(rawPoints));
+ }
+ if (rawScalars.Length < npoints * 32)
+ {
+ throw new ArgumentException($"Insufficient scalars for the given count. Expected {npoints * 32} bytes.", nameof(rawScalars));
+ }
- private readonly unsafe P1 MultiMultRawAffines(long* rawAffinesPtr, scoped Span rawScalars, int npoints)
- {
- fixed (byte* rawScalarsPtr = rawScalars)
+ // blst does not support an empty multiplication; the result is the point at infinity
+ if (npoints == 0)
{
- long*[] rawAffinesWrapper = [rawAffinesPtr, null];
- byte*[] rawScalarsWrapper = [rawScalarsPtr, null];
+ Zero();
+ return this;
+ }
- size_t scratchSize = blst_p1s_mult_pippenger_scratch_sizeof((size_t)npoints) / sizeof(long);
- Span scratch = new long[(int)scratchSize];
+ int affinesLen = npoints * P1Affine.Sz;
+ long[] affines = ArrayPool.Shared.Rent(affinesLen);
- fixed (long** rawAffinesWrapperPtr = rawAffinesWrapper)
- fixed (byte** rawScalarsWrapperPtr = rawScalarsWrapper)
- fixed (long* scratchPtr = scratch)
- blst_p1s_mult_pippenger(_point, rawAffinesWrapperPtr, (size_t)npoints, rawScalarsWrapperPtr, 256, scratchPtr);
+ try
+ {
+ // a [ptr, null] argument tells blst to read all points from one contiguous buffer
+ fixed (long* rawPointsPtr = rawPoints)
+ {
+ long** points = stackalloc long*[2] { rawPointsPtr, null };
+ blst_p1s_to_affine(affines.AsSpan(0, affinesLen), points, (size_t)npoints);
+ }
+
+ return MultiMultAffine(affines.AsSpan(0, affinesLen), rawScalars, npoints);
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(affines);
}
- return this;
}
- // points at infinity should be filtered out, scalars little endian
- public readonly unsafe P1 MultiMult(scoped Span rawPoints, scoped Span rawScalars, int npoints)
+ ///
+ /// As , but over affine points, skipping the Jacobian-to-affine
+ /// batch conversion.
+ ///
+ public readonly unsafe P1 MultiMultAffine(scoped ReadOnlySpan rawAffines, scoped ReadOnlySpan rawScalars, int npoints)
{
- Span rawAffines = new long[npoints * 12];
+ ArgumentOutOfRangeException.ThrowIfNegative(npoints);
+ if (rawAffines.Length < npoints * P1Affine.Sz)
+ {
+ throw new ArgumentException($"Insufficient points for the given count. Expected {npoints * P1Affine.Sz} longs.", nameof(rawAffines));
+ }
+ if (rawScalars.Length < npoints * 32)
+ {
+ throw new ArgumentException($"Insufficient scalars for the given count. Expected {npoints * 32} bytes.", nameof(rawScalars));
+ }
- fixed (long* rawPointsPtr = rawPoints)
+ // blst does not support an empty multiplication; the result is the point at infinity
+ if (npoints == 0)
{
- long*[] rawPointsWrapper = [rawPointsPtr, null];
+ Zero();
+ return this;
+ }
+
+ int scratchLen = (int)(blst_p1s_mult_pippenger_scratch_sizeof((size_t)npoints) / sizeof(long));
+ long[] scratch = ArrayPool.Shared.Rent(scratchLen);
- fixed (long** rawPointsWrapperPtr = rawPointsWrapper)
- blst_p1s_to_affine(rawAffines, rawPointsWrapperPtr, (size_t)npoints);
+ try
+ {
+ fixed (long* affinesPtr = rawAffines)
+ fixed (byte* scalarsPtr = rawScalars)
+ fixed (long* scratchPtr = scratch)
+ {
+ long** points = stackalloc long*[2] { affinesPtr, null };
+ byte** scalars = stackalloc byte*[2] { scalarsPtr, null };
+ blst_p1s_mult_pippenger(_point, points, (size_t)npoints, scalars, 256, scratchPtr);
+ }
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(scratch);
}
- fixed (long* rawAffinesPtr = rawAffines)
- return MultiMultRawAffines(rawAffinesPtr, rawScalars, npoints);
+ return this;
}
public readonly P1 Cneg(bool flag) { blst_p1_cneg(_point, flag); return this; }
public readonly P1 Neg() { blst_p1_cneg(_point, true); return this; }
@@ -877,31 +954,28 @@ public static P1 Generator()
return Generator(res);
}
- public unsafe static P1 Generator(Span p)
+ public static unsafe P1 Generator(Span p)
{
- int s = (int)blst_p1_sizeof();
- fixed (long* dest = p)
- {
- Buffer.MemoryCopy((byte*)blst_p1_generator(), dest, s, s);
- }
- return new(p);
+ P1 res = new(p);
+ new ReadOnlySpan((void*)blst_p1_generator(), Sz).CopyTo(res._point);
+ return res;
}
}
public static P1 G1() => P1.Generator();
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial void blst_aggregated_in_g1(Span fp12, ReadOnlySpan p);
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial void blst_aggregated_in_g1(Span fp12, ReadOnlySpan p);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial ERROR blst_pairing_aggregate_pk_in_g1(Span fp12,
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial ERROR blst_pairing_aggregate_pk_in_g1(Span fp12,
ReadOnlySpan pk, ReadOnlySpan sig,
ReadOnlySpan msg, size_t msg_len,
ReadOnlySpan aug, size_t aug_len);
[LibraryImport(LibraryName)]
- [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
- static private partial ERROR blst_pairing_mul_n_aggregate_pk_in_g1(Span fp12,
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ private static partial ERROR blst_pairing_mul_n_aggregate_pk_in_g1(Span fp12,
ReadOnlySpan pk, ReadOnlySpan sig,
ReadOnlySpan scalar, size_t nbits,
ReadOnlySpan