Last active
December 22, 2019 14:24
-
-
Save daurnimator/21252e94cbdb233349cf95814f9bdf40 to your computer and use it in GitHub Desktop.
Calling https://github.com/herumi/bls from zig
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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