#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![doc = include_str!("../README.md")] #![cfg_attr(not(feature = "std"), no_std)] #[cfg(feature = "alloc")] #[allow(unused_imports)] use std_shims::prelude::*; #[cfg(feature = "alloc")] use std_shims::io::{self, Read}; use sha2::{ digest::array::{typenum::U33, Array}, Sha512, }; use k256::elliptic_curve::{ subtle::{Choice, ConstantTimeEq, ConditionallySelectable}, zeroize::Zeroize, group::{ff::PrimeField, Group}, sec1::Tag, }; prime_field::odd_prime_field!( Scalar, "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", "03", true ); pub use k256::Scalar as FieldElement; use short_weierstrass::{ShortWeierstrass, Affine, Projective}; #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct Secq256k1; impl Zeroize for Secq256k1 { fn zeroize(&mut self) {} } impl ShortWeierstrass for Secq256k1 { type FieldElement = FieldElement; const A: FieldElement = FieldElement::ZERO; const B: FieldElement = { let two = FieldElement::ONE.add(&FieldElement::ONE); let four = two.add(&two); let six = four.add(&two); six.add(&FieldElement::ONE) }; const PRIME_ORDER: bool = true; const GENERATOR: Affine = Affine::from_xy_unchecked(FieldElement::ONE, { let y_be = hex_literal::hex!("0c7c97045a2074634909abdf82c9bd0248916189041f2af0c1b800d1ffc278c0"); let mut res = FieldElement::ZERO; let mut i = 0; while i < 32 { let mut j = 0; while j < 8 { // Shift over the existing result res = res.add(&res); // Add this bit, if set if ((y_be[i] >> (8 - 1 - j)) & 1) == 1 { res = res.add(&FieldElement::ONE); } j += 1; } i += 1; } res }); type Scalar = Scalar; type Repr = Array; /// Use the SEC1-encoded identity point, which happens to be all zeroes const IDENTITY: Self::Repr = Array([0; 33]); fn encode_compressed(x: Self::FieldElement, odd_y: Choice) -> Self::Repr { let mut res = Array([0; 33]); res[0] = <_>::conditional_select(&(Tag::CompressedEvenY as u8), &(Tag::CompressedOddY as u8), odd_y); { let res: &mut [u8] = res.as_mut(); res[1 ..].copy_from_slice(x.to_repr().as_ref()); } res } fn decode_compressed(bytes: &Self::Repr) -> (::Repr, Choice) { // Parse out if `y` is odd let odd_y = bytes[0].ct_eq(&(Tag::CompressedOddY as u8)); // Check if the tag was malleated let expected_tag = <_>::conditional_select(&(Tag::CompressedEvenY as u8), &(Tag::CompressedOddY as u8), odd_y); let invalid = !bytes[0].ct_eq(&expected_tag); // Copy the alleged `x` coordinate, overwriting with `0xffffff...` if the sign byte was // malleated (causing the `x` coordinate to be invalid) let mut x = ::Repr::default(); { let x: &mut [u8] = x.as_mut(); for i in 0 .. 32 { x[i] = <_>::conditional_select(&bytes[1 + i], &u8::MAX, invalid); } } (x, odd_y) } // No points have a torsion element as this a prime-order curve fn has_torsion_element(_point: Projective) -> Choice { 0.into() } } pub type Point = Projective; impl ciphersuite::Ciphersuite for Secq256k1 { type F = Scalar; type G = Point; type H = Sha512; const ID: &'static [u8] = b"secq256k1"; fn generator() -> Self::G { Point::generator() } // We override the provided impl, which compares against the reserialization, because // we already require canonicity #[cfg(feature = "alloc")] #[allow(non_snake_case)] fn read_G(reader: &mut R) -> io::Result { use ciphersuite::group::GroupEncoding; let mut encoding = ::Repr::default(); reader.read_exact(encoding.as_mut())?; let point = Option::::from(Self::G::from_bytes(&encoding)) .ok_or_else(|| io::Error::other("invalid point"))?; Ok(point) } } #[cfg(feature = "alloc")] impl generalized_bulletproofs_ec_gadgets::DiscreteLogParameter for Secq256k1 { type ScalarBits = sha2::digest::array::typenum::U<{ Scalar::NUM_BITS as usize }>; } #[test] fn test_curve() { ff_group_tests::group::test_prime_group_bits::<_, Point>(&mut rand_core::OsRng); } #[test] fn generator() { use ciphersuite::group::GroupEncoding; assert_eq!( Point::generator(), Point::from_bytes(&Array(hex_literal::hex!( "020000000000000000000000000000000000000000000000000000000000000001" ))) .unwrap() ); } #[test] fn zero_x_is_off_curve() { assert!(bool::from(Affine::::decompress(FieldElement::ZERO, 1.into()).is_none())); } // Checks random won't infinitely loop #[test] fn random() { Point::random(&mut rand_core::OsRng); }