1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
// -*- mode: rust; -*-
//
// This file is part of schnorrkel.
// Copyright (c) 2019 Web 3 Foundation
// See LICENSE for licensing information.
//
// Authors:
// - Jeff Burdges <jeff@web3.foundation>
//! ### Ristretto point tooling
//!
//! We provide a `RistrettoBoth` type that contains both an uncompressed
//! `RistrettoPoint` along side its matching `CompressedRistretto`,
//! which helps several protocols avoid duplicate ristretto compressions
//! and/or decompressions.
// We're discussing including some variant in curve25519-dalek directly in
// https://github.com/dalek-cryptography/curve25519-dalek/pull/220
use core::fmt::{Debug};
use curve25519_dalek::ristretto::{CompressedRistretto,RistrettoPoint};
// use curve25519_dalek::scalar::Scalar;
use crate::errors::{SignatureError,SignatureResult};
/// Compressed Ristretto point length
pub const RISTRETTO_POINT_LENGTH: usize = 32;
/// A `RistrettoBoth` contains both an uncompressed `RistrettoPoint`
/// as well as the corresponding `CompressedRistretto`. It provides
/// a convenient middle ground for protocols that both hash compressed
/// points to derive scalars for use with uncompressed points.
#[derive(Copy, Clone, Default, Eq)] // PartialEq optimnized below
pub struct RistrettoBoth {
compressed: CompressedRistretto,
point: RistrettoPoint,
}
impl Debug for RistrettoBoth {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
write!(f, "RistrettoPoint( {:?} )", self.compressed)
}
}
/*
#[inline(always)]
fn zeroize_hack<Z: Default>(z: &mut Z) {
use core::{ptr, sync::atomic};
unsafe { ptr::write_volatile(z, Z::default()); }
atomic::compiler_fence(atomic::Ordering::SeqCst);
}
impl ::zeroize::Zeroize for RistrettoBoth {
fn zeroize(&mut self) {
zeroize_hack(&mut self.compressed);
zeroize_hack(&mut self.point);
}
}
*/
impl RistrettoBoth {
const DESCRIPTION : &'static str = "A ristretto point represented as a 32-byte compressed point";
// I dislike getter methods, and prefer direct field access, but doing
// getters here permits the fields being private, and gives us faster
// equality comparisons.
/// Access the compressed Ristretto form
pub fn as_compressed(&self) -> &CompressedRistretto { &self.compressed }
/// Extract the compressed Ristretto form
pub fn into_compressed(self) -> CompressedRistretto { self.compressed }
/// Access the point form
pub fn as_point(&self) -> &RistrettoPoint { &self.point }
/// Extract the point form
pub fn into_point(self) -> RistrettoPoint { self.point }
/// Decompress into the `RistrettoBoth` format that also retains the
/// compressed form.
pub fn from_compressed(compressed: CompressedRistretto) -> SignatureResult<RistrettoBoth> {
Ok(RistrettoBoth {
point: compressed.decompress().ok_or(SignatureError::PointDecompressionError) ?,
compressed,
})
}
/// Compress into the `RistrettoBoth` format that also retains the
/// uncompressed form.
pub fn from_point(point: RistrettoPoint) -> RistrettoBoth {
RistrettoBoth {
compressed: point.compress(),
point,
}
}
/// Convert this public key to a byte array.
#[inline]
pub fn to_bytes(&self) -> [u8; RISTRETTO_POINT_LENGTH] {
self.compressed.to_bytes()
}
/// Construct a `RistrettoBoth` from a slice of bytes.
///
/// # Example
///
/// ```
/// use schnorrkel::points::RistrettoBoth;
/// use schnorrkel::PUBLIC_KEY_LENGTH;
/// use schnorrkel::SignatureError;
///
/// # fn doctest() -> Result<RistrettoBoth, SignatureError> {
/// let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [
/// 215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58,
/// 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26];
///
/// let public_key = RistrettoBoth::from_bytes(&public_key_bytes)?;
/// #
/// # Ok(public_key)
/// # }
/// #
/// # fn main() {
/// # doctest();
/// # }
/// ```
///
/// # Returns
///
/// A `Result` whose okay value is an EdDSA `RistrettoBoth` or whose error value
/// is an `SignatureError` describing the error that occurred.
#[inline]
pub fn from_bytes(bytes: &[u8]) -> SignatureResult<RistrettoBoth> {
RistrettoBoth::from_bytes_ser("RistrettoPoint",RistrettoBoth::DESCRIPTION,bytes)
}
/// Variant of `RistrettoBoth::from_bytes` that propogates more informative errors.
#[inline]
pub fn from_bytes_ser(name: &'static str, description: &'static str, bytes: &[u8]) -> SignatureResult<RistrettoBoth> {
if bytes.len() != RISTRETTO_POINT_LENGTH {
return Err(SignatureError::BytesLengthError{
name, description, length: RISTRETTO_POINT_LENGTH,
});
}
let mut compressed = CompressedRistretto([0u8; RISTRETTO_POINT_LENGTH]);
compressed.0.copy_from_slice(&bytes[..32]);
RistrettoBoth::from_compressed(compressed)
}
}
serde_boilerplate!(RistrettoBoth);
/// We hide fields largely so that only compairing the compressed forms works.
impl PartialEq<Self> for RistrettoBoth {
fn eq(&self, other: &Self) -> bool {
let r = self.compressed.eq(&other.compressed);
debug_assert_eq!(r, self.point.eq(&other.point));
r
}
// fn ne(&self, other: &Rhs) -> bool {
// self.compressed.0.ne(&other.compressed.0)
// }
}
// impl Eq for RistrettoBoth {}
impl PartialOrd<RistrettoBoth> for RistrettoBoth {
fn partial_cmp(&self, other: &RistrettoBoth) -> Option<::core::cmp::Ordering> {
self.compressed.0.partial_cmp(&other.compressed.0)
}
// fn lt(&self, other: &Rhs) -> bool {
// self.compressed.0.lt(&other.compressed.0)
// }
// fn le(&self, other: &Rhs) -> bool {
// self.compressed.0.le(&other.compressed.0)
// }
// fn gt(&self, other: &Rhs) -> bool {
// self.compressed.0.gt(&other.compressed.0)
// }
// fn ge(&self, other: &Rhs) -> bool {
// self.compressed.0.ge(&other.compressed.0)
// }
}
impl Ord for RistrettoBoth {
fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
self.compressed.0.cmp(&other.compressed.0)
}
// fn max(self, other: Self) -> Self {
// self.compressed.0.max(other.compressed.0)
// }
// fn min(self, other: Self) -> Self {
// self.compressed.0.min(other.compressed.0)
// }
}
impl ::core::hash::Hash for RistrettoBoth {
fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
self.compressed.0.hash(state);
}
}