Skip to content

Commit

Permalink
Return Result instead of Option, and raise corresponding exceptions i…
Browse files Browse the repository at this point in the history
…n Python
  • Loading branch information
fjarri committed Mar 28, 2021
1 parent 3830188 commit a9808ca
Show file tree
Hide file tree
Showing 13 changed files with 451 additions and 186 deletions.
351 changes: 260 additions & 91 deletions umbral-pre-python/src/lib.rs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions umbral-pre-python/umbral_pre/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
Capsule,
KeyFrag,
CapsuleFrag,
GenericError,
encrypt,
decrypt_original,
decrypt_reencrypted,
Expand Down
1 change: 1 addition & 0 deletions umbral-pre-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ impl CapsuleWithFrags {
backend_cfrags.as_slice(),
ciphertext,
)
.ok()
}
}

Expand Down
64 changes: 44 additions & 20 deletions umbral-pre/src/capsule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,33 @@ use crate::curve::{CurvePoint, CurveScalar};
use crate::hashing_ds::{hash_capsule_points, hash_to_polynomial_arg, hash_to_shared_secret};
use crate::keys::{PublicKey, SecretKey};
use crate::params::Parameters;
use crate::traits::SerializableToArray;
use crate::traits::{DeserializationError, SerializableToArray};

use alloc::vec::Vec;

use generic_array::sequence::Concat;
use generic_array::GenericArray;
use typenum::op;

/// Errors that can happen when opening a `Capsule` using reencrypted `CapsuleFrag` objects.
#[derive(Debug, PartialEq)]
pub enum OpenReencryptedError {
/// An empty capsule fragment list is given.
NoCapsuleFrags,
/// Capsule fragments are mismatched (originated from [`KeyFrag`](crate::KeyFrag) objects
/// generated by different [`generate_kfrags`](crate::generate_kfrags) calls).
MismatchedCapsuleFrags,
/// Some of the given capsule fragments are repeated.
RepeatingCapsuleFrags,
/// An internally hashed value is zero.
/// See [rust-umbral#39](https://github.com/nucypher/rust-umbral/issues/39).
ZeroHash,
/// Internal validation of the result has failed.
/// Can be caused by an incorrect (possibly modified) capsule
/// or some of the capsule fragments.
ValidationFailed,
}

/// Encapsulated symmetric key used to encrypt the plaintext.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Capsule {
Expand All @@ -34,11 +53,12 @@ impl SerializableToArray for Capsule {
.concat(self.signature.to_array())
}

fn from_array(arr: &GenericArray<u8, Self::Size>) -> Option<Self> {
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Result<Self, DeserializationError> {
let (point_e, rest) = CurvePoint::take(*arr)?;
let (point_v, rest) = CurvePoint::take(rest)?;
let signature = CurveScalar::take_last(rest)?;
Self::new_verified(point_e, point_v, signature)
.ok_or(DeserializationError::ConstructionFailure)
}
}

Expand Down Expand Up @@ -104,15 +124,15 @@ impl Capsule {
receiving_sk: &SecretKey,
delegating_pk: &PublicKey,
cfrags: &[CapsuleFrag],
) -> Option<CurvePoint> {
) -> Result<CurvePoint, OpenReencryptedError> {
if cfrags.is_empty() {
return None;
return Err(OpenReencryptedError::NoCapsuleFrags);
}

let precursor = cfrags[0].precursor;

if !cfrags.iter().all(|cfrag| cfrag.precursor == precursor) {
return None;
return Err(OpenReencryptedError::MismatchedCapsuleFrags);
}

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

if &orig_pub_key * &(&s * &inv_d) != &(&e_prime * &h) + &v_prime {
return None;
return Err(OpenReencryptedError::ValidationFailed);
}

let shared_key = &(&e_prime + &v_prime) * &d;
Some(shared_key)
Ok(shared_key)
}
}

Expand All @@ -177,7 +198,7 @@ mod tests {

use alloc::vec::Vec;

use super::Capsule;
use super::{Capsule, OpenReencryptedError};
use crate::{
encrypt, generate_kfrags, reencrypt, CapsuleFrag, PublicKey, SecretKey, SerializableToArray,
};
Expand Down Expand Up @@ -220,9 +241,10 @@ mod tests {
assert_eq!(key_seed, key_seed_reenc);

// Empty cfrag vector
assert!(capsule
.open_reencrypted(&receiving_sk, &delegating_pk, &[])
.is_none());
assert_eq!(
capsule.open_reencrypted(&receiving_sk, &delegating_pk, &[]),
Err(OpenReencryptedError::NoCapsuleFrags)
);

// Mismatched cfrags - each `generate_kfrags()` uses new randoms.
let kfrags2 = generate_kfrags(&delegating_sk, &receiving_pk, &signing_sk, 2, 3, true, true);
Expand All @@ -237,14 +259,16 @@ mod tests {
.cloned()
.chain(cfrags2[1..2].iter().cloned())
.collect();
assert!(capsule
.open_reencrypted(&receiving_sk, &delegating_pk, &mismatched_cfrags)
.is_none());
assert_eq!(
capsule.open_reencrypted(&receiving_sk, &delegating_pk, &mismatched_cfrags),
Err(OpenReencryptedError::MismatchedCapsuleFrags)
);

// Mismatched capsule
let (capsule2, _key_seed) = Capsule::from_public_key(&delegating_pk);
assert!(capsule2
.open_reencrypted(&receiving_sk, &delegating_pk, &cfrags)
.is_none());
assert_eq!(
capsule2.open_reencrypted(&receiving_sk, &delegating_pk, &cfrags),
Err(OpenReencryptedError::ValidationFailed)
);
}
}
10 changes: 5 additions & 5 deletions umbral-pre/src/capsule_frag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::curve::{CurvePoint, CurveScalar};
use crate::hashing_ds::{hash_to_cfrag_signature, hash_to_cfrag_verification};
use crate::key_frag::{KeyFrag, KeyFragID};
use crate::keys::{PublicKey, Signature};
use crate::traits::SerializableToArray;
use crate::traits::{DeserializationError, SerializableToArray};

use generic_array::sequence::Concat;
use generic_array::GenericArray;
Expand Down Expand Up @@ -38,14 +38,14 @@ impl SerializableToArray for CapsuleFragProof {
.concat(self.kfrag_signature.to_array())
}

fn from_array(arr: &GenericArray<u8, Self::Size>) -> Option<Self> {
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Result<Self, DeserializationError> {
let (point_e2, rest) = CurvePoint::take(*arr)?;
let (point_v2, rest) = CurvePoint::take(rest)?;
let (kfrag_commitment, rest) = CurvePoint::take(rest)?;
let (kfrag_pok, rest) = CurvePoint::take(rest)?;
let (signature, rest) = CurveScalar::take(rest)?;
let kfrag_signature = Signature::take_last(rest)?;
Some(Self {
Ok(Self {
point_e2,
point_v2,
kfrag_commitment,
Expand Down Expand Up @@ -126,13 +126,13 @@ impl SerializableToArray for CapsuleFrag {
.concat(self.proof.to_array())
}

fn from_array(arr: &GenericArray<u8, Self::Size>) -> Option<Self> {
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Result<Self, DeserializationError> {
let (point_e1, rest) = CurvePoint::take(*arr)?;
let (point_v1, rest) = CurvePoint::take(rest)?;
let (kfrag_id, rest) = KeyFragID::take(rest)?;
let (precursor, rest) = CurvePoint::take(rest)?;
let proof = CapsuleFragProof::take_last(rest)?;
Some(Self {
Ok(Self {
point_e1,
point_v1,
kfrag_id,
Expand Down
17 changes: 11 additions & 6 deletions umbral-pre/src/curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use k256::Secp256k1;
use rand_core::OsRng;
use subtle::CtOption;

use crate::traits::SerializableToArray;
use crate::traits::{DeserializationError, SerializableToArray};

pub(crate) type CurveType = Secp256k1;

Expand Down Expand Up @@ -80,8 +80,10 @@ impl SerializableToArray for CurveScalar {
self.0.to_bytes()
}

fn from_array(arr: &GenericArray<u8, Self::Size>) -> Option<Self> {
Scalar::<CurveType>::from_repr(*arr).map(Self)
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Result<Self, DeserializationError> {
Scalar::<CurveType>::from_repr(*arr)
.map(Self)
.ok_or(DeserializationError::ConstructionFailure)
}
}

Expand Down Expand Up @@ -160,9 +162,12 @@ impl SerializableToArray for CurvePoint {
)
}

fn from_array(arr: &GenericArray<u8, Self::Size>) -> Option<Self> {
let ep = EncodedPoint::<CurveType>::from_bytes(arr.as_slice()).ok()?;
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Result<Self, DeserializationError> {
let ep = EncodedPoint::<CurveType>::from_bytes(arr.as_slice())
.or(Err(DeserializationError::ConstructionFailure))?;
let cp_opt: Option<BackendPoint> = BackendPoint::from_encoded_point(&ep);
cp_opt.map(Self)
cp_opt
.map(Self)
.ok_or(DeserializationError::ConstructionFailure)
}
}
40 changes: 34 additions & 6 deletions umbral-pre/src/dem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,26 @@ use rand_core::RngCore;
use sha2::Sha256;
use typenum::Unsigned;

/// Errors that can happen during symmetric encryption.
#[derive(Debug, PartialEq)]
pub enum EncryptionError {
/// Given plaintext is too large for the backend to handle.
PlaintextTooLarge,
}

/// Errors that can happend during symmetric decryption.
#[derive(Debug, PartialEq)]
pub enum DecryptionError {
/// Ciphertext (which should be prepended by the nonce) is shorter than the nonce length.
CiphertextTooShort,
/// The ciphertext and the attached authentication data are inconsistent.
/// This can happen if:
/// - an incorrect key is used,
/// - the ciphertext is modified or cut short,
/// - an incorrect authentication data is provided on decryption.
AuthenticationFailed,
}

pub(crate) fn kdf<T: ArrayLength<u8>>(
seed: &[u8],
salt: Option<&[u8]>,
Expand Down Expand Up @@ -42,7 +62,11 @@ impl DEM {
Self { cipher }
}

pub fn encrypt(&self, data: &[u8], authenticated_data: &[u8]) -> Option<Box<[u8]>> {
pub fn encrypt(
&self,
data: &[u8],
authenticated_data: &[u8],
) -> Result<Box<[u8]>, EncryptionError> {
let mut nonce = GenericArray::<u8, NonceSize>::default();
OsRng.fill_bytes(&mut nonce);
let nonce = XNonce::from_slice(&nonce);
Expand All @@ -52,23 +76,27 @@ impl DEM {
};

let mut result = nonce.to_vec();
let enc_data = self.cipher.encrypt(nonce, payload).ok()?;
let enc_data = self
.cipher
.encrypt(nonce, payload)
.or(Err(EncryptionError::PlaintextTooLarge))?;

// Somewhat inefficient, but it doesn't seem that you can pass
// a mutable view of a vector to encrypt_in_place().
result.extend(enc_data);
Some(result.into_boxed_slice())
Ok(result.into_boxed_slice())
}

pub fn decrypt(
&self,
ciphertext: impl AsRef<[u8]>,
authenticated_data: &[u8],
) -> Option<Box<[u8]>> {
) -> Result<Box<[u8]>, DecryptionError> {
let nonce_size = <NonceSize as Unsigned>::to_usize();
let buf_size = ciphertext.as_ref().len();

if buf_size < nonce_size {
return None;
return Err(DecryptionError::CiphertextTooShort);
}

let nonce = XNonce::from_slice(&ciphertext.as_ref()[..nonce_size]);
Expand All @@ -78,8 +106,8 @@ impl DEM {
};
self.cipher
.decrypt(nonce, payload)
.ok()
.map(|pt| pt.into_boxed_slice())
.or(Err(DecryptionError::AuthenticationFailed))
}
}

Expand Down
4 changes: 2 additions & 2 deletions umbral-pre/src/hashing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ pub fn unsafe_hash_to_point(dst: &[u8], data: &[u8]) -> Option<CurvePoint> {
let maybe_point_bytes = sign_prefix.concat(result);

let maybe_point = CurvePoint::from_bytes(&maybe_point_bytes);
if maybe_point.is_some() {
return maybe_point;
if maybe_point.is_ok() {
return maybe_point.ok();
}

i += 1
Expand Down
14 changes: 7 additions & 7 deletions umbral-pre/src/key_frag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::curve::{CurvePoint, CurveScalar};
use crate::hashing_ds::{hash_to_cfrag_signature, hash_to_polynomial_arg, hash_to_shared_secret};
use crate::keys::{PublicKey, SecretKey, Signature};
use crate::params::Parameters;
use crate::traits::SerializableToArray;
use crate::traits::{DeserializationError, SerializableToArray};

use alloc::boxed::Box;
use alloc::vec::Vec;
Expand Down Expand Up @@ -38,8 +38,8 @@ impl SerializableToArray for KeyFragID {
self.0
}

fn from_array(arr: &GenericArray<u8, Self::Size>) -> Option<Self> {
Some(Self(*arr))
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Result<Self, DeserializationError> {
Ok(Self(*arr))
}
}

Expand Down Expand Up @@ -69,13 +69,13 @@ impl SerializableToArray for KeyFragProof {
.concat(self.receiving_key_signed.to_array())
}

fn from_array(arr: &GenericArray<u8, Self::Size>) -> Option<Self> {
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Result<Self, DeserializationError> {
let (commitment, rest) = CurvePoint::take(*arr)?;
let (signature_for_proxy, rest) = Signature::take(rest)?;
let (signature_for_receiver, rest) = Signature::take(rest)?;
let (delegating_key_signed, rest) = bool::take(rest)?;
let receiving_key_signed = bool::take_last(rest)?;
Some(Self {
Ok(Self {
commitment,
signature_for_proxy,
signature_for_receiver,
Expand Down Expand Up @@ -161,13 +161,13 @@ impl SerializableToArray for KeyFrag {
.concat(self.proof.to_array())
}

fn from_array(arr: &GenericArray<u8, Self::Size>) -> Option<Self> {
fn from_array(arr: &GenericArray<u8, Self::Size>) -> Result<Self, DeserializationError> {
let params = Parameters::new();
let (id, rest) = KeyFragID::take(*arr)?;
let (key, rest) = CurveScalar::take(rest)?;
let (precursor, rest) = CurvePoint::take(rest)?;
let proof = KeyFragProof::take_last(rest)?;
Some(Self {
Ok(Self {
params,
id,
key,
Expand Down
Loading

0 comments on commit a9808ca

Please sign in to comment.