Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,13 @@ Point or scalar not meeting above requirements cannot be constructed (in safe Ru
always enforced. E.g. if you're deserializing a sequence of bytes that represents an invalid point,
deserialization will result into error.

### `SecretScalar<E>`
### `SecretScalar<E>` and `SecretPoint<E>`

Sometimes your scalar represents some sensitive value like secret key, and you want to keep it safer.
`SecretScalar<E>` is in-place replacement of `Scalar<E>` that enforces additional security by storing
the scalar value on the heap, and erasing it on drop. Its advantage is that it doesn't leave any trace
in memory dump after it's dropped (which is not guaranteed by regular `Scalar<E>`).
in memory dump after it's dropped (which is not guaranteed by regular `Scalar<E>`). `SecretPoint<E>`
is a similar structure, except it acts as a replacement for `Point<E>`.

But keep in mind that we can't control the OS which could potentially load RAM page containing sensitive value
to the swap disk (i.e. on your HDD/SSD) if you're running low on memory. Or it could do any other fancy stuff.
Expand Down
7 changes: 7 additions & 0 deletions generic-ec/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## v0.5.1
* Add `SecretPoint`, `EncodedSecretPoint`, `EncodedSecretScalar`; add more
methods to `SecretScalar` [#92]
* `EncodedPoint` and `EncodedScalar` are now `Zeroize` [#92]

[#92]: https://github.com/LFDT-Lockness/generic-ec/pull/92

## v0.5.0
* Hash to scalar: guarantee reproducibility on all platforms [#55]
* Use reproducible & constant-time algorithm for random scalar
Expand Down
2 changes: 1 addition & 1 deletion generic-ec/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "generic-ec"
version = "0.5.0"
version = "0.5.1"
edition = "2021"
license = "MIT OR Apache-2.0"
repository = "https://github.com/LFDT-Lockness/generic-ec"
Expand Down
80 changes: 61 additions & 19 deletions generic-ec/src/arithmetic.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};

use crate::{Curve, Generator, NonZero, Point, Scalar, SecretScalar};
use crate::{Curve, Generator, NonZero, Point, Scalar, SecretPoint, SecretScalar};

mod laws {
use crate::{
Expand Down Expand Up @@ -38,8 +38,11 @@ mod laws {
///
/// Therefore, (2) holds.
#[inline]
pub fn sum_of_points_is_valid_point<E: Curve>(a: &Point<E>, b: &Point<E>) -> Point<E> {
let sum = Additive::add(a.as_raw(), b.as_raw());
pub fn sum_of_points_is_valid_point<E: Curve>(
a: impl AsRef<Point<E>>,
b: impl AsRef<Point<E>>,
) -> Point<E> {
let sum = Additive::add(a.as_ref().as_raw(), b.as_ref().as_raw());
// Correctness: refer to doc comment of the function
Point::from_raw_unchecked(sum)
}
Expand All @@ -48,8 +51,11 @@ mod laws {
///
/// Please, refer to [`sum_of_points_is_valid_point`], as the proof is pretty much the same.
#[inline]
pub fn sub_of_points_is_valid_point<E: Curve>(a: &Point<E>, b: &Point<E>) -> Point<E> {
let result = Additive::sub(a.as_raw(), b.as_raw());
pub fn sub_of_points_is_valid_point<E: Curve>(
a: impl AsRef<Point<E>>,
b: impl AsRef<Point<E>>,
) -> Point<E> {
let result = Additive::sub(a.as_ref().as_raw(), b.as_ref().as_raw());
// Correctness: refer to doc comment of the function
Point::from_raw_unchecked(result)
}
Expand All @@ -67,33 +73,33 @@ mod laws {
/// If $A$ is valid `Point<E>`, then $A + G$ is valid `Point<E>`
#[inline]
pub fn sum_of_point_and_generator_is_valid_point<E: Curve>(
a: &Point<E>,
a: impl AsRef<Point<E>>,
g: &Generator<E>,
) -> Point<E> {
sum_of_points_is_valid_point(a, &g.to_point())
sum_of_points_is_valid_point(a.as_ref(), g.to_point())
}
/// If $A$ is valid `Point<E>`, then $G + A$ is valid `Point<E>`
#[inline]
pub fn sum_of_generator_and_point_is_valid_point<E: Curve>(
g: &Generator<E>,
a: &Point<E>,
a: impl AsRef<Point<E>>,
) -> Point<E> {
sum_of_points_is_valid_point(&g.to_point(), a)
sum_of_points_is_valid_point(g.to_point(), a.as_ref())
}

/// If $A$ is valid `Point<E>`, then $A - G$ is valid `Point<E>`
pub fn sub_of_point_and_generator_is_valid_point<E: Curve>(
a: &Point<E>,
a: impl AsRef<Point<E>>,
g: &Generator<E>,
) -> Point<E> {
sub_of_points_is_valid_point(a, &g.to_point())
sub_of_points_is_valid_point(a.as_ref(), g.to_point())
}
/// If $A$ is valid `Point<E>`, then $G - A$ is valid `Point<E>`
pub fn sub_of_generator_and_point_is_valid_point<E: Curve>(
g: &Generator<E>,
a: &Point<E>,
a: impl AsRef<Point<E>>,
) -> Point<E> {
sub_of_points_is_valid_point(&g.to_point(), a)
sub_of_points_is_valid_point(g.to_point(), a.as_ref())
}

/// If $A$ is a valid `Point<E>`, then $-A$ is a valid `Point<E>`
Expand Down Expand Up @@ -135,20 +141,20 @@ mod laws {
#[inline]
pub fn mul_of_scalar_at_point_is_valid_point<E: Curve>(
n: impl AsRef<Scalar<E>>,
a: &Point<E>,
a: impl AsRef<Point<E>>,
) -> Point<E> {
let prod = Multiplicative::mul(n.as_ref().as_raw(), a.as_raw());
let prod = Multiplicative::mul(n.as_ref().as_raw(), a.as_ref().as_raw());
// Correctness: refer to doc comment of the function
Point::from_raw_unchecked(prod)
}

/// Same as [`mul_of_scalar_at_point_is_valid_point`] but flipped arguments
#[inline]
pub fn mul_of_point_at_scalar_is_valid_point<E: Curve>(
a: &Point<E>,
a: impl AsRef<Point<E>>,
b: impl AsRef<Scalar<E>>,
) -> Point<E> {
mul_of_scalar_at_point_is_valid_point(b, a)
mul_of_scalar_at_point_is_valid_point(b, a.as_ref())
}

/// If $n$ is valid `Scalar<E>`, then $n \G$ is valid `Point<E>`
Expand Down Expand Up @@ -186,7 +192,7 @@ mod laws {
#[inline]
pub fn mul_of_nonzero_scalar_at_nonzero_point_is_valid_nonzero_point<E: Curve>(
n: &(impl AsRef<Scalar<E>> + AlwaysNonZero),
a: &NonZero<Point<E>>,
a: &(impl AsRef<Point<E>> + AlwaysNonZero),
) -> NonZero<Point<E>> {
let prod = mul_of_scalar_at_point_is_valid_point(n, a);
// Correctness: refer to doc comment of the function
Expand All @@ -196,7 +202,7 @@ mod laws {
/// Same as [`mul_of_nonzero_scalar_at_nonzero_point_is_valid_nonzero_point`] but flipped arguments
#[inline]
pub fn mul_of_nonzero_point_at_nonzero_scalar_is_valid_nonzero_point<E: Curve>(
a: &NonZero<Point<E>>,
a: &(impl AsRef<Point<E>> + AlwaysNonZero),
n: &(impl AsRef<Scalar<E>> + AlwaysNonZero),
) -> NonZero<Point<E>> {
mul_of_nonzero_scalar_at_nonzero_point_is_valid_nonzero_point(n, a)
Expand Down Expand Up @@ -377,19 +383,35 @@ macro_rules! impl_op_assign {
// Point <> Point, Point <> Scalar, Scalar <> Scalar arithmetic ops
impl_binary_ops! {
Add (Point<E>, add, Point<E> = Point<E>) laws::sum_of_points_is_valid_point,
Add (Point<E>, add, SecretPoint<E> = Point<E>) laws::sum_of_points_is_valid_point,
Add (SecretPoint<E>, add, Point<E> = Point<E>) laws::sum_of_points_is_valid_point,

Sub (Point<E>, sub, Point<E> = Point<E>) laws::sub_of_points_is_valid_point,
Sub (Point<E>, sub, SecretPoint<E> = Point<E>) laws::sub_of_points_is_valid_point,
Sub (SecretPoint<E>, sub, Point<E> = Point<E>) laws::sub_of_points_is_valid_point,

Add (Point<E>, add, Generator<E> = Point<E>) laws::sum_of_point_and_generator_is_valid_point,
Add (Generator<E>, add, Point<E> = Point<E>) laws::sum_of_generator_and_point_is_valid_point,
Sub (Point<E>, sub, Generator<E> = Point<E>) laws::sub_of_point_and_generator_is_valid_point,
Sub (Generator<E>, sub, Point<E> = Point<E>) laws::sub_of_generator_and_point_is_valid_point,

Add (SecretPoint<E>, add, Generator<E> = Point<E>)
laws::sum_of_point_and_generator_is_valid_point,
Add (Generator<E>, add, SecretPoint<E> = Point<E>)
laws::sum_of_generator_and_point_is_valid_point,
Sub (SecretPoint<E>, sub, Generator<E> = Point<E>)
laws::sub_of_point_and_generator_is_valid_point,
Sub (Generator<E>, sub, SecretPoint<E> = Point<E>)
laws::sub_of_generator_and_point_is_valid_point,

Add (Scalar<E>, add, Scalar<E> = Scalar<E>) scalar::add,
Sub (Scalar<E>, sub, Scalar<E> = Scalar<E>) scalar::sub,
Mul (Scalar<E>, mul, Scalar<E> = Scalar<E>) scalar::mul,

Mul (Point<E>, mul, Scalar<E> = Point<E>) laws::mul_of_point_at_scalar_is_valid_point,
Mul (SecretPoint<E>, mul, Scalar<E> = Point<E>) laws::mul_of_point_at_scalar_is_valid_point,
Mul (Scalar<E>, mul, Point<E> = Point<E>) laws::mul_of_scalar_at_point_is_valid_point,
Mul (Scalar<E>, mul, SecretPoint<E> = Point<E>) laws::mul_of_scalar_at_point_is_valid_point,
Mul (Generator<E>, mul, Scalar<E> = Point<E>) laws::mul_of_generator_at_scalar_is_valid_point,
Mul (Scalar<E>, mul, Generator<E> = Point<E>) laws::mul_of_scalar_at_generator_is_valid_point,
}
Expand Down Expand Up @@ -443,6 +465,9 @@ impl_binary_ops! {
Mul (Generator<E>, mul, NonZero<Scalar<E>> = NonZero<Point<E>>) laws::mul_of_generator_at_nonzero_scalar_is_valid_nonzero_point,
Mul (NonZero<Scalar<E>>, mul, Generator<E> = NonZero<Point<E>>) laws::mul_of_nonzero_scalar_at_generator_is_valid_nonzero_point,

Mul (NonZero<SecretPoint<E>>, mul, NonZero<Scalar<E>> = NonZero<Point<E>>) laws::mul_of_nonzero_point_at_nonzero_scalar_is_valid_nonzero_point,
Mul (NonZero<Scalar<E>>, mul, NonZero<SecretPoint<E>> = NonZero<Point<E>>) laws::mul_of_nonzero_scalar_at_nonzero_point_is_valid_nonzero_point,

Mul (NonZero<Point<E>>, mul, NonZero<SecretScalar<E>> = NonZero<Point<E>>) laws::mul_of_nonzero_point_at_nonzero_scalar_is_valid_nonzero_point,
Mul (NonZero<SecretScalar<E>>, mul, NonZero<Point<E>> = NonZero<Point<E>>) laws::mul_of_nonzero_scalar_at_nonzero_point_is_valid_nonzero_point,
Mul (Generator<E>, mul, NonZero<SecretScalar<E>> = NonZero<Point<E>>) laws::mul_of_generator_at_nonzero_scalar_is_valid_nonzero_point,
Expand All @@ -455,6 +480,14 @@ impl_nonzero_ops! {
Add (Point<E>, add, Point<E> = Point<E>) laws::sum_of_points_is_valid_point,
Sub (Point<E>, sub, Point<E> = Point<E>) laws::sub_of_points_is_valid_point,

Add (Point<E>, add, SecretPoint<E> = Point<E>) laws::sum_of_points_is_valid_point,
Add (SecretPoint<E>, add, Point<E> = Point<E>) laws::sum_of_points_is_valid_point,
Sub (Point<E>, sub, SecretPoint<E> = Point<E>) laws::sub_of_points_is_valid_point,
Sub (SecretPoint<E>, sub, Point<E> = Point<E>) laws::sub_of_points_is_valid_point,

Add (SecretPoint<E>, add, SecretPoint<E> = Point<E>) laws::sum_of_points_is_valid_point,
Sub (SecretPoint<E>, sub, SecretPoint<E> = Point<E>) laws::sub_of_points_is_valid_point,

Add (Scalar<E>, add, Scalar<E> = Scalar<E>) scalar::add,
Sub (Scalar<E>, sub, Scalar<E> = Scalar<E>) scalar::sub,

Expand All @@ -479,6 +512,11 @@ impl_binary_ops! {
Mul (NonZero<Scalar<E>>, mul, Point<E> = Point<E>) laws::mul_of_scalar_at_point_is_valid_point,
Mul (NonZero<Point<E>>, mul, Scalar<E> = Point<E>) laws::mul_of_point_at_scalar_is_valid_point,
Mul (Scalar<E>, mul, NonZero<Point<E>> = Point<E>) laws::mul_of_scalar_at_point_is_valid_point,

Mul (SecretPoint<E>, mul, NonZero<Scalar<E>> = Point<E>) laws::mul_of_point_at_scalar_is_valid_point,
Mul (NonZero<Scalar<E>>, mul, SecretPoint<E> = Point<E>) laws::mul_of_scalar_at_point_is_valid_point,
Mul (NonZero<SecretPoint<E>>, mul, Scalar<E> = Point<E>) laws::mul_of_point_at_scalar_is_valid_point,
Mul (Scalar<E>, mul, NonZero<SecretPoint<E>> = Point<E>) laws::mul_of_scalar_at_point_is_valid_point,
}

// -Point, -Scalar, -NonZero<Point>, -NonZero<Scalar>
Expand All @@ -493,10 +531,14 @@ impl_unary_ops! {
impl_op_assign! {
Point<E>, AddAssign, Point<E>, add_assign, +,
Point<E>, AddAssign, NonZero<Point<E>>, add_assign, +,
Point<E>, AddAssign, SecretPoint<E>, add_assign, +,
Point<E>, AddAssign, NonZero<SecretPoint<E>>, add_assign, +,
Point<E>, AddAssign, Generator<E>, add_assign, +,

Point<E>, SubAssign, Point<E>, sub_assign, -,
Point<E>, SubAssign, NonZero<Point<E>>, sub_assign, -,
Point<E>, SubAssign, SecretPoint<E>, sub_assign, -,
Point<E>, SubAssign, NonZero<SecretPoint<E>>, sub_assign, -,
Point<E>, SubAssign, Generator<E>, sub_assign, -,

Point<E>, MulAssign, Scalar<E>, mul_assign, *,
Expand Down
129 changes: 128 additions & 1 deletion generic-ec/src/encoded.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use core::{fmt, ops};

use crate::{as_raw::AsRaw, core::ByteArray, Curve};
use subtle::{Choice, ConstantTimeEq};
use zeroize::Zeroize;

use crate::{as_raw::AsRaw, core::ByteArray, secret, Curve};

/// Bytes representation of an elliptic point
#[derive(Zeroize)]
pub struct EncodedPoint<E: Curve>(EncodedPointInner<E>);

impl<E: Curve> EncodedPoint<E> {
Expand All @@ -29,6 +33,14 @@ impl<E: Curve> Clone for EncodedPoint<E> {
}
}

impl<E: Curve> Default for EncodedPoint<E> {
fn default() -> Self {
Self(EncodedPointInner::Uncompressed(
E::UncompressedPointArray::zeroes(),
))
}
}

impl<E: Curve> PartialEq for EncodedPoint<E> {
fn eq(&self, other: &Self) -> bool {
self.as_bytes() == other.as_bytes()
Expand Down Expand Up @@ -67,6 +79,24 @@ impl<E: Curve> AsRef<[u8]> for EncodedPoint<E> {
}
}

impl<E: Curve> AsMut<[u8]> for EncodedPoint<E> {
fn as_mut(&mut self) -> &mut [u8] {
match &mut self.0 {
EncodedPointInner::Compressed(a) => a.as_mut(),
EncodedPointInner::Uncompressed(a) => a.as_mut(),
}
}
}

impl<E: Curve> Zeroize for EncodedPointInner<E> {
fn zeroize(&mut self) {
match self {
EncodedPointInner::Compressed(a) => a.as_mut().zeroize(),
EncodedPointInner::Uncompressed(a) => a.as_mut().zeroize(),
}
}
}

/// Bytes representation of a scalar (either in big-endian or in little-endian)
#[derive(Clone)]
pub struct EncodedScalar<E: Curve>(E::ScalarArray);
Expand Down Expand Up @@ -133,3 +163,100 @@ impl<E: Curve> AsRaw for EncodedScalar<E> {
&self.0
}
}

impl<E: Curve> Zeroize for EncodedScalar<E> {
fn zeroize(&mut self) {
self.as_mut().zeroize()
}
}

/// Bytes representation of a secret elliptic point. See [`SecretPoint`] for
/// more information
///
/// This representation is automatically zeroed on drop, and doesn't implement
/// some vartime methods. You can still access the underlying bytes by calling
/// `.as_ref()`. This bypasses all the secrecy guarantees given by this struct.
///
/// [`SecretPoint`]: crate::SecretPoint
#[derive(Clone, Default)]
pub struct EncodedSecretPoint<E: Curve>(secret::Secret<EncodedPoint<E>>);

impl<E: Curve> EncodedSecretPoint<E> {
/// Wrap a non-secret representation
#[inline(always)]
pub fn new(mut point: EncodedPoint<E>) -> Self {
Self(secret::new(&mut point))
}

/// Obtain the reference to the encoded bytes. This bypasses all the secrecy
/// guarantees, so be careful when handling them; ideally this reference should not be
/// held for longer than one expression
pub fn as_nonsecret_bytes(&self) -> &[u8] {
self.0.as_bytes()
}

/// Obtain the reference to the encoded point, which you can use as bytes
/// when necessary. This bypasses all the secrecy guarantees, so be careful
/// when handling the resulting value; ideally this reference should not be
/// held for longer than one expression
pub fn as_nonsecret(&self) -> &EncodedPoint<E> {
secret::inner_ref(&self.0)
}
}

impl<E: Curve> fmt::Debug for EncodedSecretPoint<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("SecretEncodedPoint")
}
}

impl<E: Curve> ConstantTimeEq for EncodedSecretPoint<E> {
fn ct_eq(&self, other: &Self) -> Choice {
self.0.ct_eq(&other.0)
}
}

/// Bytes representation of a secret elliptic scalar. See [`SecretScalar`] for
/// more information
///
/// This representation is automatically zeroed on drop, and doesn't implement
/// some vartime methods. You can still access the underlying bytes by calling
/// `.as_ref()`. This bypasses all the secrecy guarantees given by this struct.
///
/// [`SecretScalar`]: crate::SecretScalar
#[derive(Clone, Default)]
pub struct EncodedSecretScalar<E: Curve>(secret::Secret<EncodedScalar<E>>);

impl<E: Curve> EncodedSecretScalar<E> {
/// Wrap a non-secret representation
pub fn new(mut scalar: EncodedScalar<E>) -> Self {
Self(secret::new(&mut scalar))
}

/// Obtain the reference to the encoded bytes. This bypasses all the secrecy
/// guarantees, so be careful when handling them; ideally this reference should not be
/// held for longer than one expression
pub fn as_nonsecret_bytes(&self) -> &[u8] {
self.0.as_bytes()
}

/// Obtain the reference to the encoded scalar, which you can use as bytes
/// when necessary. This bypasses all the secrecy guarantees, so be careful
/// when handling the resulting value; ideally this reference should not be
/// held for longer than one expression
pub fn as_nonsecret(&self) -> &EncodedScalar<E> {
secret::inner_ref(&self.0)
}
}

impl<E: Curve> fmt::Debug for EncodedSecretScalar<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("SecretEncodedScalar")
}
}

impl<E: Curve> ConstantTimeEq for EncodedSecretScalar<E> {
fn ct_eq(&self, other: &Self) -> Choice {
self.0.ct_eq(&other.0)
}
}
Loading
Loading