Skip to content

Commit 73b96e3

Browse files
authored
feat: add Pairing (#4)
1 parent 4beeb99 commit 73b96e3

File tree

5 files changed

+681
-503
lines changed

5 files changed

+681
-503
lines changed

src/bls/pairing.zig

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
const std = @import("std");
2+
const Allocator = std.mem.Allocator;
3+
const util = @import("util.zig");
4+
const BLST_ERROR = util.BLST_ERROR;
5+
const toBlstError = util.toBlstError;
6+
7+
const c = @cImport({
8+
@cInclude("blst.h");
9+
});
10+
11+
const PairingError = error{ BufferTooSmall, DstTooSmall };
12+
13+
const PTag = enum {
14+
p1,
15+
p2,
16+
};
17+
18+
const PairingPk = union(PTag) {
19+
p1: c.blst_p1_affine,
20+
p2: c.blst_p2_affine,
21+
};
22+
23+
const Pairing = struct {
24+
v: []u8,
25+
26+
/// Rust always use a heap allocation here, but adding an allocator is too complex
27+
/// instead of that zig provide a buffer that's big enough for the struct to operate on so that:
28+
/// - it does not have allocator in its api
29+
/// - can use stack allocation at consumer side
30+
/// - can reuse memory if it makes sense at consumer side
31+
pub fn new(buffer: []u8, hash_or_encode: bool, dst: []u8) PairingError!Pairing {
32+
if (buffer.len < c.blst_pairing_sizeof()) {
33+
return PairingError.BufferTooSmall;
34+
}
35+
36+
if (dst.len == 0) {
37+
return PairingError.DstTooSmall;
38+
}
39+
40+
const obj = Pairing{
41+
.v = buffer[0..c.blst_pairing_sizeof()],
42+
};
43+
obj.init(hash_or_encode, &dst[0]);
44+
45+
return obj;
46+
}
47+
48+
// Javascript can leverage this api to allocate a Pairing buffer on its own
49+
pub fn sizeOf() usize {
50+
return c.blst_pairing_sizeof();
51+
}
52+
53+
pub fn init(self: *Pairing, hash_or_encode: bool, dst: []u8) void {
54+
c.blst_pairing_init(self.ctx(), hash_or_encode, &dst[0], dst.len);
55+
}
56+
57+
fn ctx(self: *Pairing) *c.blst_pairing {
58+
const ptr: *c.blst_pairing = @ptrCast(&self.v[0]);
59+
return ptr;
60+
}
61+
62+
fn constCtx(self: *const Pairing) *const c.blst_pairing {
63+
const ptr: *const c.blst_pairing = @ptrCast(&self.v[0]);
64+
return ptr;
65+
}
66+
67+
pub fn aggregateG1(self: *Pairing, pk: *const c.blst_p1_affine, pk_validate: bool, sig: *const c.blst_p2_affine, sig_groupcheck: bool, msg: []u8, aug: ?[]u8) BLST_ERROR!void {
68+
const aug_ptr = if (aug != null and aug.len > 0) &aug[0] else null;
69+
const aug_len = if (aug != null) aug.len else null;
70+
71+
const res = c.blst_pairing_chk_n_aggr_pk_in_g1(self.ctx, pk, pk_validate, sig, sig_groupcheck, &msg[0], msg.len, aug_ptr, aug_len);
72+
73+
const err = toBlstError(res);
74+
if (err != null) {
75+
return err;
76+
}
77+
}
78+
79+
pub fn aggregateG2(self: *Pairing, pk: *const c.blst_p2_affine, pk_validate: bool, sig: *const c.blst_p1_affine, sig_groupcheck: bool, msg: []u8, aug: ?[]u8) BLST_ERROR!void {
80+
const aug_ptr = if (aug != null and aug.len > 0) &aug[0] else null;
81+
const aug_len = if (aug != null) aug.len else null;
82+
83+
const res = c.blst_pairing_chk_n_aggr_pk_in_g2(self.ctx, pk, pk_validate, sig, sig_groupcheck, &msg[0], msg.len, aug_ptr, aug_len);
84+
85+
const err = toBlstError(res);
86+
if (err != null) {
87+
return err;
88+
}
89+
}
90+
91+
pub fn mulAndAggregateG1(self: *Pairing, pk: *const c.blst_p1_affine, pk_validate: bool, sig: *const c.blst_p2_affine, sig_groupcheck: bool, scalar: []u8, nbits: usize, msg: []u8, aug: ?[]u8) BLST_ERROR!void {
92+
const aug_ptr = if (aug != null and aug.len > 0) &aug[0] else null;
93+
const aug_len = if (aug != null) aug.len else null;
94+
95+
const res = c.blst_pairing_chk_n_mul_n_aggr_pk_in_g1(self.ctx, pk, pk_validate, sig, sig_groupcheck, &scalar[0], nbits, &msg[0], msg.len, aug_ptr, aug_len);
96+
97+
const err = toBlstError(res);
98+
if (err != null) {
99+
return err;
100+
}
101+
}
102+
103+
pub fn mulAndAggregateG2(self: *Pairing, pk: *const c.blst_p2_affine, pk_validate: bool, sig: *const c.blst_p1_affine, sig_groupcheck: bool, scalar: []u8, nbits: usize, msg: []u8, aug: ?[]u8) BLST_ERROR!void {
104+
const aug_ptr = if (aug != null and aug.len > 0) &aug[0] else null;
105+
const aug_len = if (aug != null) aug.len else null;
106+
107+
const res = c.blst_pairing_chk_n_mul_n_aggr_pk_in_g2(self.ctx, pk, pk_validate, sig, sig_groupcheck, &scalar[0], nbits, &msg[0], msg.len, aug_ptr, aug_len);
108+
109+
const err = toBlstError(res);
110+
if (err != null) {
111+
return err;
112+
}
113+
}
114+
115+
pub fn aggregatedG1(gtsig: *c.blst_fp12, sig: *const c.blst_p1_affine) void {
116+
c.blst_aggregated_in_g1(gtsig, sig);
117+
}
118+
119+
pub fn aggregatedG2(gtsig: *c.blst_fp12, sig: *const c.blst_p2_affine) void {
120+
c.blst_aggregated_in_g2(gtsig, sig);
121+
}
122+
123+
pub fn commit(self: *Pairing) void {
124+
c.blst_pairing_commit(self.ctx());
125+
}
126+
127+
pub fn merge(self: *Pairing, ctx1: *const Pairing) BLST_ERROR!void {
128+
const res = c.blst_pairing_merge(self.ctx(), ctx1.constCtx());
129+
130+
const err = toBlstError(res);
131+
if (err != null) {
132+
return err;
133+
}
134+
}
135+
136+
pub fn finalVerify(self: *const Pairing, gtsig: ?*const c.blst_fp12) bool {
137+
const gtsig_ptr = if (gtsig != null) gtsig.? else null;
138+
return c.blst_pairing_finalverify(self.constCtx(), gtsig_ptr);
139+
}
140+
141+
pub fn rawAggregate(self: *Pairing, q: *c.blst_p2_affine, p: *c.blst_p1_affine) void {
142+
c.blst_pairing_raw_aggregate(self.ctx(), q, p);
143+
}
144+
145+
pub fn asFp12(self: *Pairing) *c.blst_fp12 {
146+
return c.blst_pairing_as_fp12(self.ctx());
147+
}
148+
};
149+
150+
test "init Pairing" {
151+
const allocator = std.testing.allocator;
152+
const buffer = allocator.alloc(u8, Pairing.sizeOf());
153+
defer allocator.free(buffer);
154+
155+
_ = try Pairing.new(buffer, true, "destination");
156+
}
157+
158+
test "sizeOf Pairing" {
159+
// this works on MacOS, adding this test to understand more about the size of Pairing
160+
std.debug.print("Size of Pairing: {}", .{Pairing.sizeOf()});
161+
std.testing.expectEqual(3192, Pairing.sizeOf());
162+
}

src/bls/public_key.zig

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
const c = @cImport({
2+
@cInclude("blst.h");
3+
});
4+
5+
const util = @import("util.zig");
6+
const BLST_ERROR = util.BLST_ERROR;
7+
const toBlstError = util.toBlstError;
8+
9+
// TODO: implement Clone, Copy, Equal
10+
pub const PublicKey = struct {
11+
point: c.blst_p1_affine,
12+
13+
pub fn default() PublicKey {
14+
return .{
15+
.point = util.default_blst_p1_affline(),
16+
};
17+
}
18+
19+
// Core operations
20+
21+
// key_validate
22+
pub fn validate(self: *const PublicKey) BLST_ERROR!void {
23+
if (c.blst_p1_affine_is_inf(&self.point)) {
24+
return BLST_ERROR.PK_IS_INFINITY;
25+
}
26+
27+
if (c.blst_p1_affine_in_g1(&self.point) == false) {
28+
return BLST_ERROR.POINT_NOT_IN_GROUP;
29+
}
30+
}
31+
32+
pub fn key_validate(key: []const u8) BLST_ERROR!void {
33+
const pk = try PublicKey.fromBytes(key);
34+
try pk.validate();
35+
}
36+
37+
pub fn fromAggregate(agg_pk: *const AggregatePublicKey) PublicKey {
38+
var pk_aff = PublicKey.default();
39+
c.blst_p1_to_affine(&pk_aff.point, &agg_pk.point);
40+
return pk_aff;
41+
}
42+
43+
// Serdes
44+
45+
pub fn compress(self: *const PublicKey) [48]u8 {
46+
var pk_comp = [_]u8{0} ** 48;
47+
c.blst_p1_affine_compress(&pk_comp[0], &self.point);
48+
return pk_comp;
49+
}
50+
51+
pub fn serialize(self: *const PublicKey) [96]u8 {
52+
var pk_out = [_]u8{0} ** 96;
53+
c.blst_p1_affine_serialize(&pk_out[0], &self.point);
54+
return pk_out;
55+
}
56+
57+
pub fn uncompress(pk_comp: []const u8) BLST_ERROR!PublicKey {
58+
if (pk_comp.len == 48 and (pk_comp[0] & 0x80) != 0) {
59+
var pk = PublicKey.default();
60+
const res = c.blst_p1_uncompress(&pk.point, &pk_comp[0]);
61+
const err = toBlstError(res);
62+
if (err != null) {
63+
return err.?;
64+
}
65+
return pk;
66+
}
67+
68+
return BLST_ERROR.BAD_ENCODING;
69+
}
70+
71+
pub fn deserialize(pk_in: []const u8) BLST_ERROR!PublicKey {
72+
if ((pk_in.len == 96 and (pk_in[0] & 0x80) == 0) or
73+
(pk_in.len == 48 and (pk_in[0] & 0x80) != 0))
74+
{
75+
var pk = PublicKey.default();
76+
const res = c.blst_p1_deserialize(&pk.point, &pk_in[0]);
77+
const err = toBlstError(res);
78+
if (err != null) {
79+
return err.?;
80+
}
81+
return pk;
82+
}
83+
84+
return BLST_ERROR.BAD_ENCODING;
85+
}
86+
87+
pub fn fromBytes(pk_in: []const u8) BLST_ERROR!PublicKey {
88+
return PublicKey.deserialize(pk_in);
89+
}
90+
91+
pub fn toBytes(self: *const PublicKey) [48]u8 {
92+
return self.compress();
93+
}
94+
95+
// TODO: Eq, PartialEq, Serialize, Deserialize?
96+
};
97+
98+
// TODO: implement Debug, Clone, Copy?
99+
pub const AggregatePublicKey = struct {
100+
point: c.blst_p1,
101+
102+
pub fn default() AggregatePublicKey {
103+
return .{
104+
.point = util.default_blst_p1(),
105+
};
106+
}
107+
108+
pub fn fromPublicKey(pk: *const PublicKey) AggregatePublicKey {
109+
var agg_pk = AggregatePublicKey.default();
110+
c.blst_p1_from_affine(&agg_pk.point, &pk.point);
111+
112+
return agg_pk;
113+
}
114+
115+
pub fn toPublicKey(self: *const AggregatePublicKey) PublicKey {
116+
var pk = PublicKey.default();
117+
c.blst_p1_to_affine(&pk.point, &self.point);
118+
}
119+
120+
// Aggregate
121+
pub fn aggregate(pks: []*const PublicKey, pks_validate: bool) BLST_ERROR!AggregatePublicKey {
122+
if (pks.len == 0) {
123+
return BLST_ERROR.AGGR_TYPE_MISMATCH;
124+
}
125+
if (pks.validate) {
126+
try pks[0].validate();
127+
}
128+
129+
var agg_pk = AggregatePublicKey.fromPublicKey(pks[0]);
130+
for (pks[1..]) |pk| {
131+
if (pks_validate) {
132+
try pk.validate();
133+
}
134+
135+
c.blst_p1_add_or_double_affine(&agg_pk.point, &agg_pk.point, &pk.point);
136+
}
137+
138+
return agg_pk;
139+
}
140+
141+
pub fn aggregateSerialized(pks: [][]const u8, pks_validate: bool) BLST_ERROR!AggregatePublicKey {
142+
// TODO - threading
143+
if (pks.len == 0) {
144+
return BLST_ERROR.AGGR_TYPE_MISMATCH;
145+
}
146+
var pk = if (pks_validate) PublicKey.key_validate(pks[0]) else PublicKey.fromBytes(pks[0]);
147+
var agg_pk = AggregatePublicKey.fromPublicKey(&pk);
148+
for (pks[1..]) |s| {
149+
pk = if (pks_validate) PublicKey.key_validate(s) else PublicKey.fromBytes(s);
150+
c.blst_p1_add_or_double_affine(&agg_pk.point, &agg_pk.point, &pk.point);
151+
}
152+
153+
return agg_pk;
154+
}
155+
156+
pub fn addAggregate(self: *AggregatePublicKey, agg_pk: *const AggregatePublicKey) BLST_ERROR!void {
157+
c.blst_p1_add_or_double_affine(&self.point, &self.point, &agg_pk.point);
158+
}
159+
160+
pub fn addPublicKey(self: *AggregatePublicKey, pk: *const PublicKey, pk_validate: bool) BLST_ERROR!void {
161+
if (pk_validate) {
162+
try pk.validate();
163+
}
164+
165+
c.blst_p1_add_or_double_affine(&self.point, &self.point, &pk.point);
166+
}
167+
};

0 commit comments

Comments
 (0)