confium 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +152 -0
- data/Cargo.lock +2634 -0
- data/Cargo.toml +9 -0
- data/README.adoc +114 -14
- data/Rakefile +11 -6
- data/confium.gemspec +50 -29
- data/ext/confium_native/Cargo.toml +63 -0
- data/ext/confium_native/build.rs +72 -0
- data/ext/confium_native/extconf.rb +10 -0
- data/ext/confium_native/src/attributes.rs +88 -0
- data/ext/confium_native/src/audit.rs +169 -0
- data/ext/confium_native/src/composite.rs +302 -0
- data/ext/confium_native/src/deployment.rs +176 -0
- data/ext/confium_native/src/ers.rs +93 -0
- data/ext/confium_native/src/lib.rs +56 -0
- data/ext/confium_native/src/openpgp.rs +55 -0
- data/ext/confium_native/src/path.rs +118 -0
- data/ext/confium_native/src/pki.rs +431 -0
- data/ext/confium_native/src/tc.rs +420 -0
- data/ext/confium_native/src/transparency.rs +343 -0
- data/ext/confium_native/src/util.rs +201 -0
- data/lib/confium/audit.rb +125 -0
- data/lib/confium/cfm.rb +4 -5
- data/lib/confium/crypto.rb +50 -0
- data/lib/confium/digest.rb +11 -9
- data/lib/confium/errors/coerce.rb +47 -0
- data/lib/confium/errors/crypto_error.rb +15 -0
- data/lib/confium/errors/index_error.rb +15 -0
- data/lib/confium/errors/not_found_error.rb +15 -0
- data/lib/confium/errors/parse_error.rb +15 -0
- data/lib/confium/errors/policy_violation_error.rb +15 -0
- data/lib/confium/errors/threshold_error.rb +16 -0
- data/lib/confium/errors/unresolved_signer_error.rb +14 -0
- data/lib/confium/errors/validation_error.rb +17 -0
- data/lib/confium/errors/verification_error.rb +15 -0
- data/lib/confium/errors.rb +26 -0
- data/lib/confium/ffi.rb +23 -0
- data/lib/confium/lib.rb +18 -56
- data/lib/confium/openpgp.rb +34 -0
- data/lib/confium/pki/certificate_builder.rb +60 -0
- data/lib/confium/pki/cms/signed_data_builder.rb +92 -0
- data/lib/confium/pki/cms.rb +15 -0
- data/lib/confium/pki/cnml.rb +80 -0
- data/lib/confium/pki.rb +13 -0
- data/lib/confium/policy.rb +138 -0
- data/lib/confium/secure_bytes.rb +126 -0
- data/lib/confium/tc/coordinator.rb +68 -0
- data/lib/confium/tc/session.rb +51 -0
- data/lib/confium/tc/session_stub.rb +43 -0
- data/lib/confium/tc/share_file.rb +87 -0
- data/lib/confium/tc.rb +17 -0
- data/lib/confium/transparency/ots.rb +63 -0
- data/lib/confium/version.rb +1 -1
- data/lib/confium.rb +50 -20
- metadata +142 -25
- data/CODE_OF_CONDUCT.md +0 -84
- data/Gemfile +0 -10
- data/sig/confium.rbs +0 -4
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
//! Confium::TC — threshold cryptography surface for Ruby.
|
|
2
|
+
//!
|
|
3
|
+
//! Phase 1D-1 + 1D-2 scope: real P-256 Shamir secret sharing + keypair
|
|
4
|
+
//! generation + single-party sign + threshold ElGamal-P256 (encapsulate
|
|
5
|
+
//! / partial_decrypt / aggregate_partials). Multi-party FROST/CMP20/GG18
|
|
6
|
+
//! session orchestration lands in Phase 1D-3.
|
|
7
|
+
|
|
8
|
+
use confium_tc_elgamal_p256::{
|
|
9
|
+
aggregate_partials, encapsulate, partial_decrypt,
|
|
10
|
+
Ciphertext as ElGamalCiphertext, DecryptionShare, PartialDecryption,
|
|
11
|
+
PublicKey as ElGamalPublicKey,
|
|
12
|
+
};
|
|
13
|
+
use confium_tc_frost_p256::{
|
|
14
|
+
generate_keypair, public_key_for,
|
|
15
|
+
scalar::{scalar_from_bytes, scalar_to_bytes},
|
|
16
|
+
shamir::{recover_secret, split_secret, Share},
|
|
17
|
+
sign_message,
|
|
18
|
+
Keypair,
|
|
19
|
+
};
|
|
20
|
+
use confium_tc_cmp20::inprocess as cmp20_inprocess;
|
|
21
|
+
use confium_tc_gg18::inprocess as gg18_inprocess;
|
|
22
|
+
use magnus::{exception, function, method, prelude::*, DataTypeFunctions, Error, Module, Object, RHash, Ruby, TryConvert, TypedData, Value};
|
|
23
|
+
use p256::Scalar;
|
|
24
|
+
|
|
25
|
+
use crate::util::threshold_error;
|
|
26
|
+
|
|
27
|
+
#[derive(TypedData, DataTypeFunctions)]
|
|
28
|
+
#[magnus(class = "Confium::TC::FrostP256::Share", size)]
|
|
29
|
+
pub struct ShareWrap {
|
|
30
|
+
pub x: u32,
|
|
31
|
+
pub y_bytes: Vec<u8>,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
impl ShareWrap {
|
|
35
|
+
fn x(&self) -> u32 {
|
|
36
|
+
self.x
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
fn y_bytes(&self) -> Result<magnus::RString, Error> {
|
|
40
|
+
let ruby = Ruby::get().map_err(|e| Error::new(exception::runtime_error(), e.to_string()))?;
|
|
41
|
+
Ok(bytes_to_rstring(&ruby, &self.y_bytes))
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
fn split_secret_into_shares(
|
|
46
|
+
secret_bytes: Value,
|
|
47
|
+
threshold: u32,
|
|
48
|
+
party_count: u32,
|
|
49
|
+
) -> Result<magnus::RArray, Error> {
|
|
50
|
+
let bytes = bytes_from_value(secret_bytes)?;
|
|
51
|
+
if bytes.len() != 32 {
|
|
52
|
+
return Err(Error::new(
|
|
53
|
+
exception::arg_error(),
|
|
54
|
+
format!("secret must be exactly 32 bytes, got {}", bytes.len()),
|
|
55
|
+
));
|
|
56
|
+
}
|
|
57
|
+
let secret_arr: [u8; 32] = bytes.as_slice().try_into().unwrap();
|
|
58
|
+
let secret = scalar_from_bytes(&secret_arr).ok_or_else(|| {
|
|
59
|
+
Error::new(
|
|
60
|
+
exception::arg_error(),
|
|
61
|
+
"secret is not a valid P-256 scalar (reduce mod n failed)",
|
|
62
|
+
)
|
|
63
|
+
})?;
|
|
64
|
+
let shares: Vec<Share> = split_secret(&secret, threshold, party_count);
|
|
65
|
+
let ruby = Ruby::get().map_err(|e| Error::new(exception::runtime_error(), e.to_string()))?;
|
|
66
|
+
let result = ruby.ary_new_capa(shares.len());
|
|
67
|
+
for s in shares {
|
|
68
|
+
result.push(ruby.obj_wrap(ShareWrap {
|
|
69
|
+
x: s.x,
|
|
70
|
+
y_bytes: scalar_to_bytes(&s.y).to_vec(),
|
|
71
|
+
}))?;
|
|
72
|
+
}
|
|
73
|
+
Ok(result)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
fn recover(shares_value: Value) -> Result<magnus::RString, Error> {
|
|
77
|
+
let arr = magnus::RArray::try_convert(shares_value)?;
|
|
78
|
+
let mut shares: Vec<Share> = Vec::with_capacity(arr.len());
|
|
79
|
+
for v in arr.each() {
|
|
80
|
+
let h: RHash = RHash::try_convert(v?)?;
|
|
81
|
+
let x: u32 = h.fetch::<_, u32>("x")?;
|
|
82
|
+
let y_value: Value = h.fetch::<_, Value>("y")?;
|
|
83
|
+
let y_bytes = bytes_from_value(y_value)?;
|
|
84
|
+
if y_bytes.len() != 32 {
|
|
85
|
+
return Err(Error::new(
|
|
86
|
+
exception::arg_error(),
|
|
87
|
+
format!("share y must be 32 bytes, got {}", y_bytes.len()),
|
|
88
|
+
));
|
|
89
|
+
}
|
|
90
|
+
let y_arr: [u8; 32] = y_bytes.as_slice().try_into().unwrap();
|
|
91
|
+
let y = scalar_from_bytes(&y_arr).ok_or_else(|| {
|
|
92
|
+
Error::new(exception::arg_error(), "share y is not a valid P-256 scalar")
|
|
93
|
+
})?;
|
|
94
|
+
shares.push(Share { x, y });
|
|
95
|
+
}
|
|
96
|
+
let refs: Vec<&Share> = shares.iter().collect();
|
|
97
|
+
let secret = recover_secret(&refs)
|
|
98
|
+
.map_err(|e| Error::new(exception::runtime_error(), e.to_string()))?;
|
|
99
|
+
let ruby = Ruby::get().map_err(|e| Error::new(exception::runtime_error(), e.to_string()))?;
|
|
100
|
+
Ok(bytes_to_rstring(&ruby, scalar_to_bytes(&secret).as_ref()))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
fn keypair(ruby: &Ruby) -> Result<RHash, Error> {
|
|
104
|
+
let kp = generate_keypair();
|
|
105
|
+
let result = ruby.hash_new();
|
|
106
|
+
let signing_bytes = kp.to_signing_key().to_bytes();
|
|
107
|
+
let verifying_bytes = kp.to_verifying_key().to_sec1_bytes();
|
|
108
|
+
result.aset("private_key", bytes_to_rstring(ruby, &signing_bytes))?;
|
|
109
|
+
result.aset("public_key", bytes_to_rstring(ruby, &verifying_bytes))?;
|
|
110
|
+
Ok(result)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
fn sign(private_key_bytes: Value, message: Value) -> Result<RHash, Error> {
|
|
114
|
+
let pk_bytes = bytes_from_value(private_key_bytes)?;
|
|
115
|
+
let msg = bytes_from_value(message)?;
|
|
116
|
+
if pk_bytes.len() != 32 {
|
|
117
|
+
return Err(Error::new(
|
|
118
|
+
exception::arg_error(),
|
|
119
|
+
format!("private key must be 32 bytes, got {}", pk_bytes.len()),
|
|
120
|
+
));
|
|
121
|
+
}
|
|
122
|
+
let pk_arr: [u8; 32] = pk_bytes.as_slice().try_into().unwrap();
|
|
123
|
+
let secret = scalar_from_bytes(&pk_arr).ok_or_else(|| {
|
|
124
|
+
Error::new(exception::arg_error(), "private key not a valid P-256 scalar")
|
|
125
|
+
})?;
|
|
126
|
+
let kp = Keypair {
|
|
127
|
+
secret_scalar: secret,
|
|
128
|
+
public_key: public_key_for(&secret),
|
|
129
|
+
};
|
|
130
|
+
let signed = match sign_message(&kp, &msg) {
|
|
131
|
+
Ok(s) => s,
|
|
132
|
+
Err(e) => {
|
|
133
|
+
crate::audit::fire_event(
|
|
134
|
+
"tc_frost_p256_sign",
|
|
135
|
+
"failure",
|
|
136
|
+
Some("FROST-P256"),
|
|
137
|
+
Some(&msg),
|
|
138
|
+
Some(&e.to_string()),
|
|
139
|
+
);
|
|
140
|
+
return Err(Error::new(exception::runtime_error(), e.to_string()));
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
crate::audit::fire_event(
|
|
144
|
+
"tc_frost_p256_sign",
|
|
145
|
+
"success",
|
|
146
|
+
Some("FROST-P256"),
|
|
147
|
+
Some(&msg),
|
|
148
|
+
None,
|
|
149
|
+
);
|
|
150
|
+
let ruby = Ruby::get().map_err(|e| Error::new(exception::runtime_error(), e.to_string()))?;
|
|
151
|
+
let result = ruby.hash_new();
|
|
152
|
+
result.aset("der", bytes_to_rstring(&ruby, &signed.der_bytes))?;
|
|
153
|
+
result.aset("fixed", bytes_to_rstring(&ruby, &signed.fixed_bytes))?;
|
|
154
|
+
Ok(result)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
fn bytes_from_value(v: Value) -> Result<Vec<u8>, Error> {
|
|
158
|
+
use magnus::RString;
|
|
159
|
+
if let Ok(s) = RString::try_convert(v) {
|
|
160
|
+
return Ok(unsafe { s.as_slice() }.to_vec());
|
|
161
|
+
}
|
|
162
|
+
let arr: Vec<i64> = Vec::<i64>::try_convert(v)?;
|
|
163
|
+
arr.into_iter()
|
|
164
|
+
.map(|i| {
|
|
165
|
+
if !(0..=255).contains(&i) {
|
|
166
|
+
Err(Error::new(
|
|
167
|
+
exception::arg_error(),
|
|
168
|
+
format!("byte out of range 0..255: {i}"),
|
|
169
|
+
))
|
|
170
|
+
} else {
|
|
171
|
+
Ok(i as u8)
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
.collect()
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
fn bytes_to_rstring(_ruby: &Ruby, bytes: &[u8]) -> magnus::RString {
|
|
178
|
+
let s = magnus::RString::buf_new(0);
|
|
179
|
+
s.cat(bytes);
|
|
180
|
+
s
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ===== ElGamal-P256 threshold encryption (KEM-style) =====
|
|
184
|
+
|
|
185
|
+
fn elgamal_encapsulate(ruby: &Ruby, public_key_bytes: Value) -> Result<RHash, Error> {
|
|
186
|
+
let bytes = bytes_from_value(public_key_bytes)?;
|
|
187
|
+
let pk = ElGamalPublicKey { bytes };
|
|
188
|
+
let (ciphertext, shared_secret) = encapsulate(&pk)
|
|
189
|
+
.map_err(|e| Error::new(exception::runtime_error(), e.to_string()))?;
|
|
190
|
+
let result = ruby.hash_new();
|
|
191
|
+
let ct_hash = ruby.hash_new();
|
|
192
|
+
ct_hash.aset("c1", bytes_to_rstring(ruby, &ciphertext.c1))?;
|
|
193
|
+
ct_hash.aset("c2", bytes_to_rstring(ruby, &ciphertext.c2))?;
|
|
194
|
+
result.aset("ciphertext", ct_hash)?;
|
|
195
|
+
result.aset("shared_secret", bytes_to_rstring(ruby, &shared_secret))?;
|
|
196
|
+
Ok(result)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
fn elgamal_partial_decrypt(party_index: u32, share_bytes: Value, ciphertext_value: Value) -> Result<RHash, Error> {
|
|
200
|
+
let share_b = bytes_from_value(share_bytes)?;
|
|
201
|
+
let ct_hash: RHash = RHash::try_convert(ciphertext_value)?;
|
|
202
|
+
let c1_value: Value = ct_hash.fetch::<_, Value>("c1")?;
|
|
203
|
+
let c2_value: Value = ct_hash.fetch::<_, Value>("c2")?;
|
|
204
|
+
let ciphertext = ElGamalCiphertext {
|
|
205
|
+
c1: bytes_from_value(c1_value)?,
|
|
206
|
+
c2: bytes_from_value(c2_value)?,
|
|
207
|
+
};
|
|
208
|
+
let share = DecryptionShare {
|
|
209
|
+
party_index,
|
|
210
|
+
bytes: share_b,
|
|
211
|
+
};
|
|
212
|
+
let partial = partial_decrypt(&share, &ciphertext)
|
|
213
|
+
.map_err(|e| Error::new(exception::runtime_error(), e.to_string()))?;
|
|
214
|
+
let ruby = Ruby::get().map_err(|e| Error::new(exception::runtime_error(), e.to_string()))?;
|
|
215
|
+
let result = ruby.hash_new();
|
|
216
|
+
result.aset("party_index", partial.party_index)?;
|
|
217
|
+
result.aset("bytes", bytes_to_rstring(&ruby, &partial.bytes))?;
|
|
218
|
+
Ok(result)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
fn elgamal_aggregate(partials_value: Value, threshold: u32, ciphertext_value: Value) -> Result<magnus::RString, Error> {
|
|
222
|
+
let arr = magnus::RArray::try_convert(partials_value)?;
|
|
223
|
+
let mut partials: Vec<PartialDecryption> = Vec::with_capacity(arr.len());
|
|
224
|
+
for v in arr.each() {
|
|
225
|
+
let h: RHash = RHash::try_convert(v?)?;
|
|
226
|
+
let party_index: u32 = h.fetch::<_, u32>("party_index")?;
|
|
227
|
+
let bytes_value: Value = h.fetch::<_, Value>("bytes")?;
|
|
228
|
+
partials.push(PartialDecryption {
|
|
229
|
+
party_index,
|
|
230
|
+
bytes: bytes_from_value(bytes_value)?,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
let ct_hash: RHash = RHash::try_convert(ciphertext_value)?;
|
|
234
|
+
let c1_value: Value = ct_hash.fetch::<_, Value>("c1")?;
|
|
235
|
+
let c2_value: Value = ct_hash.fetch::<_, Value>("c2")?;
|
|
236
|
+
let ciphertext = ElGamalCiphertext {
|
|
237
|
+
c1: bytes_from_value(c1_value)?,
|
|
238
|
+
c2: bytes_from_value(c2_value)?,
|
|
239
|
+
};
|
|
240
|
+
let shared = aggregate_partials(&partials, threshold, &ciphertext)
|
|
241
|
+
.map_err(|e| Error::new(exception::runtime_error(), e.to_string()))?;
|
|
242
|
+
let ruby = Ruby::get().map_err(|e| Error::new(exception::runtime_error(), e.to_string()))?;
|
|
243
|
+
Ok(bytes_to_rstring(&ruby, &shared))
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
pub fn init(ruby: &Ruby, parent: magnus::RModule) -> Result<(), Error> {
|
|
247
|
+
let tc = parent.define_module("TC")?;
|
|
248
|
+
let frost = tc.define_module("FrostP256")?;
|
|
249
|
+
frost.define_module_function("split_secret", function!(split_secret_into_shares, 3))?;
|
|
250
|
+
frost.define_module_function("recover_secret", function!(recover, 1))?;
|
|
251
|
+
frost.define_module_function("generate_keypair", function!(keypair, 0))?;
|
|
252
|
+
frost.define_module_function("sign", function!(sign, 2))?;
|
|
253
|
+
|
|
254
|
+
let share_class = frost.define_class("Share", ruby.class_object())?;
|
|
255
|
+
share_class.define_method("x", method!(ShareWrap::x, 0))?;
|
|
256
|
+
share_class.define_method("y_bytes", method!(ShareWrap::y_bytes, 0))?;
|
|
257
|
+
|
|
258
|
+
let elgamal = tc.define_module("ElGamalP256")?;
|
|
259
|
+
elgamal.define_module_function("encapsulate", function!(elgamal_encapsulate, 1))?;
|
|
260
|
+
elgamal.define_module_function("partial_decrypt", function!(elgamal_partial_decrypt, 3))?;
|
|
261
|
+
elgamal.define_module_function("aggregate_partials", function!(elgamal_aggregate, 3))?;
|
|
262
|
+
|
|
263
|
+
// CMP20 / GG18 in-process threshold-ECDSA drivers.
|
|
264
|
+
let cmp20 = tc.define_module("Cmp20")?;
|
|
265
|
+
cmp20.define_module_function("keygen", function!(cmp20_keygen, 2))?;
|
|
266
|
+
cmp20.define_module_function("sign", function!(cmp20_sign, 3))?;
|
|
267
|
+
|
|
268
|
+
let gg18 = tc.define_module("Gg18")?;
|
|
269
|
+
gg18.define_module_function("keygen", function!(gg18_keygen, 2))?;
|
|
270
|
+
gg18.define_module_function("sign", function!(gg18_sign, 3))?;
|
|
271
|
+
|
|
272
|
+
// Touch Scalar to silence unused-import warnings if any.
|
|
273
|
+
let _: Option<Scalar> = None;
|
|
274
|
+
|
|
275
|
+
Ok(())
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ===== CMP20 in-process keygen / sign =====
|
|
279
|
+
|
|
280
|
+
fn cmp20_keygen(ruby: &Ruby, threshold: u32, party_count: u32) -> Result<RHash, Error> {
|
|
281
|
+
let kg = match cmp20_inprocess::keygen(threshold, party_count as usize) {
|
|
282
|
+
Ok(k) => k,
|
|
283
|
+
Err(e) => {
|
|
284
|
+
let msg = e.to_string();
|
|
285
|
+
crate::audit::fire_event(
|
|
286
|
+
"tc_cmp20_keygen",
|
|
287
|
+
"failure",
|
|
288
|
+
Some("CMP20-ECDSA-P256"),
|
|
289
|
+
None,
|
|
290
|
+
Some(&msg),
|
|
291
|
+
);
|
|
292
|
+
return Err(threshold_error(msg, "Cmp20.keygen", party_count as usize, threshold as usize));
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
crate::audit::fire_event(
|
|
296
|
+
"tc_cmp20_keygen",
|
|
297
|
+
"success",
|
|
298
|
+
Some("CMP20-ECDSA-P256"),
|
|
299
|
+
None,
|
|
300
|
+
None,
|
|
301
|
+
);
|
|
302
|
+
let result = ruby.hash_new();
|
|
303
|
+
let shares_arr = ruby.ary_new_capa(kg.shares.len());
|
|
304
|
+
for s in kg.shares {
|
|
305
|
+
shares_arr.push(bytes_to_rstring(ruby, &s))?;
|
|
306
|
+
}
|
|
307
|
+
result.aset("shares", shares_arr)?;
|
|
308
|
+
result.aset("public_key", bytes_to_rstring(ruby, &kg.public_key))?;
|
|
309
|
+
Ok(result)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
fn cmp20_sign(
|
|
313
|
+
ruby: &Ruby,
|
|
314
|
+
shares_value: Value,
|
|
315
|
+
threshold: u32,
|
|
316
|
+
message_value: Value,
|
|
317
|
+
) -> Result<magnus::RString, Error> {
|
|
318
|
+
let arr = magnus::RArray::try_convert(shares_value)?;
|
|
319
|
+
let mut share_bytes: Vec<Vec<u8>> = Vec::with_capacity(arr.len());
|
|
320
|
+
for v in arr.each() {
|
|
321
|
+
let s = bytes_from_value(v?)?;
|
|
322
|
+
share_bytes.push(s);
|
|
323
|
+
}
|
|
324
|
+
let supplied = share_bytes.len();
|
|
325
|
+
let msg = bytes_from_value(message_value)?;
|
|
326
|
+
let sig = match cmp20_inprocess::sign(&share_bytes, threshold, &msg) {
|
|
327
|
+
Ok(s) => s,
|
|
328
|
+
Err(e) => {
|
|
329
|
+
let human = e.to_string();
|
|
330
|
+
crate::audit::fire_event(
|
|
331
|
+
"tc_cmp20_sign",
|
|
332
|
+
"failure",
|
|
333
|
+
Some("CMP20-ECDSA-P256"),
|
|
334
|
+
Some(&msg),
|
|
335
|
+
Some(&human),
|
|
336
|
+
);
|
|
337
|
+
return Err(threshold_error(human, "Cmp20.sign", supplied, threshold as usize));
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
crate::audit::fire_event(
|
|
341
|
+
"tc_cmp20_sign",
|
|
342
|
+
"success",
|
|
343
|
+
Some("CMP20-ECDSA-P256"),
|
|
344
|
+
Some(&msg),
|
|
345
|
+
None,
|
|
346
|
+
);
|
|
347
|
+
Ok(bytes_to_rstring(ruby, &sig))
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// ===== GG18 in-process keygen / sign =====
|
|
351
|
+
|
|
352
|
+
fn gg18_keygen(ruby: &Ruby, threshold: u32, party_count: u32) -> Result<RHash, Error> {
|
|
353
|
+
let kg = match gg18_inprocess::keygen(threshold, party_count as usize) {
|
|
354
|
+
Ok(k) => k,
|
|
355
|
+
Err(e) => {
|
|
356
|
+
let msg = e.to_string();
|
|
357
|
+
crate::audit::fire_event(
|
|
358
|
+
"tc_gg18_keygen",
|
|
359
|
+
"failure",
|
|
360
|
+
Some("GG18-ECDSA-P256"),
|
|
361
|
+
None,
|
|
362
|
+
Some(&msg),
|
|
363
|
+
);
|
|
364
|
+
return Err(threshold_error(msg, "Gg18.keygen", party_count as usize, threshold as usize));
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
crate::audit::fire_event(
|
|
368
|
+
"tc_gg18_keygen",
|
|
369
|
+
"success",
|
|
370
|
+
Some("GG18-ECDSA-P256"),
|
|
371
|
+
None,
|
|
372
|
+
None,
|
|
373
|
+
);
|
|
374
|
+
let result = ruby.hash_new();
|
|
375
|
+
let shares_arr = ruby.ary_new_capa(kg.shares.len());
|
|
376
|
+
for s in kg.shares {
|
|
377
|
+
shares_arr.push(bytes_to_rstring(ruby, &s))?;
|
|
378
|
+
}
|
|
379
|
+
result.aset("shares", shares_arr)?;
|
|
380
|
+
result.aset("public_key", bytes_to_rstring(ruby, &kg.public_key))?;
|
|
381
|
+
Ok(result)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
fn gg18_sign(
|
|
385
|
+
ruby: &Ruby,
|
|
386
|
+
shares_value: Value,
|
|
387
|
+
threshold: u32,
|
|
388
|
+
message_value: Value,
|
|
389
|
+
) -> Result<magnus::RString, Error> {
|
|
390
|
+
let arr = magnus::RArray::try_convert(shares_value)?;
|
|
391
|
+
let mut share_bytes: Vec<Vec<u8>> = Vec::with_capacity(arr.len());
|
|
392
|
+
for v in arr.each() {
|
|
393
|
+
let s = bytes_from_value(v?)?;
|
|
394
|
+
share_bytes.push(s);
|
|
395
|
+
}
|
|
396
|
+
let supplied = share_bytes.len();
|
|
397
|
+
let msg = bytes_from_value(message_value)?;
|
|
398
|
+
let sig = match gg18_inprocess::sign(&share_bytes, threshold, &msg) {
|
|
399
|
+
Ok(s) => s,
|
|
400
|
+
Err(e) => {
|
|
401
|
+
let human = e.to_string();
|
|
402
|
+
crate::audit::fire_event(
|
|
403
|
+
"tc_gg18_sign",
|
|
404
|
+
"failure",
|
|
405
|
+
Some("GG18-ECDSA-P256"),
|
|
406
|
+
Some(&msg),
|
|
407
|
+
Some(&human),
|
|
408
|
+
);
|
|
409
|
+
return Err(threshold_error(human, "Gg18.sign", supplied, threshold as usize));
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
crate::audit::fire_event(
|
|
413
|
+
"tc_gg18_sign",
|
|
414
|
+
"success",
|
|
415
|
+
Some("GG18-ECDSA-P256"),
|
|
416
|
+
Some(&msg),
|
|
417
|
+
None,
|
|
418
|
+
);
|
|
419
|
+
Ok(bytes_to_rstring(ruby, &sig))
|
|
420
|
+
}
|