-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfeldman.zig
105 lines (87 loc) · 3.64 KB
/
feldman.zig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
const std = @import("std");
const Allocator = std.mem.Allocator;
const Ristretto255 = std.crypto.ecc.Ristretto255;
const CompressedScalar = Ristretto255.scalar.CompressedScalar;
const Keccak256 = std.crypto.hash.sha3.Keccak256;
const Shamir = @import("shamir.zig");
const ShamirRistretto = Shamir.ShamirRistretto;
const FeldmanRistretto = ShamirRistretto(1);
pub const GeneratedShares = struct {
shares: std.ArrayList(FeldmanRistretto.Share),
commitments: std.ArrayList(CompressedScalar),
const Self = @This();
pub fn deinit(self: *const Self) void {
self.shares.deinit();
self.commitments.deinit();
}
};
pub const Share = FeldmanRistretto.Share;
pub const Feldman = struct {
shamir: FeldmanRistretto,
allocator: Allocator,
const Self = @This();
pub fn init(allocator: Allocator) Self {
return Self{ .shamir = FeldmanRistretto.init(allocator), .allocator = allocator };
}
pub fn generate(self: *const Self, secret: []const u8, num_shares: u8, threshold: u8) !GeneratedShares {
const generated = try self.shamir.generate(secret, num_shares, threshold);
defer {
for (generated.polynomials) |poly| {
poly.deinit();
}
}
std.debug.assert(generated.polynomials.len == 1);
var commitments = std.ArrayList([32]u8).init(self.allocator);
for (generated.polynomials[0].coefficients.items) |coeff| {
const commitment = try Ristretto255.basePoint.mul(coeff);
try commitments.append(commitment.toBytes());
}
return GeneratedShares{ .shares = generated.shares, .commitments = commitments };
}
pub fn verify(commitments: []CompressedScalar, share: *const FeldmanRistretto.Share) !bool {
var i: ?CompressedScalar = null;
var rhs = try Ristretto255.fromBytes(commitments[0]);
for (commitments, 0..) |commitment_bytes, j| {
if (j == 0) {
continue;
}
if (i == null) {
i = share.x;
} else {
i = Ristretto255.scalar.mul(i.?, share.x);
}
const commitment = try Ristretto255.fromBytes(commitment_bytes);
const c_i = try Ristretto255.mul(commitment, i.?);
rhs = Ristretto255.add(rhs, c_i);
}
const y = share.ys[0..32].*;
const lhs = try Ristretto255.basePoint.mul(y);
return Ristretto255.equivalent(lhs, rhs);
}
pub fn reconstruct(self: *const Self, shares: []FeldmanRistretto.Share) !FeldmanRistretto.Secret {
return self.shamir.reconstruct(shares);
}
};
const expect = std.testing.expect;
test "can split secret into multiple shares" {
const word_secret = "secret";
var secret: [32]u8 = undefined;
Keccak256.hash(word_secret, &secret, .{});
secret = Ristretto255.scalar.reduce(secret);
const feldman = Feldman.init(std.testing.allocator);
const generated = try feldman.generate(&secret, 3, 2);
defer generated.deinit();
const shares = generated.shares;
try expect(shares.items.len == 3);
for (shares.items) |share| {
const verified = try Feldman.verify(generated.commitments.items, &share);
try expect(verified);
}
const first_share = shares.items[0];
const second_share = shares.items[1];
var thresholds = [2]FeldmanRistretto.Share{ first_share, second_share };
const reconstructed = try feldman.reconstruct(&thresholds);
const isEqual = Ristretto255.scalar.Scalar.fromBytes(Ristretto255.scalar.sub(secret, reconstructed)).isZero();
std.debug.print("\nfeldman verification is equal: {}", .{isEqual});
try expect(isEqual);
}