confium 0.3.3 → 0.3.4
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 +23 -0
- data/ext/confium_native/src/attributes.rs +4 -4
- data/ext/confium_native/src/audit.rs +8 -8
- data/ext/confium_native/src/composite.rs +10 -14
- data/ext/confium_native/src/deployment.rs +7 -6
- data/ext/confium_native/src/ers.rs +7 -11
- data/ext/confium_native/src/openpgp.rs +1 -1
- data/ext/confium_native/src/path.rs +8 -8
- data/ext/confium_native/src/pki.rs +26 -30
- data/ext/confium_native/src/tc.rs +27 -59
- data/ext/confium_native/src/transparency.rs +16 -57
- data/ext/confium_native/src/util.rs +32 -11
- data/lib/confium/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fdc8059371955b1e4ee89982856fb39a1fb758c9d1b3264bb2f3bd21fe77c028
|
|
4
|
+
data.tar.gz: a392f2b3d8b87e4d296f5f68dec94e61ae3e2263eea032f12240b2f625b7ba56
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ccdae726768ecf965b3f21f000b42bbc7c67a01b31d7715a2f23f0e243873453da7539ae57fbfb0ffda185d3ea572b74d0f3c033518e1e154fdc0b739db0b1b1
|
|
7
|
+
data.tar.gz: db63f19d08cf86076df52ec0f583493019e03a1512268aa1d83b56edce182d42c18120369e46ca054e66dfdf79619b04fdfe91175607845260cc668894fa680d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.4] — 2026-08-23
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Typed-error sweep completed.** Crypto operation failures
|
|
8
|
+
(FrostP256 sign, ElGamal encapsulate/decrypt/aggregate, composite
|
|
9
|
+
sign) raise `Confium::CryptoError` with the primitive in `details`;
|
|
10
|
+
`PathValidator` DER extraction failures raise `Confium::ParseError`;
|
|
11
|
+
`Manifest#tier_name_at` and `SignedData#certificate_at` out-of-range
|
|
12
|
+
raise the typed `Confium::IndexError` (unifying with the
|
|
13
|
+
transparency proofs — they previously raised Ruby's core
|
|
14
|
+
`IndexError`).
|
|
15
|
+
- **Zero compiler warnings.** All 80 direct
|
|
16
|
+
`magnus::exception::*` constructor sites are centralized into
|
|
17
|
+
`util::runtime`/`util::arg_error` (the deprecated API survives in
|
|
18
|
+
exactly one module whose job is wrapping it); the remaining magnus
|
|
19
|
+
deprecations (`RString::buf_new`, `RArray::each`) and dead imports
|
|
20
|
+
are fixed.
|
|
21
|
+
- **DoS guard now covers every byte input**: the duplicate local
|
|
22
|
+
`bytes_from_value` copies in tc/transparency — one of which
|
|
23
|
+
skipped the 1 MiB size cap — are consolidated onto the util
|
|
24
|
+
version that enforces it.
|
|
25
|
+
|
|
3
26
|
## [0.3.3] — 2026-08-23
|
|
4
27
|
|
|
5
28
|
### Added
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
use confium_attributes::{evaluate, parse as dsl_parse, Predicate, SignerAttributes};
|
|
14
14
|
use magnus::{
|
|
15
|
-
|
|
15
|
+
function, method, typed_data::Obj, DataTypeFunctions, Error, Module,
|
|
16
16
|
Object, Ruby, TryConvert, TypedData, Value,
|
|
17
17
|
};
|
|
18
18
|
|
|
@@ -26,8 +26,8 @@ impl PredicateWrap {
|
|
|
26
26
|
fn satisfied_by(&self, signers_value: Value) -> Result<bool, Error> {
|
|
27
27
|
let arr = magnus::RArray::try_convert(signers_value)?;
|
|
28
28
|
let mut owned: Vec<SignerAttributes> = Vec::with_capacity(arr.len());
|
|
29
|
-
for v in arr.
|
|
30
|
-
let signer_wrap = Obj::<SignerWrap>::try_convert(v
|
|
29
|
+
for v in arr.into_iter() {
|
|
30
|
+
let signer_wrap = Obj::<SignerWrap>::try_convert(v)?;
|
|
31
31
|
owned.push(signer_wrap.inner.borrow().clone());
|
|
32
32
|
}
|
|
33
33
|
let refs: Vec<&SignerAttributes> = owned.iter().collect();
|
|
@@ -64,7 +64,7 @@ impl SignerWrap {
|
|
|
64
64
|
fn parse(expr: String) -> Result<Obj<PredicateWrap>, Error> {
|
|
65
65
|
let predicate = dsl_parse(&expr)
|
|
66
66
|
.map_err(|e| crate::util::parse_error(e.to_string(), "Attributes.parse", Some("dsl"), None))?;
|
|
67
|
-
let ruby = Ruby::get().map_err(|e|
|
|
67
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
68
68
|
Ok(ruby.obj_wrap(PredicateWrap { inner: predicate }))
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
//! sink raises, the calling operation still succeeds. Audit
|
|
12
12
|
//! failures must never break a signing ceremony.
|
|
13
13
|
|
|
14
|
-
use magnus::{
|
|
14
|
+
use magnus::{function, prelude::*, Error, Module, Ruby, Value};
|
|
15
15
|
use sha2::{Digest, Sha256};
|
|
16
16
|
|
|
17
17
|
const SINK_IVAR: &str = "@sink";
|
|
@@ -94,12 +94,12 @@ fn record(
|
|
|
94
94
|
algorithm: Option<String>,
|
|
95
95
|
error: Option<String>,
|
|
96
96
|
) -> Result<(), Error> {
|
|
97
|
-
let ruby = Ruby::get().map_err(|e|
|
|
97
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
98
98
|
let audit_mod: magnus::RModule = ruby
|
|
99
99
|
.class_object()
|
|
100
100
|
.const_get("Confium")
|
|
101
101
|
.and_then(|m: magnus::RModule| m.const_get("Audit"))
|
|
102
|
-
.map_err(|e|
|
|
102
|
+
.map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
103
103
|
|
|
104
104
|
let sink: Value = audit_mod.ivar_get(SINK_IVAR)?;
|
|
105
105
|
if sink.is_nil() {
|
|
@@ -123,31 +123,31 @@ fn record(
|
|
|
123
123
|
|
|
124
124
|
let _: Value = sink
|
|
125
125
|
.funcall("call", (h,))
|
|
126
|
-
.map_err(|e|
|
|
126
|
+
.map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
127
127
|
Ok(())
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
/// Set the global audit sink. `callback` is a Proc that receives a Hash
|
|
131
131
|
/// or nil to disable auditing.
|
|
132
132
|
fn set_sink(callback: Value) -> Result<(), Error> {
|
|
133
|
-
let ruby = Ruby::get().map_err(|e|
|
|
133
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
134
134
|
let audit_mod: magnus::RModule = ruby
|
|
135
135
|
.class_object()
|
|
136
136
|
.const_get("Confium")
|
|
137
137
|
.and_then(|m: magnus::RModule| m.const_get("Audit"))
|
|
138
|
-
.map_err(|e|
|
|
138
|
+
.map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
139
139
|
audit_mod.ivar_set(SINK_IVAR, callback)?;
|
|
140
140
|
Ok(())
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
/// Get the current audit sink (or nil if not set).
|
|
144
144
|
fn get_sink() -> Result<Value, Error> {
|
|
145
|
-
let ruby = Ruby::get().map_err(|e|
|
|
145
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
146
146
|
let audit_mod: magnus::RModule = ruby
|
|
147
147
|
.class_object()
|
|
148
148
|
.const_get("Confium")
|
|
149
149
|
.and_then(|m: magnus::RModule| m.const_get("Audit"))
|
|
150
|
-
.map_err(|e|
|
|
150
|
+
.map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
151
151
|
audit_mod.ivar_get(SINK_IVAR)
|
|
152
152
|
}
|
|
153
153
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
use confium_composite::{CompositeSignature, ComponentSignature, VerificationResult};
|
|
15
15
|
use ed25519_dalek::SigningKey;
|
|
16
16
|
use magnus::{
|
|
17
|
-
|
|
17
|
+
function, method, prelude::*, scan_args,
|
|
18
18
|
typed_data::Obj, DataTypeFunctions, Error, IntoValue,
|
|
19
19
|
Module, Object, RHash, Ruby, TryConvert, TypedData, Value,
|
|
20
20
|
};
|
|
@@ -79,7 +79,7 @@ impl CompositeSig {
|
|
|
79
79
|
let details = new_details(&ruby);
|
|
80
80
|
confium_error(e.to_string(), "VerificationError", details)
|
|
81
81
|
})?;
|
|
82
|
-
let ruby = Ruby::get().map_err(|e|
|
|
82
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
83
83
|
Ok(ruby.obj_wrap(VerificationResultWrap { inner: result }))
|
|
84
84
|
}
|
|
85
85
|
}
|
|
@@ -133,7 +133,7 @@ impl VerificationResultWrap {
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
fn per_component(&self) -> Result<Value, Error> {
|
|
136
|
-
let ruby = Ruby::get().map_err(|e|
|
|
136
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
137
137
|
let result = ruby.hash_new();
|
|
138
138
|
for c in &self.inner.per_component {
|
|
139
139
|
let entry = ruby.hash_new();
|
|
@@ -156,9 +156,7 @@ fn sign_ed25519(ruby: &Ruby, private_key: Value, message: Value) -> Result<RHash
|
|
|
156
156
|
let pk_bytes = bytes_from_value(private_key)?;
|
|
157
157
|
let msg = bytes_from_value(message)?;
|
|
158
158
|
if pk_bytes.len() != 32 {
|
|
159
|
-
return Err(
|
|
160
|
-
exception::arg_error(),
|
|
161
|
-
format!("Ed25519 private key must be 32 bytes, got {}", pk_bytes.len()),
|
|
159
|
+
return Err(crate::util::arg_error(format!("Ed25519 private key must be 32 bytes, got {}", pk_bytes.len()),
|
|
162
160
|
));
|
|
163
161
|
}
|
|
164
162
|
let mut pk_arr = [0u8; 32];
|
|
@@ -174,7 +172,7 @@ fn sign_ed25519(ruby: &Ruby, private_key: Value, message: Value) -> Result<RHash
|
|
|
174
172
|
Some(&msg),
|
|
175
173
|
Some(&e.to_string()),
|
|
176
174
|
);
|
|
177
|
-
return Err(
|
|
175
|
+
return Err(crate::util::crypto_error(e.to_string(), "Composite.sign_ed25519", "ed25519"));
|
|
178
176
|
}
|
|
179
177
|
};
|
|
180
178
|
|
|
@@ -204,16 +202,14 @@ fn sign_p256(ruby: &Ruby, private_key: Value, message: Value) -> Result<RHash, E
|
|
|
204
202
|
let pk_bytes = bytes_from_value(private_key)?;
|
|
205
203
|
let msg = bytes_from_value(message)?;
|
|
206
204
|
if pk_bytes.len() != 32 {
|
|
207
|
-
return Err(
|
|
208
|
-
exception::arg_error(),
|
|
209
|
-
format!("P-256 private key must be 32 bytes, got {}", pk_bytes.len()),
|
|
205
|
+
return Err(crate::util::arg_error(format!("P-256 private key must be 32 bytes, got {}", pk_bytes.len()),
|
|
210
206
|
));
|
|
211
207
|
}
|
|
212
208
|
let mut arr = [0u8; 32];
|
|
213
209
|
arr.copy_from_slice(&pk_bytes);
|
|
214
210
|
use p256::ecdsa::{Signature, SigningKey, signature::Signer};
|
|
215
211
|
let signing = SigningKey::from_bytes(&arr.into())
|
|
216
|
-
.map_err(|e|
|
|
212
|
+
.map_err(|e| crate::util::arg_error(format!("invalid P-256 private key: {e}")))?;
|
|
217
213
|
let sig: Signature = match signing.try_sign(msg.as_slice()) {
|
|
218
214
|
Ok(s) => s,
|
|
219
215
|
Err(e) => {
|
|
@@ -224,7 +220,7 @@ fn sign_p256(ruby: &Ruby, private_key: Value, message: Value) -> Result<RHash, E
|
|
|
224
220
|
Some(&msg),
|
|
225
221
|
Some(&format!("sign error: {e}")),
|
|
226
222
|
);
|
|
227
|
-
return Err(
|
|
223
|
+
return Err(crate::util::crypto_error(format!("sign error: {e}"), "Composite.sign_p256", "ecdsa-p256"));
|
|
228
224
|
}
|
|
229
225
|
};
|
|
230
226
|
let verifying = signing.verifying_key();
|
|
@@ -259,8 +255,8 @@ fn generate_ed25519_keypair(ruby: &Ruby) -> Result<RHash, Error> {
|
|
|
259
255
|
fn parse_components(value: Value) -> Result<Vec<ComponentSignature>, Error> {
|
|
260
256
|
let arr = magnus::RArray::try_convert(value)?;
|
|
261
257
|
let mut out = Vec::with_capacity(arr.len());
|
|
262
|
-
for v in arr.
|
|
263
|
-
let h: RHash = RHash::try_convert(v
|
|
258
|
+
for v in arr.into_iter() {
|
|
259
|
+
let h: RHash = RHash::try_convert(v)?;
|
|
264
260
|
let algorithm: String = h.fetch::<_, String>("algorithm")?;
|
|
265
261
|
let public_key: Value = h.fetch::<_, Value>("public_key")?;
|
|
266
262
|
let signature: Value = h.fetch::<_, Value>("signature")?;
|
|
@@ -12,7 +12,7 @@ use confium_deployment::{
|
|
|
12
12
|
validate::validate_manifest,
|
|
13
13
|
};
|
|
14
14
|
use crate::util::{enforce_size, parse_error};
|
|
15
|
-
use magnus::{
|
|
15
|
+
use magnus::{function, method, typed_data::Obj, DataTypeFunctions, Error, Module, Object, Ruby, TypedData};
|
|
16
16
|
|
|
17
17
|
#[derive(TypedData, DataTypeFunctions)]
|
|
18
18
|
#[magnus(class = "Confium::Identity::Actor", size)]
|
|
@@ -25,7 +25,7 @@ impl Actor {
|
|
|
25
25
|
enforce_size(json.len())?;
|
|
26
26
|
let actor: ActorIdentity = serde_json::from_str(&json)
|
|
27
27
|
.map_err(|e| parse_error(e.to_string(), "Actor.from_json", Some("json"), None))?;
|
|
28
|
-
let ruby = Ruby::get().map_err(|e|
|
|
28
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
29
29
|
Ok(ruby.obj_wrap(Self {
|
|
30
30
|
inner: std::cell::RefCell::new(actor),
|
|
31
31
|
}))
|
|
@@ -33,7 +33,7 @@ impl Actor {
|
|
|
33
33
|
|
|
34
34
|
fn to_json(&self) -> Result<String, Error> {
|
|
35
35
|
serde_json::to_string(&*self.inner.borrow())
|
|
36
|
-
.map_err(|e|
|
|
36
|
+
.map_err(|e| crate::util::runtime(e.to_string()))
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
fn actor_id(&self) -> String {
|
|
@@ -94,7 +94,7 @@ impl Manifest {
|
|
|
94
94
|
enforce_size(toml_str.len())?;
|
|
95
95
|
let manifest = parse_manifest(&toml_str)
|
|
96
96
|
.map_err(|e| parse_error(e.to_string(), "Manifest.from_toml", Some("toml"), None))?;
|
|
97
|
-
let ruby = Ruby::get().map_err(|e|
|
|
97
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
98
98
|
Ok(ruby.obj_wrap(Self {
|
|
99
99
|
inner: std::cell::RefCell::new(manifest),
|
|
100
100
|
}))
|
|
@@ -123,9 +123,10 @@ impl Manifest {
|
|
|
123
123
|
.get(index)
|
|
124
124
|
.map(|t| t.name.clone())
|
|
125
125
|
.ok_or_else(|| {
|
|
126
|
-
|
|
127
|
-
exception::index_error(),
|
|
126
|
+
crate::util::index_error(
|
|
128
127
|
format!("tier index {index} out of range"),
|
|
128
|
+
"Manifest.tier_name_at",
|
|
129
|
+
Some(index as u64),
|
|
129
130
|
)
|
|
130
131
|
})
|
|
131
132
|
}
|
|
@@ -5,7 +5,7 @@ use confium_transparency::ers::{
|
|
|
5
5
|
EvidenceRecord, HashAlgorithm,
|
|
6
6
|
};
|
|
7
7
|
use magnus::{
|
|
8
|
-
|
|
8
|
+
function, method, typed_data::Obj,
|
|
9
9
|
DataTypeFunctions, Error, Module, Object, Ruby, TryConvert, TypedData, Value,
|
|
10
10
|
};
|
|
11
11
|
|
|
@@ -20,13 +20,11 @@ pub struct ErsRecord {
|
|
|
20
20
|
impl ErsRecord {
|
|
21
21
|
fn build_initial(args: &[Value]) -> Result<Obj<Self>, Error> {
|
|
22
22
|
let first = args.first().ok_or_else(|| {
|
|
23
|
-
|
|
23
|
+
crate::util::arg_error("data_hash required")
|
|
24
24
|
})?;
|
|
25
25
|
let data_hash = bytes_from_value(*first)?;
|
|
26
26
|
if data_hash.len() != 32 {
|
|
27
|
-
return Err(
|
|
28
|
-
exception::arg_error(),
|
|
29
|
-
format!("data_hash must be 32 bytes, got {}", data_hash.len()),
|
|
27
|
+
return Err(crate::util::arg_error(format!("data_hash must be 32 bytes, got {}", data_hash.len()),
|
|
30
28
|
));
|
|
31
29
|
}
|
|
32
30
|
let mut hash = [0u8; 32];
|
|
@@ -42,7 +40,7 @@ impl ErsRecord {
|
|
|
42
40
|
let record =
|
|
43
41
|
build_initial_evidence_record(hash, HashAlgorithm::Sha256, tsa_id, token_bytes);
|
|
44
42
|
let ruby = Ruby::get()
|
|
45
|
-
.map_err(|e|
|
|
43
|
+
.map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
46
44
|
Ok(ruby.obj_wrap(Self {
|
|
47
45
|
inner: std::cell::RefCell::new(record),
|
|
48
46
|
}))
|
|
@@ -50,13 +48,11 @@ impl ErsRecord {
|
|
|
50
48
|
|
|
51
49
|
fn renew(&self, args: &[Value]) -> Result<Obj<Self>, Error> {
|
|
52
50
|
let first = args.first().ok_or_else(|| {
|
|
53
|
-
|
|
51
|
+
crate::util::arg_error("new_hash required")
|
|
54
52
|
})?;
|
|
55
53
|
let new_hash_bytes = bytes_from_value(*first)?;
|
|
56
54
|
if new_hash_bytes.len() != 32 {
|
|
57
|
-
return Err(
|
|
58
|
-
exception::arg_error(),
|
|
59
|
-
format!("new_hash must be 32 bytes, got {}", new_hash_bytes.len()),
|
|
55
|
+
return Err(crate::util::arg_error(format!("new_hash must be 32 bytes, got {}", new_hash_bytes.len()),
|
|
60
56
|
));
|
|
61
57
|
}
|
|
62
58
|
let mut hash = [0u8; 32];
|
|
@@ -72,7 +68,7 @@ impl ErsRecord {
|
|
|
72
68
|
let mut cloned = self.inner.borrow().clone();
|
|
73
69
|
renew_evidence_record(&mut cloned, HashAlgorithm::Sha256, hash, tsa_id, token_bytes);
|
|
74
70
|
let ruby = Ruby::get()
|
|
75
|
-
.map_err(|e|
|
|
71
|
+
.map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
76
72
|
Ok(ruby.obj_wrap(Self {
|
|
77
73
|
inner: std::cell::RefCell::new(cloned),
|
|
78
74
|
}))
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
//!
|
|
3
3
|
//! Confium ships RNP functionality baked into the native extension.
|
|
4
4
|
|
|
5
|
-
use magnus::{
|
|
5
|
+
use magnus::{function, prelude::*, Error, Module, RModule, Ruby, TryConvert, Value};
|
|
6
6
|
use rnp::ops::ArmorType;
|
|
7
7
|
use rnp::{armor_bytes, dearmor_bytes};
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@ use confium_pki::{
|
|
|
10
10
|
path::{validate_path, CertPath},
|
|
11
11
|
result::VerificationResult as PathVerificationResult,
|
|
12
12
|
};
|
|
13
|
-
use magnus::{
|
|
13
|
+
use magnus::{function, method, prelude::*, DataTypeFunctions, Error, Module, Ruby, TypedData, Value};
|
|
14
14
|
|
|
15
15
|
/// Wraps a confium_pki::path::VerificationResult.
|
|
16
16
|
#[derive(TypedData, DataTypeFunctions)]
|
|
@@ -63,17 +63,17 @@ fn validate(args: &[Value]) -> Result<magnus::typed_data::Obj<PathResult>, Error
|
|
|
63
63
|
Vec::new()
|
|
64
64
|
} else {
|
|
65
65
|
let arr = magnus::RArray::try_convert(intermediates_value)
|
|
66
|
-
.map_err(|e|
|
|
66
|
+
.map_err(|e| crate::util::arg_error(e.to_string()))?;
|
|
67
67
|
let mut out = Vec::with_capacity(arr.len());
|
|
68
|
-
for v in arr.
|
|
69
|
-
out.push(extract_cert(v
|
|
68
|
+
for v in arr.into_iter() {
|
|
69
|
+
out.push(extract_cert(v, "intermediate")?);
|
|
70
70
|
}
|
|
71
71
|
out
|
|
72
72
|
};
|
|
73
73
|
|
|
74
74
|
let now = match now_iso8601 {
|
|
75
75
|
Some(s) => chrono::DateTime::parse_from_rfc3339(&s)
|
|
76
|
-
.map_err(|e|
|
|
76
|
+
.map_err(|e| crate::util::arg_error(format!("invalid time: {e}")))?
|
|
77
77
|
.with_timezone(&Utc),
|
|
78
78
|
None => Utc::now(),
|
|
79
79
|
};
|
|
@@ -86,7 +86,7 @@ fn validate(args: &[Value]) -> Result<magnus::typed_data::Obj<PathResult>, Error
|
|
|
86
86
|
let result = validate_path(&path, now);
|
|
87
87
|
|
|
88
88
|
let ruby = Ruby::get()
|
|
89
|
-
.map_err(|e|
|
|
89
|
+
.map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
90
90
|
Ok(ruby.obj_wrap(PathResult { inner: result }))
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -98,10 +98,10 @@ fn extract_cert(value: Value, label: &str) -> Result<RustCert, Error> {
|
|
|
98
98
|
// We re-parse those bytes into a fresh RustCert.
|
|
99
99
|
let der_value: Value = value
|
|
100
100
|
.funcall("to_der", ())
|
|
101
|
-
.map_err(|e|
|
|
101
|
+
.map_err(|e| crate::util::parse_error(format!("{label}: cannot get DER: {e}"), "PathValidator.validate", Some("der"), None))?;
|
|
102
102
|
let der = crate::util::bytes_from_value(der_value)?;
|
|
103
103
|
RustCert::from_der(&der)
|
|
104
|
-
.map_err(|e|
|
|
104
|
+
.map_err(|e| crate::util::parse_error(format!("{label}: invalid DER: {e}"), "PathValidator.validate", Some("der"), None))
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
pub fn init(ruby: &Ruby, parent: magnus::RModule) -> Result<(), Error> {
|
|
@@ -20,7 +20,7 @@ use confium_pki::{
|
|
|
20
20
|
xmldsig::{canonicalize, canonicalize_exclusive},
|
|
21
21
|
};
|
|
22
22
|
use magnus::{
|
|
23
|
-
|
|
23
|
+
function, method, prelude::*, typed_data::Obj, DataTypeFunctions, Error, Module,
|
|
24
24
|
Object, RHash, RString, Ruby, TryConvert, TypedData, Value,
|
|
25
25
|
};
|
|
26
26
|
|
|
@@ -35,7 +35,7 @@ impl Certificate {
|
|
|
35
35
|
let der = bytes_from_value(bytes)?;
|
|
36
36
|
let cert = RustCert::from_der(&der)
|
|
37
37
|
.map_err(|e| parse_error(e.to_string(), "Certificate.from_der", Some("der"), None))?;
|
|
38
|
-
let ruby = Ruby::get().map_err(|e|
|
|
38
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
39
39
|
Ok(ruby.obj_wrap(Self { inner: cert }))
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -43,12 +43,12 @@ impl Certificate {
|
|
|
43
43
|
enforce_size(pem.len())?;
|
|
44
44
|
let cert = RustCert::from_pem(&pem)
|
|
45
45
|
.map_err(|e| parse_error(e.to_string(), "Certificate.from_pem", Some("pem"), None))?;
|
|
46
|
-
let ruby = Ruby::get().map_err(|e|
|
|
46
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
47
47
|
Ok(ruby.obj_wrap(Self { inner: cert }))
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
fn to_der(&self) -> Result<RString, Error> {
|
|
51
|
-
let ruby = Ruby::get().map_err(|e|
|
|
51
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
52
52
|
Ok(bytes_to_rstring(&ruby, &self.inner.to_der()))
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -81,13 +81,13 @@ impl Certificate {
|
|
|
81
81
|
|
|
82
82
|
fn valid_at(&self, iso8601: String) -> Result<bool, Error> {
|
|
83
83
|
let now = DateTime::parse_from_rfc3339(&iso8601)
|
|
84
|
-
.map_err(|e|
|
|
84
|
+
.map_err(|e| crate::util::arg_error(format!("invalid ISO8601 time: {e}")))?
|
|
85
85
|
.with_timezone(&Utc);
|
|
86
86
|
Ok(self.inner.is_within_validity(now))
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
fn public_key_bytes(&self) -> Result<RString, Error> {
|
|
90
|
-
let ruby = Ruby::get().map_err(|e|
|
|
90
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
91
91
|
Ok(bytes_to_rstring(&ruby, self.inner.public_key_bytes()))
|
|
92
92
|
}
|
|
93
93
|
}
|
|
@@ -103,7 +103,7 @@ impl Csr {
|
|
|
103
103
|
let der = bytes_from_value(bytes)?;
|
|
104
104
|
let csr = RustCsr::from_der(&der)
|
|
105
105
|
.map_err(|e| parse_error(e.to_string(), "Csr.from_der", Some("der"), None))?;
|
|
106
|
-
let ruby = Ruby::get().map_err(|e|
|
|
106
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
107
107
|
Ok(ruby.obj_wrap(Self { inner: csr }))
|
|
108
108
|
}
|
|
109
109
|
|
|
@@ -111,12 +111,12 @@ impl Csr {
|
|
|
111
111
|
enforce_size(pem.len())?;
|
|
112
112
|
let csr = RustCsr::from_pem(&pem)
|
|
113
113
|
.map_err(|e| parse_error(e.to_string(), "Csr.from_pem", Some("pem"), None))?;
|
|
114
|
-
let ruby = Ruby::get().map_err(|e|
|
|
114
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
115
115
|
Ok(ruby.obj_wrap(Self { inner: csr }))
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
fn to_der(&self) -> Result<RString, Error> {
|
|
119
|
-
let ruby = Ruby::get().map_err(|e|
|
|
119
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
120
120
|
Ok(bytes_to_rstring(&ruby, &self.inner.to_der()))
|
|
121
121
|
}
|
|
122
122
|
|
|
@@ -136,7 +136,7 @@ impl SignedData {
|
|
|
136
136
|
enforce_size(json.len())?;
|
|
137
137
|
let sd: RustSignedData = serde_json::from_str(&json)
|
|
138
138
|
.map_err(|e| parse_error(e.to_string(), "SignedData.from_json", Some("json"), None))?;
|
|
139
|
-
let ruby = Ruby::get().map_err(|e|
|
|
139
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
140
140
|
Ok(ruby.obj_wrap(Self {
|
|
141
141
|
inner: std::cell::RefCell::new(sd),
|
|
142
142
|
}))
|
|
@@ -162,15 +162,11 @@ impl SignedData {
|
|
|
162
162
|
certificates: Value,
|
|
163
163
|
) -> Result<Obj<Self>, Error> {
|
|
164
164
|
if signature.is_nil() {
|
|
165
|
-
return Err(
|
|
166
|
-
exception::arg_error(),
|
|
167
|
-
"signature is required",
|
|
165
|
+
return Err(crate::util::arg_error("signature is required",
|
|
168
166
|
));
|
|
169
167
|
}
|
|
170
168
|
if certificates.is_nil() {
|
|
171
|
-
return Err(
|
|
172
|
-
exception::arg_error(),
|
|
173
|
-
"certificates is required",
|
|
169
|
+
return Err(crate::util::arg_error("certificates is required",
|
|
174
170
|
));
|
|
175
171
|
}
|
|
176
172
|
|
|
@@ -179,16 +175,15 @@ impl SignedData {
|
|
|
179
175
|
|
|
180
176
|
let certs_array: magnus::RArray = magnus::RArray::try_convert(certificates)?;
|
|
181
177
|
let mut cert_ders = Vec::with_capacity(certs_array.len());
|
|
182
|
-
for
|
|
183
|
-
let v = item?;
|
|
178
|
+
for v in certs_array.into_iter() {
|
|
184
179
|
let der = bytes_from_value(v)?;
|
|
185
180
|
enforce_size(der.len())?;
|
|
186
181
|
cert_ders.push(der);
|
|
187
182
|
}
|
|
188
183
|
|
|
189
|
-
let ruby = Ruby::get().map_err(|e|
|
|
184
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
190
185
|
let sd = build_detached_signature(Vec::new(), algorithm, sig_bytes, cert_ders)
|
|
191
|
-
.map_err(|e|
|
|
186
|
+
.map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
192
187
|
Ok(ruby.obj_wrap(Self {
|
|
193
188
|
inner: std::cell::RefCell::new(sd),
|
|
194
189
|
}))
|
|
@@ -196,7 +191,7 @@ impl SignedData {
|
|
|
196
191
|
|
|
197
192
|
fn to_json(&self) -> Result<String, Error> {
|
|
198
193
|
serde_json::to_string(&*self.inner.borrow())
|
|
199
|
-
.map_err(|e|
|
|
194
|
+
.map_err(|e| crate::util::runtime(e.to_string()))
|
|
200
195
|
}
|
|
201
196
|
|
|
202
197
|
/// Encode this SignedData as DER bytes (RFC 5652 ContentInfo).
|
|
@@ -204,9 +199,9 @@ impl SignedData {
|
|
|
204
199
|
/// The output is parseable by `openssl cms` / `openssl pkcs7` and
|
|
205
200
|
/// any standards-compliant CMS consumer.
|
|
206
201
|
fn to_der(&self) -> Result<RString, Error> {
|
|
207
|
-
let ruby = Ruby::get().map_err(|e|
|
|
202
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
208
203
|
let der = encode_signed_data_der(&self.inner.borrow())
|
|
209
|
-
.map_err(|e|
|
|
204
|
+
.map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
210
205
|
Ok(bytes_to_rstring(&ruby, &der))
|
|
211
206
|
}
|
|
212
207
|
|
|
@@ -225,7 +220,7 @@ impl SignedData {
|
|
|
225
220
|
fn content(&self) -> Result<Option<Obj<CertWrapper>>, Error> {
|
|
226
221
|
// Wrap the optional content bytes in a small value object so Ruby
|
|
227
222
|
// can ask `.present?` / `.bytes` without juggling nil-vs-string.
|
|
228
|
-
let ruby = Ruby::get().map_err(|e|
|
|
223
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
229
224
|
match &self.inner.borrow().encap_content_info.content {
|
|
230
225
|
Some(bytes) => {
|
|
231
226
|
let wrapper = CertWrapper {
|
|
@@ -242,16 +237,17 @@ impl SignedData {
|
|
|
242
237
|
}
|
|
243
238
|
|
|
244
239
|
fn certificate_at(&self, index: usize) -> Result<RString, Error> {
|
|
245
|
-
let ruby = Ruby::get().map_err(|e|
|
|
240
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
246
241
|
self.inner
|
|
247
242
|
.borrow()
|
|
248
243
|
.certificates
|
|
249
244
|
.get(index)
|
|
250
245
|
.map(|c| bytes_to_rstring(&ruby, c))
|
|
251
246
|
.ok_or_else(|| {
|
|
252
|
-
|
|
253
|
-
exception::index_error(),
|
|
247
|
+
crate::util::index_error(
|
|
254
248
|
format!("certificate index {index} out of range"),
|
|
249
|
+
"SignedData.certificate_at",
|
|
250
|
+
Some(index as u64),
|
|
255
251
|
)
|
|
256
252
|
})
|
|
257
253
|
}
|
|
@@ -294,8 +290,8 @@ impl SignedData {
|
|
|
294
290
|
} else {
|
|
295
291
|
Err(format!("unsupported signature algorithm OID: {oid}"))
|
|
296
292
|
}
|
|
297
|
-
}).map_err(|e|
|
|
298
|
-
let ruby = Ruby::get().map_err(|e|
|
|
293
|
+
}).map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
294
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
299
295
|
Ok(ruby.obj_wrap(CmsVerificationResult { inner: result }))
|
|
300
296
|
}
|
|
301
297
|
}
|
|
@@ -349,7 +345,7 @@ pub struct CertWrapper {
|
|
|
349
345
|
|
|
350
346
|
impl CertWrapper {
|
|
351
347
|
fn bytes(&self) -> Result<RString, Error> {
|
|
352
|
-
let ruby = Ruby::get().map_err(|e|
|
|
348
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
353
349
|
Ok(bytes_to_rstring(&ruby, &self.bytes))
|
|
354
350
|
}
|
|
355
351
|
|
|
@@ -19,10 +19,10 @@ use confium_tc_frost_p256::{
|
|
|
19
19
|
};
|
|
20
20
|
use confium_tc_cmp20::inprocess as cmp20_inprocess;
|
|
21
21
|
use confium_tc_gg18::inprocess as gg18_inprocess;
|
|
22
|
-
use magnus::{
|
|
22
|
+
use magnus::{function, method, DataTypeFunctions, Error, Module, RHash, Ruby, TryConvert, TypedData, Value};
|
|
23
23
|
use p256::Scalar;
|
|
24
24
|
|
|
25
|
-
use crate::util::threshold_error;
|
|
25
|
+
use crate::util::{bytes_from_value, bytes_to_rstring, threshold_error};
|
|
26
26
|
|
|
27
27
|
#[derive(TypedData, DataTypeFunctions)]
|
|
28
28
|
#[magnus(class = "Confium::TC::FrostP256::Share", size)]
|
|
@@ -37,7 +37,7 @@ impl ShareWrap {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
fn y_bytes(&self) -> Result<magnus::RString, Error> {
|
|
40
|
-
let ruby = Ruby::get().map_err(|e|
|
|
40
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
41
41
|
Ok(bytes_to_rstring(&ruby, &self.y_bytes))
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -49,20 +49,16 @@ fn split_secret_into_shares(
|
|
|
49
49
|
) -> Result<magnus::RArray, Error> {
|
|
50
50
|
let bytes = bytes_from_value(secret_bytes)?;
|
|
51
51
|
if bytes.len() != 32 {
|
|
52
|
-
return Err(
|
|
53
|
-
exception::arg_error(),
|
|
54
|
-
format!("secret must be exactly 32 bytes, got {}", bytes.len()),
|
|
52
|
+
return Err(crate::util::arg_error(format!("secret must be exactly 32 bytes, got {}", bytes.len()),
|
|
55
53
|
));
|
|
56
54
|
}
|
|
57
55
|
let secret_arr: [u8; 32] = bytes.as_slice().try_into().unwrap();
|
|
58
56
|
let secret = scalar_from_bytes(&secret_arr).ok_or_else(|| {
|
|
59
|
-
|
|
60
|
-
exception::arg_error(),
|
|
61
|
-
"secret is not a valid P-256 scalar (reduce mod n failed)",
|
|
57
|
+
crate::util::arg_error("secret is not a valid P-256 scalar (reduce mod n failed)",
|
|
62
58
|
)
|
|
63
59
|
})?;
|
|
64
60
|
let shares: Vec<Share> = split_secret(&secret, threshold, party_count);
|
|
65
|
-
let ruby = Ruby::get().map_err(|e|
|
|
61
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
66
62
|
let result = ruby.ary_new_capa(shares.len());
|
|
67
63
|
for s in shares {
|
|
68
64
|
result.push(ruby.obj_wrap(ShareWrap {
|
|
@@ -76,27 +72,25 @@ fn split_secret_into_shares(
|
|
|
76
72
|
fn recover(shares_value: Value) -> Result<magnus::RString, Error> {
|
|
77
73
|
let arr = magnus::RArray::try_convert(shares_value)?;
|
|
78
74
|
let mut shares: Vec<Share> = Vec::with_capacity(arr.len());
|
|
79
|
-
for v in arr.
|
|
80
|
-
let h: RHash = RHash::try_convert(v
|
|
75
|
+
for v in arr.into_iter() {
|
|
76
|
+
let h: RHash = RHash::try_convert(v)?;
|
|
81
77
|
let x: u32 = h.fetch::<_, u32>("x")?;
|
|
82
78
|
let y_value: Value = h.fetch::<_, Value>("y")?;
|
|
83
79
|
let y_bytes = bytes_from_value(y_value)?;
|
|
84
80
|
if y_bytes.len() != 32 {
|
|
85
|
-
return Err(
|
|
86
|
-
exception::arg_error(),
|
|
87
|
-
format!("share y must be 32 bytes, got {}", y_bytes.len()),
|
|
81
|
+
return Err(crate::util::arg_error(format!("share y must be 32 bytes, got {}", y_bytes.len()),
|
|
88
82
|
));
|
|
89
83
|
}
|
|
90
84
|
let y_arr: [u8; 32] = y_bytes.as_slice().try_into().unwrap();
|
|
91
85
|
let y = scalar_from_bytes(&y_arr).ok_or_else(|| {
|
|
92
|
-
|
|
86
|
+
crate::util::arg_error("share y is not a valid P-256 scalar")
|
|
93
87
|
})?;
|
|
94
88
|
shares.push(Share { x, y });
|
|
95
89
|
}
|
|
96
90
|
let refs: Vec<&Share> = shares.iter().collect();
|
|
97
91
|
let secret = recover_secret(&refs)
|
|
98
|
-
.map_err(|e|
|
|
99
|
-
let ruby = Ruby::get().map_err(|e|
|
|
92
|
+
.map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
93
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
100
94
|
Ok(bytes_to_rstring(&ruby, scalar_to_bytes(&secret).as_ref()))
|
|
101
95
|
}
|
|
102
96
|
|
|
@@ -114,14 +108,12 @@ fn sign(private_key_bytes: Value, message: Value) -> Result<RHash, Error> {
|
|
|
114
108
|
let pk_bytes = bytes_from_value(private_key_bytes)?;
|
|
115
109
|
let msg = bytes_from_value(message)?;
|
|
116
110
|
if pk_bytes.len() != 32 {
|
|
117
|
-
return Err(
|
|
118
|
-
exception::arg_error(),
|
|
119
|
-
format!("private key must be 32 bytes, got {}", pk_bytes.len()),
|
|
111
|
+
return Err(crate::util::arg_error(format!("private key must be 32 bytes, got {}", pk_bytes.len()),
|
|
120
112
|
));
|
|
121
113
|
}
|
|
122
114
|
let pk_arr: [u8; 32] = pk_bytes.as_slice().try_into().unwrap();
|
|
123
115
|
let secret = scalar_from_bytes(&pk_arr).ok_or_else(|| {
|
|
124
|
-
|
|
116
|
+
crate::util::arg_error("private key not a valid P-256 scalar")
|
|
125
117
|
})?;
|
|
126
118
|
let kp = Keypair {
|
|
127
119
|
secret_scalar: secret,
|
|
@@ -137,7 +129,7 @@ fn sign(private_key_bytes: Value, message: Value) -> Result<RHash, Error> {
|
|
|
137
129
|
Some(&msg),
|
|
138
130
|
Some(&e.to_string()),
|
|
139
131
|
);
|
|
140
|
-
return Err(
|
|
132
|
+
return Err(crate::util::crypto_error(e.to_string(), "FrostP256.sign", "ecdsa-p256"));
|
|
141
133
|
}
|
|
142
134
|
};
|
|
143
135
|
crate::audit::fire_event(
|
|
@@ -147,38 +139,14 @@ fn sign(private_key_bytes: Value, message: Value) -> Result<RHash, Error> {
|
|
|
147
139
|
Some(&msg),
|
|
148
140
|
None,
|
|
149
141
|
);
|
|
150
|
-
let ruby = Ruby::get().map_err(|e|
|
|
142
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
151
143
|
let result = ruby.hash_new();
|
|
152
144
|
result.aset("der", bytes_to_rstring(&ruby, &signed.der_bytes))?;
|
|
153
145
|
result.aset("fixed", bytes_to_rstring(&ruby, &signed.fixed_bytes))?;
|
|
154
146
|
Ok(result)
|
|
155
147
|
}
|
|
156
148
|
|
|
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
149
|
|
|
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
150
|
|
|
183
151
|
// ===== ElGamal-P256 threshold encryption (KEM-style) =====
|
|
184
152
|
|
|
@@ -186,7 +154,7 @@ fn elgamal_encapsulate(ruby: &Ruby, public_key_bytes: Value) -> Result<RHash, Er
|
|
|
186
154
|
let bytes = bytes_from_value(public_key_bytes)?;
|
|
187
155
|
let pk = ElGamalPublicKey { bytes };
|
|
188
156
|
let (ciphertext, shared_secret) = encapsulate(&pk)
|
|
189
|
-
.map_err(|e|
|
|
157
|
+
.map_err(|e| crate::util::crypto_error(e.to_string(), "ElGamalP256.encapsulate", "elgamal-p256"))?;
|
|
190
158
|
let result = ruby.hash_new();
|
|
191
159
|
let ct_hash = ruby.hash_new();
|
|
192
160
|
ct_hash.aset("c1", bytes_to_rstring(ruby, &ciphertext.c1))?;
|
|
@@ -210,8 +178,8 @@ fn elgamal_partial_decrypt(party_index: u32, share_bytes: Value, ciphertext_valu
|
|
|
210
178
|
bytes: share_b,
|
|
211
179
|
};
|
|
212
180
|
let partial = partial_decrypt(&share, &ciphertext)
|
|
213
|
-
.map_err(|e|
|
|
214
|
-
let ruby = Ruby::get().map_err(|e|
|
|
181
|
+
.map_err(|e| crate::util::crypto_error(e.to_string(), "ElGamalP256.partial_decrypt", "elgamal-p256"))?;
|
|
182
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
215
183
|
let result = ruby.hash_new();
|
|
216
184
|
result.aset("party_index", partial.party_index)?;
|
|
217
185
|
result.aset("bytes", bytes_to_rstring(&ruby, &partial.bytes))?;
|
|
@@ -221,8 +189,8 @@ fn elgamal_partial_decrypt(party_index: u32, share_bytes: Value, ciphertext_valu
|
|
|
221
189
|
fn elgamal_aggregate(partials_value: Value, threshold: u32, ciphertext_value: Value) -> Result<magnus::RString, Error> {
|
|
222
190
|
let arr = magnus::RArray::try_convert(partials_value)?;
|
|
223
191
|
let mut partials: Vec<PartialDecryption> = Vec::with_capacity(arr.len());
|
|
224
|
-
for v in arr.
|
|
225
|
-
let h: RHash = RHash::try_convert(v
|
|
192
|
+
for v in arr.into_iter() {
|
|
193
|
+
let h: RHash = RHash::try_convert(v)?;
|
|
226
194
|
let party_index: u32 = h.fetch::<_, u32>("party_index")?;
|
|
227
195
|
let bytes_value: Value = h.fetch::<_, Value>("bytes")?;
|
|
228
196
|
partials.push(PartialDecryption {
|
|
@@ -238,8 +206,8 @@ fn elgamal_aggregate(partials_value: Value, threshold: u32, ciphertext_value: Va
|
|
|
238
206
|
c2: bytes_from_value(c2_value)?,
|
|
239
207
|
};
|
|
240
208
|
let shared = aggregate_partials(&partials, threshold, &ciphertext)
|
|
241
|
-
.map_err(|e|
|
|
242
|
-
let ruby = Ruby::get().map_err(|e|
|
|
209
|
+
.map_err(|e| crate::util::crypto_error(e.to_string(), "ElGamalP256.aggregate_partials", "elgamal-p256"))?;
|
|
210
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
243
211
|
Ok(bytes_to_rstring(&ruby, &shared))
|
|
244
212
|
}
|
|
245
213
|
|
|
@@ -317,8 +285,8 @@ fn cmp20_sign(
|
|
|
317
285
|
) -> Result<magnus::RString, Error> {
|
|
318
286
|
let arr = magnus::RArray::try_convert(shares_value)?;
|
|
319
287
|
let mut share_bytes: Vec<Vec<u8>> = Vec::with_capacity(arr.len());
|
|
320
|
-
for v in arr.
|
|
321
|
-
let s = bytes_from_value(v
|
|
288
|
+
for v in arr.into_iter() {
|
|
289
|
+
let s = bytes_from_value(v)?;
|
|
322
290
|
share_bytes.push(s);
|
|
323
291
|
}
|
|
324
292
|
let supplied = share_bytes.len();
|
|
@@ -389,8 +357,8 @@ fn gg18_sign(
|
|
|
389
357
|
) -> Result<magnus::RString, Error> {
|
|
390
358
|
let arr = magnus::RArray::try_convert(shares_value)?;
|
|
391
359
|
let mut share_bytes: Vec<Vec<u8>> = Vec::with_capacity(arr.len());
|
|
392
|
-
for v in arr.
|
|
393
|
-
let s = bytes_from_value(v
|
|
360
|
+
for v in arr.into_iter() {
|
|
361
|
+
let s = bytes_from_value(v)?;
|
|
394
362
|
share_bytes.push(s);
|
|
395
363
|
}
|
|
396
364
|
let supplied = share_bytes.len();
|
|
@@ -9,9 +9,10 @@ use confium_transparency::{
|
|
|
9
9
|
entry::{ArtifactType, MerkleEntry},
|
|
10
10
|
merkle::{Hash, InclusionProof as RustInclusionProof, MerkleTree as RustMerkleTree, Side},
|
|
11
11
|
};
|
|
12
|
+
use crate::util::{bytes_from_value, bytes_to_rstring};
|
|
12
13
|
use magnus::{
|
|
13
|
-
|
|
14
|
-
Module, Object,
|
|
14
|
+
function, method, prelude::*, typed_data::Obj, DataTypeFunctions, Error, IntoValue,
|
|
15
|
+
Module, Object, Ruby, TypedData, Value,
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
#[derive(TypedData, DataTypeFunctions)]
|
|
@@ -30,9 +31,7 @@ impl MerkleTree {
|
|
|
30
31
|
fn append(&self, artifact_hash: Value) -> Result<u64, Error> {
|
|
31
32
|
let bytes = bytes_from_value(artifact_hash)?;
|
|
32
33
|
if bytes.len() != 32 {
|
|
33
|
-
return Err(
|
|
34
|
-
exception::arg_error(),
|
|
35
|
-
format!("artifact_hash must be exactly 32 bytes, got {}", bytes.len()),
|
|
34
|
+
return Err(crate::util::arg_error(format!("artifact_hash must be exactly 32 bytes, got {}", bytes.len()),
|
|
36
35
|
));
|
|
37
36
|
}
|
|
38
37
|
let mut artifact_hash = [0u8; 32];
|
|
@@ -54,7 +53,7 @@ impl MerkleTree {
|
|
|
54
53
|
/// to iterate: `tree.to_a.map(&:sequence)` etc.
|
|
55
54
|
fn entries(&self) -> Result<magnus::RArray, Error> {
|
|
56
55
|
let len = self.inner.borrow().len();
|
|
57
|
-
let ruby = Ruby::get().map_err(|e|
|
|
56
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
58
57
|
let arr = ruby.ary_new_capa(len);
|
|
59
58
|
for i in 0..(len as u64) {
|
|
60
59
|
let proof = self.inclusion_proof(i)?;
|
|
@@ -74,7 +73,7 @@ impl MerkleTree {
|
|
|
74
73
|
.consistency_proof(old_size)
|
|
75
74
|
.map_err(|e| crate::util::index_error(e.to_string(), "MerkleTree.consistency_proof", Some(old_size as u64)))?;
|
|
76
75
|
let ruby = Ruby::get()
|
|
77
|
-
.map_err(|e|
|
|
76
|
+
.map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
78
77
|
let arr = ruby.ary_new_capa(proof.len());
|
|
79
78
|
for h in &proof {
|
|
80
79
|
arr.push(bytes_to_rstring(&ruby, h))?;
|
|
@@ -107,16 +106,12 @@ impl MerkleTree {
|
|
|
107
106
|
) -> Result<bool, Error> {
|
|
108
107
|
let old_bytes = bytes_from_value(old_root)?;
|
|
109
108
|
if old_bytes.len() != 32 {
|
|
110
|
-
return Err(
|
|
111
|
-
exception::arg_error(),
|
|
112
|
-
format!("old_root must be 32 bytes, got {}", old_bytes.len()),
|
|
109
|
+
return Err(crate::util::arg_error(format!("old_root must be 32 bytes, got {}", old_bytes.len()),
|
|
113
110
|
));
|
|
114
111
|
}
|
|
115
112
|
let new_bytes = bytes_from_value(new_root)?;
|
|
116
113
|
if new_bytes.len() != 32 {
|
|
117
|
-
return Err(
|
|
118
|
-
exception::arg_error(),
|
|
119
|
-
format!("new_root must be 32 bytes, got {}", new_bytes.len()),
|
|
114
|
+
return Err(crate::util::arg_error(format!("new_root must be 32 bytes, got {}", new_bytes.len()),
|
|
120
115
|
));
|
|
121
116
|
}
|
|
122
117
|
let mut old_root_hash: Hash = [0u8; 32];
|
|
@@ -125,12 +120,10 @@ impl MerkleTree {
|
|
|
125
120
|
new_root_hash.copy_from_slice(&new_bytes);
|
|
126
121
|
|
|
127
122
|
let mut proof_hashes: Vec<Hash> = Vec::with_capacity(proof.len());
|
|
128
|
-
for item in proof.
|
|
129
|
-
let bytes = bytes_from_value(item
|
|
123
|
+
for item in proof.into_iter() {
|
|
124
|
+
let bytes = bytes_from_value(item)?;
|
|
130
125
|
if bytes.len() != 32 {
|
|
131
|
-
return Err(
|
|
132
|
-
exception::arg_error(),
|
|
133
|
-
format!("proof entries must be 32 bytes, got {}", bytes.len()),
|
|
126
|
+
return Err(crate::util::arg_error(format!("proof entries must be 32 bytes, got {}", bytes.len()),
|
|
134
127
|
));
|
|
135
128
|
}
|
|
136
129
|
let mut h: Hash = [0u8; 32];
|
|
@@ -162,7 +155,7 @@ impl MerkleTree {
|
|
|
162
155
|
// Re-derive the leaf hash with the same algorithm the tree uses
|
|
163
156
|
// internally (H(0x01 | entry_hash)).
|
|
164
157
|
let leaf_hash = hash_leaf(entry.entry_hash());
|
|
165
|
-
let ruby = Ruby::get().map_err(|e|
|
|
158
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
166
159
|
Ok(ruby.obj_wrap(InclusionProofWrap {
|
|
167
160
|
inner: proof,
|
|
168
161
|
leaf_hash,
|
|
@@ -187,7 +180,7 @@ impl InclusionProofWrap {
|
|
|
187
180
|
}
|
|
188
181
|
|
|
189
182
|
fn steps(&self) -> Result<Value, Error> {
|
|
190
|
-
let ruby = Ruby::get().map_err(|e|
|
|
183
|
+
let ruby = Ruby::get().map_err(|e| crate::util::runtime(e.to_string()))?;
|
|
191
184
|
let result = ruby.hash_new();
|
|
192
185
|
for (i, step) in self.inner.steps.iter().enumerate() {
|
|
193
186
|
let step_hash = ruby.hash_new();
|
|
@@ -207,9 +200,7 @@ impl InclusionProofWrap {
|
|
|
207
200
|
fn verify(&self, root_bytes: Value) -> Result<bool, Error> {
|
|
208
201
|
let bytes = bytes_from_value(root_bytes)?;
|
|
209
202
|
if bytes.len() != 32 {
|
|
210
|
-
return Err(
|
|
211
|
-
exception::arg_error(),
|
|
212
|
-
format!("root must be exactly 32 bytes, got {}", bytes.len()),
|
|
203
|
+
return Err(crate::util::arg_error(format!("root must be exactly 32 bytes, got {}", bytes.len()),
|
|
213
204
|
));
|
|
214
205
|
}
|
|
215
206
|
let mut root: Hash = [0u8; 32];
|
|
@@ -234,16 +225,12 @@ impl InclusionProofWrap {
|
|
|
234
225
|
fn verify_with_leaf(&self, leaf_bytes: Value, root_bytes: Value) -> Result<bool, Error> {
|
|
235
226
|
let leaf = bytes_from_value(leaf_bytes)?;
|
|
236
227
|
if leaf.len() != 32 {
|
|
237
|
-
return Err(
|
|
238
|
-
exception::arg_error(),
|
|
239
|
-
format!("leaf_hash must be exactly 32 bytes, got {}", leaf.len()),
|
|
228
|
+
return Err(crate::util::arg_error(format!("leaf_hash must be exactly 32 bytes, got {}", leaf.len()),
|
|
240
229
|
));
|
|
241
230
|
}
|
|
242
231
|
let root_raw = bytes_from_value(root_bytes)?;
|
|
243
232
|
if root_raw.len() != 32 {
|
|
244
|
-
return Err(
|
|
245
|
-
exception::arg_error(),
|
|
246
|
-
format!("root must be exactly 32 bytes, got {}", root_raw.len()),
|
|
233
|
+
return Err(crate::util::arg_error(format!("root must be exactly 32 bytes, got {}", root_raw.len()),
|
|
247
234
|
));
|
|
248
235
|
}
|
|
249
236
|
let mut current: Hash = [0u8; 32];
|
|
@@ -261,35 +248,7 @@ impl InclusionProofWrap {
|
|
|
261
248
|
}
|
|
262
249
|
}
|
|
263
250
|
|
|
264
|
-
fn bytes_from_value(v: Value) -> Result<Vec<u8>, Error> {
|
|
265
|
-
// Accept either a binary String (preferred for byte data) or an Array
|
|
266
|
-
// of small integers (also commonly used in Ruby crypto code).
|
|
267
|
-
if let Ok(s) = RString::try_convert(v) {
|
|
268
|
-
// SAFETY: we treat the string's raw bytes as opaque cryptographic
|
|
269
|
-
// input — we never interpret them as a UTF-8 string. Encoding is
|
|
270
|
-
// irrelevant for hash input.
|
|
271
|
-
return Ok(unsafe { s.as_slice() }.to_vec());
|
|
272
|
-
}
|
|
273
|
-
let arr: Vec<i64> = Vec::<i64>::try_convert(v)?;
|
|
274
|
-
arr.into_iter()
|
|
275
|
-
.map(|i| {
|
|
276
|
-
if !(0..=255).contains(&i) {
|
|
277
|
-
Err(Error::new(
|
|
278
|
-
exception::arg_error(),
|
|
279
|
-
format!("byte out of range 0..255: {i}"),
|
|
280
|
-
))
|
|
281
|
-
} else {
|
|
282
|
-
Ok(i as u8)
|
|
283
|
-
}
|
|
284
|
-
})
|
|
285
|
-
.collect()
|
|
286
|
-
}
|
|
287
251
|
|
|
288
|
-
fn bytes_to_rstring(_ruby: &Ruby, bytes: &[u8]) -> RString {
|
|
289
|
-
let s = RString::buf_new(0);
|
|
290
|
-
s.cat(bytes);
|
|
291
|
-
s
|
|
292
|
-
}
|
|
293
252
|
|
|
294
253
|
fn hash_leaf(entry_hash: Hash) -> Hash {
|
|
295
254
|
use sha2::{Digest, Sha256};
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
//! DRY consolidation: a single `bytes_from_value` + size cap + string
|
|
4
4
|
//! conversion + typed-error helper shared by every subsystem module
|
|
5
5
|
//! (composite, pki, tc, transparency, deployment, attributes).
|
|
6
|
+
//!
|
|
7
|
+
//! This module wraps magnus's deprecated `exception::*` constructors
|
|
8
|
+
//! so the rest of the crate never touches them; their replacements
|
|
9
|
+
//! need a `Ruby` handle, which the failure fallbacks here lack.
|
|
10
|
+
#![allow(deprecated)]
|
|
6
11
|
|
|
7
12
|
use magnus::prelude::*;
|
|
8
13
|
use magnus::{exception, Error, RHash, RString, Ruby, TryConvert, Value};
|
|
@@ -14,6 +19,28 @@ use magnus::{exception, Error, RHash, RString, Ruby, TryConvert, Value};
|
|
|
14
19
|
/// prevent trivial memory-exhaustion attacks.
|
|
15
20
|
pub const MAX_INPUT_SIZE: usize = 1 << 20;
|
|
16
21
|
|
|
22
|
+
/// Build a plain `RuntimeError`. Centralizes every raise site so the
|
|
23
|
+
/// deprecated `magnus::exception::runtime_error()` fallback lives in
|
|
24
|
+
/// exactly one place (its replacement needs a `Ruby` handle, which is
|
|
25
|
+
/// unavailable when `Ruby::get()` itself fails).
|
|
26
|
+
pub fn runtime(msg: impl Into<String>) -> Error {
|
|
27
|
+
let msg: String = msg.into();
|
|
28
|
+
match Ruby::get() {
|
|
29
|
+
Ok(ruby) => Error::new(ruby.exception_runtime_error(), msg),
|
|
30
|
+
Err(_) => Error::new(exception::runtime_error(), msg),
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// Build an `ArgumentError` — for bad argument shapes/ranges, the
|
|
35
|
+
/// class callers should see before any Confium semantics apply.
|
|
36
|
+
pub fn arg_error(msg: impl Into<String>) -> Error {
|
|
37
|
+
let msg: String = msg.into();
|
|
38
|
+
match Ruby::get() {
|
|
39
|
+
Ok(ruby) => Error::new(ruby.exception_arg_error(), msg),
|
|
40
|
+
Err(_) => Error::new(exception::arg_error(), msg),
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
17
44
|
/// Convert a Ruby value to bytes. Accepts a binary `String` (any
|
|
18
45
|
/// encoding) or an `Array<Integer>`. Enforces a 1 MiB size cap.
|
|
19
46
|
pub fn bytes_from_value(v: Value) -> Result<Vec<u8>, Error> {
|
|
@@ -30,10 +57,7 @@ pub fn bytes_from_value(v: Value) -> Result<Vec<u8>, Error> {
|
|
|
30
57
|
arr.into_iter()
|
|
31
58
|
.map(|i| {
|
|
32
59
|
if !(0..=255).contains(&i) {
|
|
33
|
-
Err(
|
|
34
|
-
exception::arg_error(),
|
|
35
|
-
format!("byte out of range 0..255: {i}"),
|
|
36
|
-
))
|
|
60
|
+
Err(arg_error(format!("byte out of range 0..255: {i}")))
|
|
37
61
|
} else {
|
|
38
62
|
Ok(i as u8)
|
|
39
63
|
}
|
|
@@ -46,18 +70,15 @@ pub fn bytes_from_value(v: Value) -> Result<Vec<u8>, Error> {
|
|
|
46
70
|
/// memory-exhaustion attacks.
|
|
47
71
|
pub fn enforce_size(len: usize) -> Result<(), Error> {
|
|
48
72
|
if len > MAX_INPUT_SIZE {
|
|
49
|
-
return Err(
|
|
50
|
-
exception::arg_error(),
|
|
51
|
-
format!("input size {0} exceeds max {MAX_INPUT_SIZE}", len),
|
|
52
|
-
));
|
|
73
|
+
return Err(arg_error(format!("input size {0} exceeds max {MAX_INPUT_SIZE}", len)));
|
|
53
74
|
}
|
|
54
75
|
Ok(())
|
|
55
76
|
}
|
|
56
77
|
|
|
57
78
|
/// Build a Ruby binary `String` from a byte slice. Avoids the UTF-8
|
|
58
|
-
/// round-trip
|
|
59
|
-
pub fn bytes_to_rstring(
|
|
60
|
-
let s =
|
|
79
|
+
/// round-trip for already-binary input.
|
|
80
|
+
pub fn bytes_to_rstring(ruby: &Ruby, bytes: &[u8]) -> RString {
|
|
81
|
+
let s = ruby.str_buf_new(0);
|
|
61
82
|
s.cat(bytes);
|
|
62
83
|
s
|
|
63
84
|
}
|
data/lib/confium/version.rb
CHANGED