Skip to content

Instantly share code, notes, and snippets.

@daurnimator
Last active December 22, 2019 14:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save daurnimator/21252e94cbdb233349cf95814f9bdf40 to your computer and use it in GitHub Desktop.
Save daurnimator/21252e94cbdb233349cf95814f9bdf40 to your computer and use it in GitHub Desktop.
// zig test bls.zig -I/usr/include -lc -lstdc++ -L/usr/lib -lbls_c256
const std = @import("std");
const assert = std.debug.assert;
const testing = std.testing;
const BLS = struct {
const curves = enum {
bls_c256,
bls_c384,
bls_c384_256,
};
fn blsLibrary(comptime curve: curves) type {
return struct {
pub usingnamespace @cImport({
@cDefine("MCLBN_FP_UNIT_SIZE", switch (curve) {
.bls_c256 => "4",
.bls_c384 => "6",
.bls_c384_256 => "6",
// .bls_c512 => "8",
});
if (curve == .bls_c384_256) @cDefine("MCLBN_FR_UNIT_SIZE", "4");
@cInclude("bls/bls.h");
});
pub const MCLBN_COMPILED_TIME_VAR = MCLBN_FR_UNIT_SIZE * 10 + MCLBN_FP_UNIT_SIZE;
};
}
const bls = blsLibrary(.bls_c256);
const ID_SIZE = 32;
const SECRETKEY_SIZE = 32;
const PUBLICKEY_SIZE = 64;
const SIGNATURE_SIZE = 32;
pub fn init() void {
if (bls.blsInit(bls.MCL_BN254, bls.MCLBN_COMPILED_TIME_VAR) != 0) @panic("BLS library mismatch");
}
pub fn signatureVerifyOrder(doVerify: bool) void {
bls.blsSignatureVerifyOrder(@boolToInt(doVerify));
}
pub fn publicKeyVerifyOrder(doVerify: bool) void {
bls.blsPublicKeyVerifyOrder(@boolToInt(doVerify));
}
pub fn setETHserialization(doVerify: bool) void {
bls.blsSetETHserialization(@boolToInt(doVerify));
}
pub const Id = extern struct {
id: bls.blsId,
pub fn initInteger(n: u31) Id {
var id: bls.blsId = undefined;
bls.blsIdSetInt(&id, n);
return .{ .id = id };
}
pub fn initDeserialize(data: [ID_SIZE]u8) !Id {
var id: bls.blsId = undefined;
if (bls.blsIdDeserialize(&id, &data, data.len) != data.len) return error.InvalidData;
return Id{ .id = id };
}
pub fn initRandom() !Id {
var id: bls.blsId = undefined;
// blsId is the same internal type as a blsSecretKey
if (bls.blsSecretKeySetByCSPRNG(@ptrCast(*bls.blsSecretKey, &id)) != 0) return error.RandomGenerationFailed;
return Id{ .id = id };
}
pub fn eql(a: Id, b: Id) bool {
return @intCast(u1, bls.blsIdIsEqual(&a.id, &b.id)) != 0;
}
pub fn serialize(self: Id) [ID_SIZE]u8 {
var buf: [ID_SIZE]u8 = undefined;
const len = bls.blsIdSerialize(&buf, buf.len, &self.id);
assert(len == buf.len);
return buf;
}
};
pub const SecretKey = extern struct {
key: bls.blsSecretKey,
pub fn initDeserialize(data: [SECRETKEY_SIZE]u8) !SecretKey {
var sec: bls.blsSecretKey = undefined;
if (bls.blsSecretKeyDeserialize(&sec, &data, data.len) != data.len) return error.InvalidData;
return SecretKey{ .key = sec };
}
pub fn initRandom() !SecretKey {
var sec: bls.blsSecretKey = undefined;
if (bls.blsSecretKeySetByCSPRNG(&sec) != 0) return error.RandomGenerationFailed;
return SecretKey{ .key = sec };
}
pub fn initFromShare(master_keys: []const SecretKey, id: Id) SecretKey {
var sec: bls.blsSecretKey = undefined;
if (bls.blsSecretKeyShare(
&sec,
@ptrCast([*]const bls.blsSecretKey, master_keys.ptr),
master_keys.len,
&id.id,
) != 0) unreachable;
return .{ .key = sec };
}
pub fn initRecover(secret_keys: []const SecretKey, id: []const Id) !SecretKey {
assert(secret_keys.len == id.len);
var sec: bls.blsSecretKey = undefined;
if (bls.blsSecretKeyRecover(
&sec,
@ptrCast([*]const bls.blsSecretKey, secret_keys.ptr),
@ptrCast([*]const bls.blsId, id.ptr),
id.len,
) != 0) return error.RecoveryFailed;
return SecretKey{ .key = sec };
}
pub fn eql(a: SecretKey, b: SecretKey) bool {
return @intCast(u1, bls.blsSecretKeyIsEqual(&a.key, &b.key)) != 0;
}
pub fn add(self: *SecretKey, b: SecretKey) void {
bls.blsSecretKeyAdd(&self.key, &b.key);
}
pub fn serialize(self: SecretKey) [SECRETKEY_SIZE]u8 {
assert(bls.blsGetSerializedSecretKeyByteSize() == SECRETKEY_SIZE);
var buf: [SECRETKEY_SIZE]u8 = undefined;
_ = bls.blsSecretKeySerialize(&buf, buf.len, &self.key);
return buf;
}
pub fn getPublicKey(self: SecretKey) PublicKey {
var pubkey: bls.blsPublicKey = undefined;
bls.blsGetPublicKey(&pubkey, &self.key);
return .{ .pubkey = pubkey };
}
pub fn sign(self: SecretKey, msg: []const u8) Signature {
var sig: bls.blsSignature = undefined;
bls.blsSign(&sig, &self.key, msg.ptr, msg.len);
return .{ .sig = sig };
}
};
pub const PublicKey = extern struct {
pubkey: bls.blsPublicKey,
pub fn initDeserialize(data: [PUBLICKEY_SIZE]u8) !PublicKey {
var pubkey: bls.blsPublicKey = undefined;
if (bls.blsPublicKeyDeserialize(&pubkey, &data, data.len) != data.len) return error.InvalidData;
return PublicKey{ .pubkey = pubkey };
}
pub fn initFromShare(public_keys: []const PublicKey, id: Id) PublicKey {
var pubkey: bls.blsPublicKey = undefined;
if (bls.blsPublicKeyShare(
&pubkey,
@ptrCast([*]const bls.blsPublicKey, public_keys.ptr),
public_keys.len,
&id.id,
) != 0) unreachable;
return .{ .pubkey = pubkey };
}
pub fn initRecover(public_keys: []const PublicKey, id: []const Id) !PublicKey {
assert(public_keys.len == id.len);
var pubkey: bls.blsPublicKey = undefined;
if (bls.blsPublicKeyRecover(
&pubkey,
@ptrCast([*]const bls.blsPublicKey, public_keys.ptr),
@ptrCast([*]const bls.blsId, id.ptr),
id.len,
) != 0) return error.RecoveryFailed;
return PublicKey{ .pubkey = pubkey };
}
pub fn eql(a: PublicKey, b: PublicKey) bool {
return @intCast(u1, bls.blsPublicKeyIsEqual(&a.pubkey, &b.pubkey)) != 0;
}
pub fn add(self: *PublicKey, b: PublicKey) void {
bls.blsPublicKeyAdd(&self.pubkey, &b.pubkey);
}
pub fn serialize(self: PublicKey) [PUBLICKEY_SIZE]u8 {
assert(bls.blsGetSerializedPublicKeyByteSize() == PUBLICKEY_SIZE);
var buf: [PUBLICKEY_SIZE]u8 = undefined;
_ = bls.blsPublicKeySerialize(&buf, buf.len, &self.pubkey);
return buf;
}
};
pub const Signature = extern struct {
sig: bls.blsSignature,
pub fn initDeserialize(data: [SIGNATURE_SIZE]u8) !Signature {
var sig: bls.blsSignature = undefined;
if (bls.blsSignatureDeserialize(&sig, &data, data.len) != data.len) return error.InvalidData;
return Signature{ .sig = sig };
}
pub fn initRecover(signatures: []const Signature, id: []const Id) !Signature {
assert(signatures.len == id.len);
var sig: bls.blsSignature = undefined;
if (bls.blsSignatureRecover(
&sig,
@ptrCast([*]const bls.blsSignature, signatures.ptr),
@ptrCast([*]const bls.blsId, id.ptr),
id.len,
) != 0) return error.RecoveryFailed;
return Signature{ .sig = sig };
}
pub fn eql(a: Signature, b: Signature) bool {
return @intCast(u1, bls.blsSignatureIsEqual(&a.sig, &b.sig)) != 0;
}
pub fn add(self: *Signature, b: Signature) void {
bls.blsSignatureAdd(&self.sig, &b.sig);
}
pub fn serialize(self: Signature) [SIGNATURE_SIZE]u8 {
assert(bls.blsGetSerializedSignatureByteSize() == SIGNATURE_SIZE);
var buf: [SIGNATURE_SIZE]u8 = undefined;
_ = bls.blsSignatureSerialize(&buf, buf.len, &self.sig);
return buf;
}
pub fn verify(self: Signature, pubkey: PublicKey, msg: []const u8) bool {
return @intCast(u1, bls.blsVerify(&self.sig, &pubkey.pubkey, msg.ptr, msg.len)) != 0;
}
};
};
test "BLS" {
BLS.init();
BLS.signatureVerifyOrder(false);
BLS.publicKeyVerifyOrder(false);
BLS.setETHserialization(true);
const THRESHOLD = 2;
const MEMBERS = 3;
const master_keys = blk: { // generate master keys: number is the threshold for signing
var keys: [THRESHOLD]BLS.SecretKey = undefined;
for (keys) |*sec| {
sec.* = try BLS.SecretKey.initRandom();
}
break :blk keys;
};
const member_secret_keys = blk: { // key sharing
var keys: [MEMBERS]BLS.SecretKey = undefined;
for (keys) |*sec, i| {
const id = BLS.Id.initInteger(@intCast(u31, i));
sec.* = BLS.SecretKey.initFromShare(&master_keys, id);
}
break :blk keys;
};
const member_public_keys = blk: { // key sharing
var keys: [MEMBERS]BLS.PublicKey = undefined;
for (keys) |*pubkey, i| {
pubkey.* = member_secret_keys[i].getPublicKey();
}
break :blk keys;
};
const msg = "abc";
// have keys 1 and 2 sign
const signatures = [2]BLS.Signature{ member_secret_keys[1].sign(msg), member_secret_keys[2].sign(msg) };
testing.expect(signatures[0].verify(member_public_keys[1], msg));
testing.expect(signatures[1].verify(member_public_keys[2], msg));
{ // recover
const subIdVec = [_]BLS.Id{ BLS.Id.initInteger(1), BLS.Id.initInteger(2) };
const subSecVec = [_]BLS.SecretKey{ member_secret_keys[1], member_secret_keys[2] };
const subPubVec = [_]BLS.PublicKey{ member_public_keys[1], member_public_keys[2] };
const sec = try BLS.SecretKey.initRecover(&subSecVec, &subIdVec);
testing.expectEqual(master_keys[0].serialize(), sec.serialize());
testing.expect(BLS.SecretKey.eql(master_keys[0], sec));
const pubkey = try BLS.PublicKey.initRecover(&subPubVec, &subIdVec);
testing.expectEqual(master_keys[0].getPublicKey().serialize(), pubkey.serialize());
testing.expect(BLS.PublicKey.eql(master_keys[0].getPublicKey(), pubkey));
const sig = try BLS.Signature.initRecover(&signatures, &subIdVec);
testing.expect(sig.verify(pubkey, msg));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment