Skip to content

Commit 53ea2ec

Browse files
committed
Return Result instead of Option, and raise corresponding exceptions in Python
1 parent c8687fd commit 53ea2ec

File tree

13 files changed

+414
-172
lines changed

13 files changed

+414
-172
lines changed

umbral-pre-python/src/lib.rs

+225-77
Large diffs are not rendered by default.

umbral-pre-python/umbral_pre/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
Capsule,
66
KeyFrag,
77
CapsuleFrag,
8+
UmbralError,
89
encrypt,
910
decrypt_original,
1011
decrypt_reencrypted,

umbral-pre-wasm/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ impl CapsuleWithFrags {
114114
backend_cfrags.as_slice(),
115115
ciphertext,
116116
)
117+
.ok()
117118
}
118119
}
119120

umbral-pre/src/capsule.rs

+44-20
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,33 @@ use crate::curve::{CurvePoint, CurveScalar};
33
use crate::hashing_ds::{hash_capsule_points, hash_to_polynomial_arg, hash_to_shared_secret};
44
use crate::keys::{PublicKey, SecretKey};
55
use crate::params::Parameters;
6-
use crate::traits::SerializableToArray;
6+
use crate::traits::{DeserializationError, SerializableToArray};
77

88
use alloc::vec::Vec;
99

1010
use generic_array::sequence::Concat;
1111
use generic_array::GenericArray;
1212
use typenum::op;
1313

14+
/// Errors that can happen when opening a `Capsule` using reencrypted `CapsuleFrag` objects.
15+
#[derive(Debug, PartialEq)]
16+
pub enum OpenReencryptedError {
17+
/// An empty capsule fragment list is given.
18+
NoCapsuleFrags,
19+
/// Capsule fragments are mismatched (originated from [`KeyFrag`](crate::KeyFrag) objects
20+
/// generated by different [`generate_kfrags`](crate::generate_kfrags) calls).
21+
MismatchedCapsuleFrags,
22+
/// Some of the given capsule fragments are repeated.
23+
RepeatingCapsuleFrags,
24+
/// An internally hashed value is zero.
25+
/// See [rust-umbral#39](https://github.com/nucypher/rust-umbral/issues/39).
26+
ZeroHash,
27+
/// Internal validation of the result has failed.
28+
/// Can be caused by an incorrect (possibly modified) capsule
29+
/// or some of the capsule fragments.
30+
ValidationFailed,
31+
}
32+
1433
/// Encapsulated symmetric key used to encrypt the plaintext.
1534
#[derive(Clone, Copy, Debug, PartialEq)]
1635
pub struct Capsule {
@@ -34,11 +53,12 @@ impl SerializableToArray for Capsule {
3453
.concat(self.signature.to_array())
3554
}
3655

37-
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Option<Self> {
56+
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Result<Self, DeserializationError> {
3857
let (point_e, rest) = CurvePoint::take(*arr)?;
3958
let (point_v, rest) = CurvePoint::take(rest)?;
4059
let signature = CurveScalar::take_last(rest)?;
4160
Self::new_verified(point_e, point_v, signature)
61+
.ok_or(DeserializationError::ConstructionFailure)
4262
}
4363
}
4464

@@ -104,15 +124,15 @@ impl Capsule {
104124
receiving_sk: &SecretKey,
105125
delegating_pk: &PublicKey,
106126
cfrags: &[CapsuleFrag],
107-
) -> Option<CurvePoint> {
127+
) -> Result<CurvePoint, OpenReencryptedError> {
108128
if cfrags.is_empty() {
109-
return None;
129+
return Err(OpenReencryptedError::NoCapsuleFrags);
110130
}
111131

112132
let precursor = cfrags[0].precursor;
113133

114134
if !cfrags.iter().all(|cfrag| cfrag.precursor == precursor) {
115-
return None;
135+
return Err(OpenReencryptedError::MismatchedCapsuleFrags);
116136
}
117137

118138
let pub_key = PublicKey::from_secret_key(receiving_sk).to_point();
@@ -128,9 +148,10 @@ impl Capsule {
128148
let mut e_prime = CurvePoint::identity();
129149
let mut v_prime = CurvePoint::identity();
130150
for (i, cfrag) in (&cfrags).iter().enumerate() {
131-
// There is a minuscule probability that two elements of `lc` are equal,
151+
// There is a minuscule probability that coefficients for two different frags are equal,
132152
// in which case we'd rather fail gracefully.
133-
let lambda_i = lambda_coeff(&lc, i)?;
153+
let lambda_i =
154+
lambda_coeff(&lc, i).ok_or(OpenReencryptedError::RepeatingCapsuleFrags)?;
134155
e_prime = &e_prime + &(&cfrag.point_e1 * &lambda_i);
135156
v_prime = &v_prime + &(&cfrag.point_v1 * &lambda_i);
136157
}
@@ -149,14 +170,14 @@ impl Capsule {
149170
// Technically, it is supposed to be non-zero by the choice of `precursor`,
150171
// but if is was somehow replaced by an incorrect value,
151172
// we'd rather fail gracefully than panic.
152-
let inv_d = inv_d_opt?;
173+
let inv_d = inv_d_opt.ok_or(OpenReencryptedError::ZeroHash)?;
153174

154175
if &orig_pub_key * &(&s * &inv_d) != &(&e_prime * &h) + &v_prime {
155-
return None;
176+
return Err(OpenReencryptedError::ValidationFailed);
156177
}
157178

158179
let shared_key = &(&e_prime + &v_prime) * &d;
159-
Some(shared_key)
180+
Ok(shared_key)
160181
}
161182
}
162183

@@ -177,7 +198,7 @@ mod tests {
177198

178199
use alloc::vec::Vec;
179200

180-
use super::Capsule;
201+
use super::{Capsule, OpenReencryptedError};
181202
use crate::{
182203
encrypt, generate_kfrags, reencrypt, CapsuleFrag, PublicKey, SecretKey, SerializableToArray,
183204
};
@@ -220,9 +241,10 @@ mod tests {
220241
assert_eq!(key_seed, key_seed_reenc);
221242

222243
// Empty cfrag vector
223-
assert!(capsule
224-
.open_reencrypted(&receiving_sk, &delegating_pk, &[])
225-
.is_none());
244+
assert_eq!(
245+
capsule.open_reencrypted(&receiving_sk, &delegating_pk, &[]),
246+
Err(OpenReencryptedError::NoCapsuleFrags)
247+
);
226248

227249
// Mismatched cfrags - each `generate_kfrags()` uses new randoms.
228250
let kfrags2 = generate_kfrags(&delegating_sk, &receiving_pk, &signing_sk, 2, 3, true, true);
@@ -237,14 +259,16 @@ mod tests {
237259
.cloned()
238260
.chain(cfrags2[1..2].iter().cloned())
239261
.collect();
240-
assert!(capsule
241-
.open_reencrypted(&receiving_sk, &delegating_pk, &mismatched_cfrags)
242-
.is_none());
262+
assert_eq!(
263+
capsule.open_reencrypted(&receiving_sk, &delegating_pk, &mismatched_cfrags),
264+
Err(OpenReencryptedError::MismatchedCapsuleFrags)
265+
);
243266

244267
// Mismatched capsule
245268
let (capsule2, _key_seed) = Capsule::from_public_key(&delegating_pk);
246-
assert!(capsule2
247-
.open_reencrypted(&receiving_sk, &delegating_pk, &cfrags)
248-
.is_none());
269+
assert_eq!(
270+
capsule2.open_reencrypted(&receiving_sk, &delegating_pk, &cfrags),
271+
Err(OpenReencryptedError::ValidationFailed)
272+
);
249273
}
250274
}

umbral-pre/src/capsule_frag.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::curve::{CurvePoint, CurveScalar};
33
use crate::hashing_ds::{hash_to_cfrag_signature, hash_to_cfrag_verification};
44
use crate::key_frag::{KeyFrag, KeyFragID};
55
use crate::keys::{PublicKey, Signature};
6-
use crate::traits::SerializableToArray;
6+
use crate::traits::{DeserializationError, SerializableToArray};
77

88
use generic_array::sequence::Concat;
99
use generic_array::GenericArray;
@@ -38,14 +38,14 @@ impl SerializableToArray for CapsuleFragProof {
3838
.concat(self.kfrag_signature.to_array())
3939
}
4040

41-
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Option<Self> {
41+
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Result<Self, DeserializationError> {
4242
let (point_e2, rest) = CurvePoint::take(*arr)?;
4343
let (point_v2, rest) = CurvePoint::take(rest)?;
4444
let (kfrag_commitment, rest) = CurvePoint::take(rest)?;
4545
let (kfrag_pok, rest) = CurvePoint::take(rest)?;
4646
let (signature, rest) = CurveScalar::take(rest)?;
4747
let kfrag_signature = Signature::take_last(rest)?;
48-
Some(Self {
48+
Ok(Self {
4949
point_e2,
5050
point_v2,
5151
kfrag_commitment,
@@ -126,13 +126,13 @@ impl SerializableToArray for CapsuleFrag {
126126
.concat(self.proof.to_array())
127127
}
128128

129-
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Option<Self> {
129+
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Result<Self, DeserializationError> {
130130
let (point_e1, rest) = CurvePoint::take(*arr)?;
131131
let (point_v1, rest) = CurvePoint::take(rest)?;
132132
let (kfrag_id, rest) = KeyFragID::take(rest)?;
133133
let (precursor, rest) = CurvePoint::take(rest)?;
134134
let proof = CapsuleFragProof::take_last(rest)?;
135-
Some(Self {
135+
Ok(Self {
136136
point_e1,
137137
point_v1,
138138
kfrag_id,

umbral-pre/src/curve.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use k256::Secp256k1;
1414
use rand_core::OsRng;
1515
use subtle::CtOption;
1616

17-
use crate::traits::SerializableToArray;
17+
use crate::traits::{DeserializationError, SerializableToArray};
1818

1919
pub(crate) type CurveType = Secp256k1;
2020

@@ -80,8 +80,10 @@ impl SerializableToArray for CurveScalar {
8080
self.0.to_bytes()
8181
}
8282

83-
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Option<Self> {
84-
Scalar::<CurveType>::from_repr(*arr).map(Self)
83+
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Result<Self, DeserializationError> {
84+
Scalar::<CurveType>::from_repr(*arr)
85+
.map(Self)
86+
.ok_or(DeserializationError::ConstructionFailure)
8587
}
8688
}
8789

@@ -160,9 +162,12 @@ impl SerializableToArray for CurvePoint {
160162
)
161163
}
162164

163-
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Option<Self> {
164-
let ep = EncodedPoint::<CurveType>::from_bytes(arr.as_slice()).ok()?;
165+
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Result<Self, DeserializationError> {
166+
let ep = EncodedPoint::<CurveType>::from_bytes(arr.as_slice())
167+
.or(Err(DeserializationError::ConstructionFailure))?;
165168
let cp_opt: Option<BackendPoint> = BackendPoint::from_encoded_point(&ep);
166-
cp_opt.map(Self)
169+
cp_opt
170+
.map(Self)
171+
.ok_or(DeserializationError::ConstructionFailure)
167172
}
168173
}

umbral-pre/src/dem.rs

+32-6
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,24 @@ use rand_core::RngCore;
1010
use sha2::Sha256;
1111
use typenum::Unsigned;
1212

13+
/// Errors that can happen during symmetric encryption.
14+
#[derive(Debug, PartialEq)]
15+
pub enum EncryptionError {
16+
/// Given plaintext is too large for the backend to handle.
17+
PlaintextTooLarge,
18+
}
19+
20+
/// Errors that can happend during symmetric decryption.
21+
#[derive(Debug, PartialEq)]
22+
pub enum DecryptionError {
23+
/// Ciphertext (which should be prepended by the nonce) is shorter than the nonce length.
24+
CiphertextTooShort,
25+
/// The ciphertext and the authentication data are inconsistent.
26+
/// This can happen if either is modified or cut short,
27+
/// or incorrect authentication data is provided on decryption.
28+
AuthenticationFailed,
29+
}
30+
1331
pub(crate) fn kdf<T: ArrayLength<u8>>(
1432
seed: &[u8],
1533
salt: Option<&[u8]>,
@@ -42,7 +60,11 @@ impl DEM {
4260
Self { cipher }
4361
}
4462

45-
pub fn encrypt(&self, data: &[u8], authenticated_data: &[u8]) -> Option<Box<[u8]>> {
63+
pub fn encrypt(
64+
&self,
65+
data: &[u8],
66+
authenticated_data: &[u8],
67+
) -> Result<Box<[u8]>, EncryptionError> {
4668
let mut nonce = GenericArray::<u8, NonceSize>::default();
4769
OsRng.fill_bytes(&mut nonce);
4870
let nonce = XNonce::from_slice(&nonce);
@@ -52,23 +74,27 @@ impl DEM {
5274
};
5375

5476
let mut result = nonce.to_vec();
55-
let enc_data = self.cipher.encrypt(nonce, payload).ok()?;
77+
let enc_data = self
78+
.cipher
79+
.encrypt(nonce, payload)
80+
.or(Err(EncryptionError::PlaintextTooLarge))?;
81+
5682
// Somewhat inefficient, but it doesn't seem that you can pass
5783
// a mutable view of a vector to encrypt_in_place().
5884
result.extend(enc_data);
59-
Some(result.into_boxed_slice())
85+
Ok(result.into_boxed_slice())
6086
}
6187

6288
pub fn decrypt(
6389
&self,
6490
ciphertext: impl AsRef<[u8]>,
6591
authenticated_data: &[u8],
66-
) -> Option<Box<[u8]>> {
92+
) -> Result<Box<[u8]>, DecryptionError> {
6793
let nonce_size = <NonceSize as Unsigned>::to_usize();
6894
let buf_size = ciphertext.as_ref().len();
6995

7096
if buf_size < nonce_size {
71-
return None;
97+
return Err(DecryptionError::CiphertextTooShort);
7298
}
7399

74100
let nonce = XNonce::from_slice(&ciphertext.as_ref()[..nonce_size]);
@@ -78,8 +104,8 @@ impl DEM {
78104
};
79105
self.cipher
80106
.decrypt(nonce, payload)
81-
.ok()
82107
.map(|pt| pt.into_boxed_slice())
108+
.or(Err(DecryptionError::AuthenticationFailed))
83109
}
84110
}
85111

umbral-pre/src/hashing.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ pub fn unsafe_hash_to_point(dst: &[u8], data: &[u8]) -> Option<CurvePoint> {
4242
let maybe_point_bytes = sign_prefix.concat(result);
4343

4444
let maybe_point = CurvePoint::from_bytes(&maybe_point_bytes);
45-
if maybe_point.is_some() {
46-
return maybe_point;
45+
if maybe_point.is_ok() {
46+
return maybe_point.ok();
4747
}
4848

4949
i += 1

umbral-pre/src/key_frag.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::curve::{CurvePoint, CurveScalar};
22
use crate::hashing_ds::{hash_to_cfrag_signature, hash_to_polynomial_arg, hash_to_shared_secret};
33
use crate::keys::{PublicKey, SecretKey, Signature};
44
use crate::params::Parameters;
5-
use crate::traits::SerializableToArray;
5+
use crate::traits::{DeserializationError, SerializableToArray};
66

77
use alloc::boxed::Box;
88
use alloc::vec::Vec;
@@ -38,8 +38,8 @@ impl SerializableToArray for KeyFragID {
3838
self.0
3939
}
4040

41-
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Option<Self> {
42-
Some(Self(*arr))
41+
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Result<Self, DeserializationError> {
42+
Ok(Self(*arr))
4343
}
4444
}
4545

@@ -69,13 +69,13 @@ impl SerializableToArray for KeyFragProof {
6969
.concat(self.receiving_key_signed.to_array())
7070
}
7171

72-
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Option<Self> {
72+
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Result<Self, DeserializationError> {
7373
let (commitment, rest) = CurvePoint::take(*arr)?;
7474
let (signature_for_proxy, rest) = Signature::take(rest)?;
7575
let (signature_for_receiver, rest) = Signature::take(rest)?;
7676
let (delegating_key_signed, rest) = bool::take(rest)?;
7777
let receiving_key_signed = bool::take_last(rest)?;
78-
Some(Self {
78+
Ok(Self {
7979
commitment,
8080
signature_for_proxy,
8181
signature_for_receiver,
@@ -161,13 +161,13 @@ impl SerializableToArray for KeyFrag {
161161
.concat(self.proof.to_array())
162162
}
163163

164-
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Option<Self> {
164+
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Result<Self, DeserializationError> {
165165
let params = Parameters::new();
166166
let (id, rest) = KeyFragID::take(*arr)?;
167167
let (key, rest) = CurveScalar::take(rest)?;
168168
let (precursor, rest) = CurvePoint::take(rest)?;
169169
let proof = KeyFragProof::take_last(rest)?;
170-
Some(Self {
170+
Ok(Self {
171171
params,
172172
id,
173173
key,

0 commit comments

Comments
 (0)