Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
herumi committed Dec 13, 2024
2 parents b58e3ba + 26d3c03 commit dcc5a07
Show file tree
Hide file tree
Showing 9 changed files with 373 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ obj/*.o
sample/*.txt
!sample/CMakeLists.txt
build/
ffi/zig/.zig-cache/
ffi/zig/zig-out/
19 changes: 19 additions & 0 deletions ffi/zig/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
all: sample

lib:
make -C ../../ -f Makefile.onelib ETH_CFLAGS=-DBLS_ETH LIB_DIR=lib

sample: lib
zig build

run: sample
zig-out/bin/sample

fmt:
zig fmt *.zig

test:
zig build test

clean:
\rm -rf .zig-cache/ zig-out/
157 changes: 157 additions & 0 deletions ffi/zig/bls.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// git clone -b release https://github.com/herumi/bls-eth-go-binary
// cd bls-eth-go-binary
// copy this file into bls
// zig build-exe main.zig -I bls/include/ -L bls/lib/linux/amd64/ -l bls384_256 -l stdc++ -femit-bin=bls.exe && ./bls.exe

const std = @import("std");
const bls = @cImport({
@cDefine("BLS_ETH", "");
// MCLBN_COMPILED_TIME_VAR should be determined by the definition of BLS_ETH,
// but since this is not working as intended, we explicitly provide a magic number.
@cDefine("MCLBN_COMPILED_TIME_VAR", "246");
@cInclude("bls/bls384_256.h");
});

pub const MSG_SIZE = 32;
pub const Message = [MSG_SIZE]u8;

pub const Error = error{
CantInit,
BufferTooSmall, // serialize, getStr
BufferTooLarge, // setLittleEndianMod, setBigEndianMod
InvalidFormat, // deserialize, setStr
InvalidLength,
};

pub fn init() !void {
const res = bls.blsInit(bls.MCL_BLS12_381, bls.MCLBN_COMPILED_TIME_VAR);
if (res != 0) return Error.CantInit;
}

pub const SecretKey = struct {
v_: bls.blsSecretKey,
pub fn setByCSPRNG(self: *SecretKey) void {
const ret = bls.mclBnFr_setByCSPRNG(&self.v_.v);
if (ret != 0) @panic("mclBnFr_setByCSPRNG");
}
pub fn serialize(self: *const SecretKey, buf: []u8) ![]u8 {
const len: usize = @intCast(bls.blsSecretKeySerialize(buf.ptr, buf.len, &self.v_));
if (len == 0) return Error.BufferTooSmall;
return buf[0..len];
}
pub fn deserialize(self: *SecretKey, buf: []const u8) !void {
const len: usize = @intCast(bls.blsSecretKeyDeserialize(&self.v_, buf.ptr, buf.len));
if (len == 0 or len != buf.len) return Error.InvalidFormat;
}
// set (buf[] as littleEndian) % r
pub fn setLittleEndianMod(self: *SecretKey, buf: []const u8) !void {
const r = bls.mclBnFr_setLittleEndianMod(&self.v_.v, buf.ptr, buf.len);
if (r < 0) return Error.BufferTooLarge;
}
// set (buf[] as bigEndian) % r
pub fn setBigEndianMod(self: *SecretKey, buf: []const u8) !void {
const r = bls.mclBnFr_setBigEndianMod(&self.v_.v, buf.ptr, buf.len);
if (r < 0) return Error.BufferTooLarge;
}
pub fn setStr(self: *SecretKey, s: []const u8, base: i32) !void {
const r = bls.mclBnFr_setStr(&self.v_.v, s.ptr, s.len, base);
if (r != 0) return Error.InvalidFormat;
}
pub fn getStr(self: *const SecretKey, s: []u8, base: i32) ![]u8 {
const len: usize = @intCast(bls.mclBnFr_getStr(s.ptr, s.len, &self.v_.v, base));
if (len == 0) return Error.BufferTooSmall;
return s[0..len];
}
pub fn getPublicKey(self: *const SecretKey, pk: *PublicKey) void {
bls.blsGetPublicKey(&pk.v_, &self.v_);
}
pub fn sign(self: *const SecretKey, sig: *Signature, msg: []const u8) void {
bls.blsSign(&sig.v_, &self.v_, msg.ptr, msg.len);
}
pub fn add(self: *SecretKey, rhs: *const SecretKey) void {
bls.blsSecretKeyAdd(&self.v_, &rhs.v_);
}
};

pub const PublicKey = struct {
v_: bls.blsPublicKey,
pub fn serialize(self: *const PublicKey, buf: []u8) ![]u8 {
const len: usize = @intCast(bls.blsPublicKeySerialize(buf.ptr, buf.len, &self.v_));
if (len == 0) return Error.BufferTooSmall;
return buf[0..len];
}
pub fn deserialize(self: *PublicKey, buf: []const u8) !void {
const len: usize = @intCast(bls.blsPublicKeyDeserialize(&self.v_, buf.ptr, buf.len));
if (len == 0 or len != buf.len) return Error.InvalidFormat;
}
pub fn verify(self: *const PublicKey, sig: *const Signature, msg: []const u8) bool {
return bls.blsVerify(&sig.v_, &self.v_, msg.ptr, msg.len) == 1;
}
pub fn add(self: *PublicKey, rhs: *const PublicKey) void {
bls.blsPublicKeyAdd(&self.v_, &rhs.v_);
}
};

pub const Signature = struct {
v_: bls.blsSignature,
// Returns a zero-length slice if the function fails.
pub fn serialize(self: *const Signature, buf: []u8) ![]u8 {
const len: usize = @intCast(bls.blsSignatureSerialize(buf.ptr, buf.len, &self.v_));
if (len == 0) return Error.BufferTooSmall;
return buf[0..len];
}
pub fn deserialize(self: *Signature, buf: []const u8) !void {
const len: usize = @intCast(bls.blsSignatureDeserialize(&self.v_, buf.ptr, buf.len));
if (len == 0 or len != buf.len) return Error.InvalidFormat;
}
pub fn add(self: *Signature, rhs: *const Signature) void {
bls.blsSignatureAdd(&self.v_, &rhs.v_);
}
pub fn fastAggregateVerify(self: *const Signature, pubVec: []const PublicKey, msg: []const u8) !bool {
if (pubVec.len == 0) return Error.InvalidLength;
return bls.blsFastAggregateVerify(&self.v_, &pubVec[0].v_, pubVec.len, msg.ptr, msg.len) == 1;
}
pub fn aggregate(self: *Signature, sigVec: []const Signature) !void {
if (sigVec.len == 0) return Error.InvalidLength;
bls.blsAggregateSignature(&self.v_, &sigVec[0].v_, sigVec.len);
}
// Assume that all msgVec are different..
pub fn aggregateVerifyNocheck(self: *const Signature, pubVec: []const PublicKey, msgVec: []const Message) bool {
const n = pubVec.len;
if (n == 0 or n != msgVec.len) return Error.InvalidLength;
return bls.blsAggregateVerifyNoCheck(&self.v_, &pubVec[0].v_, &msgVec[0][0], MSG_SIZE, n) == 1;
}
// Check whether all msgVec are different..
pub fn aggregateVerify(self: *const Signature, pubVec: []const PublicKey, msgVec: []const Message) !bool {
const n = pubVec.len;
if (n == 0 or n != msgVec.len) return Error.InvalidLength;
if (!try areAllMessageDifferent(msgVec)) return false;
return bls.blsAggregateVerifyNoCheck(&self.v_, &pubVec[0].v_, &msgVec[0][0], MSG_SIZE, n) == 1;
}
};

const MessageComp = struct {
pub fn hash(self: @This(), key: Message) u64 {
_ = self;
return std.hash.Wyhash.hash(0, &key);
}

pub fn eql(self: @This(), lhs: Message, rhs: Message) bool {
_ = self;
return std.mem.eql(u8, &lhs, &rhs);
}
};
// Returns true if all msgVec are different.
pub fn areAllMessageDifferent(msgVec: []const Message) !bool {
if (msgVec.len <= 1) return true;
const gpa_allocator = std.heap.page_allocator;
var set = std.HashMap(Message, void, MessageComp, std.hash_map.default_max_load_percentage).init(gpa_allocator);

defer set.deinit();

for (msgVec) |msg| {
const ret = try set.getOrPut(msg);
if (ret.found_existing) return false;
}
return true;
}
57 changes: 57 additions & 0 deletions ffi/zig/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
const std = @import("std");

fn setOption(comptime T: type, target: T, b: *std.Build) void {
target.addIncludePath(b.path("../../include"));
target.addIncludePath(b.path("../../mcl/include"));
target.linkLibC();
target.linkSystemLibrary("stdc++");
target.addObjectFile(b.path("../../lib/libbls384_256.a"));
}

pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});

const lib = b.addStaticLibrary(.{
.name = "bls-zig",
.root_source_file = b.path("bls.zig"),
.target = target,
.optimize = optimize,
});
lib.linkLibC();
lib.addLibraryPath(b.path("../../lib"));
lib.linkSystemLibrary("stdc++");
lib.linkSystemLibrary("bls384_256");
b.installArtifact(lib);

const exe = b.addExecutable(.{
.name = "sample",
.root_source_file = b.path("sample.zig"),
.target = target,
.optimize = optimize,
});

setOption(@TypeOf(exe), exe, b);

// Make the executable installable
b.installArtifact(exe);

// Create a run step
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
const run_step = b.step("run", "Run the example");
run_step.dependOn(&run_cmd.step);

// Add test build and run steps
const test_exe = b.addTest(.{
.root_source_file = b.path("test.zig"),
.target = target,
.optimize = optimize,
});
setOption(@TypeOf(test_exe), test_exe, b);

const test_cmd = b.addRunArtifact(test_exe);
test_cmd.step.dependOn(&test_exe.step);
const test_step = b.step("test", "Run the tests");
test_step.dependOn(&test_cmd.step);
}
10 changes: 10 additions & 0 deletions ffi/zig/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# How to build libbls384_256.a and Zig sample

```
git clone --recursive https://github.com/herumi/bls
cd bls
make -f Makefile.onelib ETH_CFLAGS=-DBLS_ETH LIB_DIR=lib
cd ffi/zig
zig build
zig-out/bin/sample
```
88 changes: 88 additions & 0 deletions ffi/zig/sample.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
const std = @import("std");
const bls = @import("bls.zig");

fn multiSig() !void {
const N = 30;
var skVec: [N]bls.SecretKey = undefined;
var pkVec: [N]bls.PublicKey = undefined;
var sigVec: [N]bls.Signature = undefined;
var sig2Vec: [N]bls.Signature = undefined;
var msgVec: [N]bls.Message = undefined;
const msg = "doremifa";
for (0..N) |i| {
skVec[i].setByCSPRNG();
skVec[i].getPublicKey(&pkVec[i]);
skVec[i].sign(&sigVec[i], msg);
if (!pkVec[i].verify(&sigVec[i], msg)) {
std.debug.print("ERR verify i={}\n", .{i});
return;
}
msgVec[i][0] = @intCast(i & 255);
msgVec[i][1] = @intCast((i >> 8) & 255);
@memset(msgVec[i][2..], 0);
skVec[i].sign(&sig2Vec[i], &msgVec[i]);
}
var agg: bls.Signature = undefined;
try agg.aggregate(&sigVec);
// valid
if (try agg.fastAggregateVerify(&pkVec, msg)) {
std.debug.print("OK fastAggregateVerify\n", .{});
} else {
std.debug.print("ERR fastAggregateVerify\n", .{});
return;
}
// invalid
if (!try agg.fastAggregateVerify(pkVec[0 .. N - 1], msg)) {
std.debug.print("OK fastAggregateVerify for invalid pk\n", .{});
} else {
std.debug.print("ERR fastAggregateVerify\n", .{});
return;
}

try agg.aggregate(&sig2Vec);
// valid
if (try agg.aggregateVerify(&pkVec, &msgVec)) {
std.debug.print("OK aggregateVerify\n", .{});
} else {
std.debug.print("ERR aggregateVerify\n", .{});
return;
}
// invalid
msgVec[0][0] += 1;
if (!try agg.aggregateVerify(&pkVec, &msgVec)) {
std.debug.print("OK aggregateVerify for invalid msg\n", .{});
} else {
std.debug.print("ERR aggregateVerify\n", .{});
return;
}
}

pub fn main() !void {
try bls.init();
var sk: bls.SecretKey = undefined;
sk.setByCSPRNG();
var buf: [128]u8 = undefined;

const cbuf: []u8 = try sk.serialize(buf[0..]);
std.debug.print("sk:serialize={}\n", .{std.fmt.fmtSliceHexLower(cbuf)});
var sk2: bls.SecretKey = undefined;
try sk2.deserialize(cbuf);
std.debug.print("sk2:serialize={}\n", .{std.fmt.fmtSliceHexLower(try sk2.serialize(buf[0..]))});
std.debug.print("sk:getStr(10)={s}\n", .{try sk.getStr(buf[0..], 10)});
std.debug.print("sk:getStr(16)=0x{s}\n", .{try sk.getStr(buf[0..], 16)});
try sk.setLittleEndianMod(@as([]const u8, &.{ 1, 2, 3, 4, 5 }));
std.debug.print("sk={s}\n", .{try sk.getStr(buf[0..], 16)});
try sk.setBigEndianMod(@as([]const u8, &.{ 1, 2, 3, 4, 5 }));
std.debug.print("sk={s}\n", .{try sk.getStr(buf[0..], 16)});
try sk.setStr("1234567890123", 10);
std.debug.print("sk={s}\n", .{try sk.getStr(buf[0..], 10)});
var pk: bls.PublicKey = undefined;
sk.getPublicKey(&pk);
std.debug.print("pk={}\n", .{std.fmt.fmtSliceHexLower(try pk.serialize(buf[0..]))});
const msg = "abcdefg";
var sig: bls.Signature = undefined;
sk.sign(&sig, msg);
std.debug.print("verify={}\n", .{pk.verify(&sig, msg)});
std.debug.print("verify={}\n", .{pk.verify(&sig, "abc")});
try multiSig();
}
38 changes: 38 additions & 0 deletions ffi/zig/test.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const std = @import("std");
const bls = @import("bls.zig");

test "init bls" {
try bls.init();
}
const MSG_N: usize = 258;

test "all" {
const msg1 = "bls zig test";
const msg2 = "bls zig te";
try std.testing.expect(signAndVerifyTest(msg1, msg1));
try std.testing.expect(!signAndVerifyTest(msg1, msg2));
try std.testing.expect(try areAllMessageDifferentTest(5));
try std.testing.expect(try areAllMessageDifferentTest(100));
try std.testing.expect(try areAllMessageDifferentTest(255));
try std.testing.expect(try areAllMessageDifferentTest(256));
try std.testing.expect(!try areAllMessageDifferentTest(257)); // contains the same msg
}

fn signAndVerifyTest(msg1: []const u8, msg2: []const u8) bool {
var sk: bls.SecretKey = undefined;
var pk: bls.PublicKey = undefined;
var sig: bls.Signature = undefined;
sk.setByCSPRNG();
sk.getPublicKey(&pk);
sk.sign(&sig, msg1);
return pk.verify(&sig, msg2);
}

fn areAllMessageDifferentTest(n: usize) !bool {
var msgVec: [MSG_N]bls.Message = undefined;
for (0..MSG_N) |i| {
@memset(&msgVec[i], 0);
msgVec[i][1] = @intCast(i & 255);
}
return bls.areAllMessageDifferent(msgVec[0..n]);
}
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This library is an implementation of BLS threshold signature,
which supports the new BLS Signatures specified at [Ethereum 2.0 Phase 0](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#bls-signatures).

## News
- 2024/Oct/23 Add a sample for Zig (See ffi/zig/).
- 2023/Aug/17 The performance of Sign is improved.
- 2022/Apr/20 The performance of MulVec got 2x speed for n >= 256, but const attribute of some arguments of MulVec and MultiVerify is removed.
- They may be normalized in processing but the value are not changed.
Expand Down

0 comments on commit dcc5a07

Please sign in to comment.