Skip to content

Commit dcc5a07

Browse files
committed
Merge branch 'dev'
2 parents b58e3ba + 26d3c03 commit dcc5a07

File tree

9 files changed

+373
-1
lines changed

9 files changed

+373
-1
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ obj/*.o
88
sample/*.txt
99
!sample/CMakeLists.txt
1010
build/
11+
ffi/zig/.zig-cache/
12+
ffi/zig/zig-out/

ffi/zig/Makefile

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
all: sample
2+
3+
lib:
4+
make -C ../../ -f Makefile.onelib ETH_CFLAGS=-DBLS_ETH LIB_DIR=lib
5+
6+
sample: lib
7+
zig build
8+
9+
run: sample
10+
zig-out/bin/sample
11+
12+
fmt:
13+
zig fmt *.zig
14+
15+
test:
16+
zig build test
17+
18+
clean:
19+
\rm -rf .zig-cache/ zig-out/

ffi/zig/bls.zig

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// git clone -b release https://github.com/herumi/bls-eth-go-binary
2+
// cd bls-eth-go-binary
3+
// copy this file into bls
4+
// zig build-exe main.zig -I bls/include/ -L bls/lib/linux/amd64/ -l bls384_256 -l stdc++ -femit-bin=bls.exe && ./bls.exe
5+
6+
const std = @import("std");
7+
const bls = @cImport({
8+
@cDefine("BLS_ETH", "");
9+
// MCLBN_COMPILED_TIME_VAR should be determined by the definition of BLS_ETH,
10+
// but since this is not working as intended, we explicitly provide a magic number.
11+
@cDefine("MCLBN_COMPILED_TIME_VAR", "246");
12+
@cInclude("bls/bls384_256.h");
13+
});
14+
15+
pub const MSG_SIZE = 32;
16+
pub const Message = [MSG_SIZE]u8;
17+
18+
pub const Error = error{
19+
CantInit,
20+
BufferTooSmall, // serialize, getStr
21+
BufferTooLarge, // setLittleEndianMod, setBigEndianMod
22+
InvalidFormat, // deserialize, setStr
23+
InvalidLength,
24+
};
25+
26+
pub fn init() !void {
27+
const res = bls.blsInit(bls.MCL_BLS12_381, bls.MCLBN_COMPILED_TIME_VAR);
28+
if (res != 0) return Error.CantInit;
29+
}
30+
31+
pub const SecretKey = struct {
32+
v_: bls.blsSecretKey,
33+
pub fn setByCSPRNG(self: *SecretKey) void {
34+
const ret = bls.mclBnFr_setByCSPRNG(&self.v_.v);
35+
if (ret != 0) @panic("mclBnFr_setByCSPRNG");
36+
}
37+
pub fn serialize(self: *const SecretKey, buf: []u8) ![]u8 {
38+
const len: usize = @intCast(bls.blsSecretKeySerialize(buf.ptr, buf.len, &self.v_));
39+
if (len == 0) return Error.BufferTooSmall;
40+
return buf[0..len];
41+
}
42+
pub fn deserialize(self: *SecretKey, buf: []const u8) !void {
43+
const len: usize = @intCast(bls.blsSecretKeyDeserialize(&self.v_, buf.ptr, buf.len));
44+
if (len == 0 or len != buf.len) return Error.InvalidFormat;
45+
}
46+
// set (buf[] as littleEndian) % r
47+
pub fn setLittleEndianMod(self: *SecretKey, buf: []const u8) !void {
48+
const r = bls.mclBnFr_setLittleEndianMod(&self.v_.v, buf.ptr, buf.len);
49+
if (r < 0) return Error.BufferTooLarge;
50+
}
51+
// set (buf[] as bigEndian) % r
52+
pub fn setBigEndianMod(self: *SecretKey, buf: []const u8) !void {
53+
const r = bls.mclBnFr_setBigEndianMod(&self.v_.v, buf.ptr, buf.len);
54+
if (r < 0) return Error.BufferTooLarge;
55+
}
56+
pub fn setStr(self: *SecretKey, s: []const u8, base: i32) !void {
57+
const r = bls.mclBnFr_setStr(&self.v_.v, s.ptr, s.len, base);
58+
if (r != 0) return Error.InvalidFormat;
59+
}
60+
pub fn getStr(self: *const SecretKey, s: []u8, base: i32) ![]u8 {
61+
const len: usize = @intCast(bls.mclBnFr_getStr(s.ptr, s.len, &self.v_.v, base));
62+
if (len == 0) return Error.BufferTooSmall;
63+
return s[0..len];
64+
}
65+
pub fn getPublicKey(self: *const SecretKey, pk: *PublicKey) void {
66+
bls.blsGetPublicKey(&pk.v_, &self.v_);
67+
}
68+
pub fn sign(self: *const SecretKey, sig: *Signature, msg: []const u8) void {
69+
bls.blsSign(&sig.v_, &self.v_, msg.ptr, msg.len);
70+
}
71+
pub fn add(self: *SecretKey, rhs: *const SecretKey) void {
72+
bls.blsSecretKeyAdd(&self.v_, &rhs.v_);
73+
}
74+
};
75+
76+
pub const PublicKey = struct {
77+
v_: bls.blsPublicKey,
78+
pub fn serialize(self: *const PublicKey, buf: []u8) ![]u8 {
79+
const len: usize = @intCast(bls.blsPublicKeySerialize(buf.ptr, buf.len, &self.v_));
80+
if (len == 0) return Error.BufferTooSmall;
81+
return buf[0..len];
82+
}
83+
pub fn deserialize(self: *PublicKey, buf: []const u8) !void {
84+
const len: usize = @intCast(bls.blsPublicKeyDeserialize(&self.v_, buf.ptr, buf.len));
85+
if (len == 0 or len != buf.len) return Error.InvalidFormat;
86+
}
87+
pub fn verify(self: *const PublicKey, sig: *const Signature, msg: []const u8) bool {
88+
return bls.blsVerify(&sig.v_, &self.v_, msg.ptr, msg.len) == 1;
89+
}
90+
pub fn add(self: *PublicKey, rhs: *const PublicKey) void {
91+
bls.blsPublicKeyAdd(&self.v_, &rhs.v_);
92+
}
93+
};
94+
95+
pub const Signature = struct {
96+
v_: bls.blsSignature,
97+
// Returns a zero-length slice if the function fails.
98+
pub fn serialize(self: *const Signature, buf: []u8) ![]u8 {
99+
const len: usize = @intCast(bls.blsSignatureSerialize(buf.ptr, buf.len, &self.v_));
100+
if (len == 0) return Error.BufferTooSmall;
101+
return buf[0..len];
102+
}
103+
pub fn deserialize(self: *Signature, buf: []const u8) !void {
104+
const len: usize = @intCast(bls.blsSignatureDeserialize(&self.v_, buf.ptr, buf.len));
105+
if (len == 0 or len != buf.len) return Error.InvalidFormat;
106+
}
107+
pub fn add(self: *Signature, rhs: *const Signature) void {
108+
bls.blsSignatureAdd(&self.v_, &rhs.v_);
109+
}
110+
pub fn fastAggregateVerify(self: *const Signature, pubVec: []const PublicKey, msg: []const u8) !bool {
111+
if (pubVec.len == 0) return Error.InvalidLength;
112+
return bls.blsFastAggregateVerify(&self.v_, &pubVec[0].v_, pubVec.len, msg.ptr, msg.len) == 1;
113+
}
114+
pub fn aggregate(self: *Signature, sigVec: []const Signature) !void {
115+
if (sigVec.len == 0) return Error.InvalidLength;
116+
bls.blsAggregateSignature(&self.v_, &sigVec[0].v_, sigVec.len);
117+
}
118+
// Assume that all msgVec are different..
119+
pub fn aggregateVerifyNocheck(self: *const Signature, pubVec: []const PublicKey, msgVec: []const Message) bool {
120+
const n = pubVec.len;
121+
if (n == 0 or n != msgVec.len) return Error.InvalidLength;
122+
return bls.blsAggregateVerifyNoCheck(&self.v_, &pubVec[0].v_, &msgVec[0][0], MSG_SIZE, n) == 1;
123+
}
124+
// Check whether all msgVec are different..
125+
pub fn aggregateVerify(self: *const Signature, pubVec: []const PublicKey, msgVec: []const Message) !bool {
126+
const n = pubVec.len;
127+
if (n == 0 or n != msgVec.len) return Error.InvalidLength;
128+
if (!try areAllMessageDifferent(msgVec)) return false;
129+
return bls.blsAggregateVerifyNoCheck(&self.v_, &pubVec[0].v_, &msgVec[0][0], MSG_SIZE, n) == 1;
130+
}
131+
};
132+
133+
const MessageComp = struct {
134+
pub fn hash(self: @This(), key: Message) u64 {
135+
_ = self;
136+
return std.hash.Wyhash.hash(0, &key);
137+
}
138+
139+
pub fn eql(self: @This(), lhs: Message, rhs: Message) bool {
140+
_ = self;
141+
return std.mem.eql(u8, &lhs, &rhs);
142+
}
143+
};
144+
// Returns true if all msgVec are different.
145+
pub fn areAllMessageDifferent(msgVec: []const Message) !bool {
146+
if (msgVec.len <= 1) return true;
147+
const gpa_allocator = std.heap.page_allocator;
148+
var set = std.HashMap(Message, void, MessageComp, std.hash_map.default_max_load_percentage).init(gpa_allocator);
149+
150+
defer set.deinit();
151+
152+
for (msgVec) |msg| {
153+
const ret = try set.getOrPut(msg);
154+
if (ret.found_existing) return false;
155+
}
156+
return true;
157+
}

ffi/zig/build.zig

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
const std = @import("std");
2+
3+
fn setOption(comptime T: type, target: T, b: *std.Build) void {
4+
target.addIncludePath(b.path("../../include"));
5+
target.addIncludePath(b.path("../../mcl/include"));
6+
target.linkLibC();
7+
target.linkSystemLibrary("stdc++");
8+
target.addObjectFile(b.path("../../lib/libbls384_256.a"));
9+
}
10+
11+
pub fn build(b: *std.Build) void {
12+
const target = b.standardTargetOptions(.{});
13+
const optimize = b.standardOptimizeOption(.{});
14+
15+
const lib = b.addStaticLibrary(.{
16+
.name = "bls-zig",
17+
.root_source_file = b.path("bls.zig"),
18+
.target = target,
19+
.optimize = optimize,
20+
});
21+
lib.linkLibC();
22+
lib.addLibraryPath(b.path("../../lib"));
23+
lib.linkSystemLibrary("stdc++");
24+
lib.linkSystemLibrary("bls384_256");
25+
b.installArtifact(lib);
26+
27+
const exe = b.addExecutable(.{
28+
.name = "sample",
29+
.root_source_file = b.path("sample.zig"),
30+
.target = target,
31+
.optimize = optimize,
32+
});
33+
34+
setOption(@TypeOf(exe), exe, b);
35+
36+
// Make the executable installable
37+
b.installArtifact(exe);
38+
39+
// Create a run step
40+
const run_cmd = b.addRunArtifact(exe);
41+
run_cmd.step.dependOn(b.getInstallStep());
42+
const run_step = b.step("run", "Run the example");
43+
run_step.dependOn(&run_cmd.step);
44+
45+
// Add test build and run steps
46+
const test_exe = b.addTest(.{
47+
.root_source_file = b.path("test.zig"),
48+
.target = target,
49+
.optimize = optimize,
50+
});
51+
setOption(@TypeOf(test_exe), test_exe, b);
52+
53+
const test_cmd = b.addRunArtifact(test_exe);
54+
test_cmd.step.dependOn(&test_exe.step);
55+
const test_step = b.step("test", "Run the tests");
56+
test_step.dependOn(&test_cmd.step);
57+
}

ffi/zig/readme.md

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# How to build libbls384_256.a and Zig sample
2+
3+
```
4+
git clone --recursive https://github.com/herumi/bls
5+
cd bls
6+
make -f Makefile.onelib ETH_CFLAGS=-DBLS_ETH LIB_DIR=lib
7+
cd ffi/zig
8+
zig build
9+
zig-out/bin/sample
10+
```

ffi/zig/sample.zig

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
const std = @import("std");
2+
const bls = @import("bls.zig");
3+
4+
fn multiSig() !void {
5+
const N = 30;
6+
var skVec: [N]bls.SecretKey = undefined;
7+
var pkVec: [N]bls.PublicKey = undefined;
8+
var sigVec: [N]bls.Signature = undefined;
9+
var sig2Vec: [N]bls.Signature = undefined;
10+
var msgVec: [N]bls.Message = undefined;
11+
const msg = "doremifa";
12+
for (0..N) |i| {
13+
skVec[i].setByCSPRNG();
14+
skVec[i].getPublicKey(&pkVec[i]);
15+
skVec[i].sign(&sigVec[i], msg);
16+
if (!pkVec[i].verify(&sigVec[i], msg)) {
17+
std.debug.print("ERR verify i={}\n", .{i});
18+
return;
19+
}
20+
msgVec[i][0] = @intCast(i & 255);
21+
msgVec[i][1] = @intCast((i >> 8) & 255);
22+
@memset(msgVec[i][2..], 0);
23+
skVec[i].sign(&sig2Vec[i], &msgVec[i]);
24+
}
25+
var agg: bls.Signature = undefined;
26+
try agg.aggregate(&sigVec);
27+
// valid
28+
if (try agg.fastAggregateVerify(&pkVec, msg)) {
29+
std.debug.print("OK fastAggregateVerify\n", .{});
30+
} else {
31+
std.debug.print("ERR fastAggregateVerify\n", .{});
32+
return;
33+
}
34+
// invalid
35+
if (!try agg.fastAggregateVerify(pkVec[0 .. N - 1], msg)) {
36+
std.debug.print("OK fastAggregateVerify for invalid pk\n", .{});
37+
} else {
38+
std.debug.print("ERR fastAggregateVerify\n", .{});
39+
return;
40+
}
41+
42+
try agg.aggregate(&sig2Vec);
43+
// valid
44+
if (try agg.aggregateVerify(&pkVec, &msgVec)) {
45+
std.debug.print("OK aggregateVerify\n", .{});
46+
} else {
47+
std.debug.print("ERR aggregateVerify\n", .{});
48+
return;
49+
}
50+
// invalid
51+
msgVec[0][0] += 1;
52+
if (!try agg.aggregateVerify(&pkVec, &msgVec)) {
53+
std.debug.print("OK aggregateVerify for invalid msg\n", .{});
54+
} else {
55+
std.debug.print("ERR aggregateVerify\n", .{});
56+
return;
57+
}
58+
}
59+
60+
pub fn main() !void {
61+
try bls.init();
62+
var sk: bls.SecretKey = undefined;
63+
sk.setByCSPRNG();
64+
var buf: [128]u8 = undefined;
65+
66+
const cbuf: []u8 = try sk.serialize(buf[0..]);
67+
std.debug.print("sk:serialize={}\n", .{std.fmt.fmtSliceHexLower(cbuf)});
68+
var sk2: bls.SecretKey = undefined;
69+
try sk2.deserialize(cbuf);
70+
std.debug.print("sk2:serialize={}\n", .{std.fmt.fmtSliceHexLower(try sk2.serialize(buf[0..]))});
71+
std.debug.print("sk:getStr(10)={s}\n", .{try sk.getStr(buf[0..], 10)});
72+
std.debug.print("sk:getStr(16)=0x{s}\n", .{try sk.getStr(buf[0..], 16)});
73+
try sk.setLittleEndianMod(@as([]const u8, &.{ 1, 2, 3, 4, 5 }));
74+
std.debug.print("sk={s}\n", .{try sk.getStr(buf[0..], 16)});
75+
try sk.setBigEndianMod(@as([]const u8, &.{ 1, 2, 3, 4, 5 }));
76+
std.debug.print("sk={s}\n", .{try sk.getStr(buf[0..], 16)});
77+
try sk.setStr("1234567890123", 10);
78+
std.debug.print("sk={s}\n", .{try sk.getStr(buf[0..], 10)});
79+
var pk: bls.PublicKey = undefined;
80+
sk.getPublicKey(&pk);
81+
std.debug.print("pk={}\n", .{std.fmt.fmtSliceHexLower(try pk.serialize(buf[0..]))});
82+
const msg = "abcdefg";
83+
var sig: bls.Signature = undefined;
84+
sk.sign(&sig, msg);
85+
std.debug.print("verify={}\n", .{pk.verify(&sig, msg)});
86+
std.debug.print("verify={}\n", .{pk.verify(&sig, "abc")});
87+
try multiSig();
88+
}

ffi/zig/test.zig

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
const std = @import("std");
2+
const bls = @import("bls.zig");
3+
4+
test "init bls" {
5+
try bls.init();
6+
}
7+
const MSG_N: usize = 258;
8+
9+
test "all" {
10+
const msg1 = "bls zig test";
11+
const msg2 = "bls zig te";
12+
try std.testing.expect(signAndVerifyTest(msg1, msg1));
13+
try std.testing.expect(!signAndVerifyTest(msg1, msg2));
14+
try std.testing.expect(try areAllMessageDifferentTest(5));
15+
try std.testing.expect(try areAllMessageDifferentTest(100));
16+
try std.testing.expect(try areAllMessageDifferentTest(255));
17+
try std.testing.expect(try areAllMessageDifferentTest(256));
18+
try std.testing.expect(!try areAllMessageDifferentTest(257)); // contains the same msg
19+
}
20+
21+
fn signAndVerifyTest(msg1: []const u8, msg2: []const u8) bool {
22+
var sk: bls.SecretKey = undefined;
23+
var pk: bls.PublicKey = undefined;
24+
var sig: bls.Signature = undefined;
25+
sk.setByCSPRNG();
26+
sk.getPublicKey(&pk);
27+
sk.sign(&sig, msg1);
28+
return pk.verify(&sig, msg2);
29+
}
30+
31+
fn areAllMessageDifferentTest(n: usize) !bool {
32+
var msgVec: [MSG_N]bls.Message = undefined;
33+
for (0..MSG_N) |i| {
34+
@memset(&msgVec[i], 0);
35+
msgVec[i][1] = @intCast(i & 255);
36+
}
37+
return bls.areAllMessageDifferent(msgVec[0..n]);
38+
}

readme.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ This library is an implementation of BLS threshold signature,
66
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).
77

88
## News
9+
- 2024/Oct/23 Add a sample for Zig (See ffi/zig/).
910
- 2023/Aug/17 The performance of Sign is improved.
1011
- 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.
1112
- They may be normalized in processing but the value are not changed.

0 commit comments

Comments
 (0)