blob: c851e346503014e658f9bf199740a90bd75aba7e [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
use super::super::big::Big;
use super::super::ecp::ECP;
use super::super::ecp2::ECP2;
use super::super::fp::FP;
use super::super::fp2::FP2;
use super::super::hash_to_curve::*;
use super::super::pair;
use super::super::rom::*;
use super::iso::{iso11_to_ecp, iso3_to_ecp2};
use crate::errors::AmclError;
use crate::hash256::HASH256;
use crate::rand::RAND;
// Key Generation Constants
/// Domain for key generation.
pub const KEY_SALT: &[u8] = b"BLS-SIG-KEYGEN-SALT-";
/// L = ceil((3 * ceil(log2(r))) / 16) = 48.
pub const KEY_GENERATION_L: u8 = 48;
// Length of objects in bytes
/// The required number of bytes for a secret key
pub const SECRET_KEY_BYTES: usize = 32;
/// The required number of bytes for a compressed G1 point
pub const G1_BYTES: usize = MODBYTES;
/// The required number of bytes for a compressed G2 point
pub const G2_BYTES: usize = MODBYTES * 2;
// Serialization flags
const COMPRESION_FLAG: u8 = 0b_1000_0000;
const INFINITY_FLAG: u8 = 0b_0100_0000;
const Y_FLAG: u8 = 0b_0010_0000;
/// KeyGenerate
///
/// Generate a new Secret Key based off Initial Keying Material (IKM) and Key Info (salt).
/// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-2.3
pub(crate) fn key_generate(ikm: &[u8], key_info: &[u8]) -> [u8; SECRET_KEY_BYTES] {
let mut secret_key = Big::new();
let mut salt = KEY_SALT.to_vec();
while secret_key.is_zilch() {
// salt = H(salt)
let mut hash256 = HASH256::new();
hash256.init();
hash256.process_array(&salt);
salt = hash256.hash().to_vec();
// PRK = HKDF-Extract(salt, IKM || I2OSP(0, 1))
let mut prk = Vec::<u8>::with_capacity(1 + ikm.len());
prk.extend_from_slice(ikm);
prk.push(0);
let prk = HASH256::hkdf_extract(&salt, &prk);
// OKM = HKDF-Expand(PRK, key_info || I2OSP(L, 2), L)
let mut info = key_info.to_vec();
info.extend_from_slice(&[0, KEY_GENERATION_L]);
let okm = HASH256::hkdf_extend(&prk, &info, KEY_GENERATION_L);
// SK = OS2IP(OKM) mod r
let r = Big::new_ints(&CURVE_ORDER);
secret_key = Big::from_bytes(&okm);
secret_key.rmod(&r);
}
secret_key_to_bytes(&secret_key)
}
// Converts secret key bytes to a Big
pub fn secret_key_from_bytes(secret_key: &[u8]) -> Result<Big, AmclError> {
if secret_key.len() != SECRET_KEY_BYTES {
return Err(AmclError::InvalidSecretKeySize);
}
// Prepend to MODBYTES in length
let mut secret_key_bytes = [0u8; MODBYTES];
secret_key_bytes[MODBYTES - SECRET_KEY_BYTES..].copy_from_slice(secret_key);
// Ensure secret key is in the range [1, r-1].
let secret_key = Big::from_bytes(&secret_key_bytes);
if secret_key.is_zilch() || secret_key >= Big::new_ints(&CURVE_ORDER) {
return Err(AmclError::InvalidSecretKeyRange);
}
Ok(secret_key)
}
// Converts secret key Big to bytes
pub fn secret_key_to_bytes(secret_key: &Big) -> [u8; SECRET_KEY_BYTES] {
let mut big_bytes = [0u8; MODBYTES];
secret_key.to_bytes(&mut big_bytes);
let mut secret_key_bytes = [0u8; SECRET_KEY_BYTES];
secret_key_bytes.copy_from_slice(&big_bytes[MODBYTES - SECRET_KEY_BYTES..]);
secret_key_bytes
}
// Verifies a G1 point is in subgroup `r`.
pub fn subgroup_check_g1(point: &ECP) -> bool {
let r = Big::new_ints(&CURVE_ORDER);
let check = pair::g1mul(&point, &r);
check.is_infinity()
}
// Verifies a G2 point is in subgroup `r`.
pub fn subgroup_check_g2(point: &ECP2) -> bool {
let r = Big::new_ints(&CURVE_ORDER);
let check = pair::g2mul(&point, &r);
check.is_infinity()
}
// Compare values of two FP2 elements,
// -1 if num1 < num2; 0 if num1 == num2; 1 if num1 > num2
fn zcash_cmp_fp2(num1: &mut FP2, num2: &mut FP2) -> isize {
// First compare FP2.b
let mut result = Big::comp(&num1.getb(), &num2.getb());
// If FP2.b is equal compare FP2.a
if result == 0 {
result = Big::comp(&num1.geta(), &num2.geta());
}
result
}
/// Take a G1 point (x, y) and compress it to a 48 byte array.
///
/// See https://github.com/zkcrypto/pairing/blob/master/src/bls12_381/README.md#serialization
pub fn serialize_g1(g1: &ECP) -> [u8; G1_BYTES] {
// Check point at inifinity
if g1.is_infinity() {
let mut result = [0u8; G1_BYTES];
// Set compressed flag and infinity flag
result[0] = u8::pow(2, 6) + u8::pow(2, 7);
return result;
}
// Convert x-coordinate to bytes
let mut result = [0u8; G1_BYTES];
g1.getx().to_bytes(&mut result);
// Evaluate if y > -y
let mut tmp = g1.clone();
tmp.affine();
let y = tmp.gety();
tmp.neg();
let y_neg = tmp.gety();
// Set flags
if y > y_neg {
result[0] += Y_FLAG;
}
result[0] += COMPRESION_FLAG;
result
}
/// Take a G1 point (x, y) and converti it to a 96 byte array.
///
/// See https://github.com/zkcrypto/pairing/blob/master/src/bls12_381/README.md#serialization
pub fn serialize_uncompressed_g1(g1: &ECP) -> [u8; G1_BYTES * 2] {
// Check point at inifinity
let mut result = [0u8; G1_BYTES * 2];
if g1.is_infinity() {
result[0] = INFINITY_FLAG;
return result;
}
// Convert x-coordinate to bytes
g1.getx().to_bytes(&mut result[..MODBYTES]);
g1.gety().to_bytes(&mut result[MODBYTES..]);
result
}
/// Take a 48 or 96 byte array and convert to a G1 point (x, y)
///
/// See https://github.com/zkcrypto/pairing/blob/master/src/bls12_381/README.md#serialization
pub fn deserialize_g1(g1_bytes: &[u8]) -> Result<ECP, AmclError> {
if g1_bytes.len() == 0 {
return Err(AmclError::InvalidG1Size);
}
if g1_bytes[0] & COMPRESION_FLAG == 0 {
deserialize_uncompressed_g1(g1_bytes)
} else {
deserialize_compressed_g1(g1_bytes)
}
}
// Deserialization of a G1 point from x-coordinate
fn deserialize_compressed_g1(g1_bytes: &[u8]) -> Result<ECP, AmclError> {
// Length must be 48 bytes
if g1_bytes.len() != G1_BYTES {
return Err(AmclError::InvalidG1Size);
}
// Check infinity flag
if g1_bytes[0] & INFINITY_FLAG != 0 {
// Trailing bits should all be 0.
if g1_bytes[0] & 0b_0011_1111 != 0 {
return Err(AmclError::InvalidPoint);
}
for item in g1_bytes.iter().skip(1) {
if *item != 0 {
return Err(AmclError::InvalidPoint);
}
}
return Ok(ECP::new()); // infinity
}
let y_flag: bool = (g1_bytes[0] & Y_FLAG) > 0;
// Zero flags
let mut g1_bytes = g1_bytes.to_owned();
g1_bytes[0] = g1_bytes[0] & 0b_0001_1111;
let x = Big::from_bytes(&g1_bytes);
// Require element less than field modulus
let m = Big::new_ints(&MODULUS);
if x >= m {
return Err(AmclError::InvalidPoint);
}
// Convert to G1 from x-coordinate
let point = ECP::new_big(&x);
if point.is_infinity() {
return Err(AmclError::InvalidPoint);
}
// Confirm y value
let mut point_neg = point.clone();
point_neg.neg();
if (point.gety() > point_neg.gety()) != y_flag {
Ok(point_neg)
} else {
Ok(point)
}
}
// Deserialization of a G1 point from (x, y).
fn deserialize_uncompressed_g1(g1_bytes: &[u8]) -> Result<ECP, AmclError> {
// Length must be 96 bytes
if g1_bytes.len() != G1_BYTES * 2 {
return Err(AmclError::InvalidG1Size);
}
// Check infinity flag
if g1_bytes[0] & INFINITY_FLAG != 0 {
// Trailing bits should all be 0.
if g1_bytes[0] & 0b_0011_1111 != 0 {
return Err(AmclError::InvalidPoint);
}
for item in g1_bytes.iter().skip(1) {
if *item != 0 {
return Err(AmclError::InvalidPoint);
}
}
return Ok(ECP::new()); // infinity
}
// Require y_flag to be zero
if (g1_bytes[0] & Y_FLAG) > 0 {
return Err(AmclError::InvalidYFlag);
}
// Zero flags
let mut g1_bytes = g1_bytes.to_owned();
g1_bytes[0] = g1_bytes[0] & 0b_0001_1111;
let x = Big::from_bytes(&g1_bytes[..MODBYTES]);
let y = Big::from_bytes(&g1_bytes[MODBYTES..]);
// Require elements less than field modulus
let m = Big::new_ints(&MODULUS);
if x >= m || y >= m {
return Err(AmclError::InvalidPoint);
}
// Convert to G1
let point = ECP::new_bigs(&x, &y);
if point.is_infinity() {
return Err(AmclError::InvalidPoint);
}
Ok(point)
}
/// Take a G2 point (x, y) and compress it to a 96 byte array as the x-coordinate.
///
/// See https://github.com/zkcrypto/pairing/blob/master/src/bls12_381/README.md#serialization
pub fn serialize_g2(g2: &ECP2) -> [u8; G2_BYTES] {
// Check point at inifinity
if g2.is_infinity() {
let mut result = [0; G2_BYTES];
result[0] += COMPRESION_FLAG + INFINITY_FLAG;
return result;
}
// Convert x-coordinate to bytes
// Note: Zcash uses (x_im, x_re)
let mut result = [0u8; G2_BYTES];
let x = g2.getx();
x.geta().to_bytes(&mut result[MODBYTES..(MODBYTES * 2)]);
x.getb().to_bytes(&mut result[0..MODBYTES]);
// Check y value
let mut y = g2.gety();
let mut y_neg = y.clone();
y_neg.neg();
// Set flags
if zcash_cmp_fp2(&mut y, &mut y_neg) > 0 {
result[0] += Y_FLAG;
}
result[0] += COMPRESION_FLAG;
result
}
/// Take a G2 point (x, y) and convert it to a 192 byte array as (x, y).
///
/// See https://github.com/zkcrypto/pairing/blob/master/src/bls12_381/README.md#serialization
pub fn serialize_uncompressed_g2(g2: &ECP2) -> [u8; G2_BYTES * 2] {
let mut result = [0; G2_BYTES * 2];
// Check point at inifinity
if g2.is_infinity() {
result[0] += INFINITY_FLAG;
return result;
}
// Convert to bytes
// Note: Zcash uses (x_im, x_re), (y_im, y_re)
let x = g2.getx();
x.getb().to_bytes(&mut result[0..MODBYTES]);
x.geta().to_bytes(&mut result[MODBYTES..(MODBYTES * 2)]);
let x = g2.gety();
x.getb()
.to_bytes(&mut result[(MODBYTES * 2)..(MODBYTES * 3)]);
x.geta().to_bytes(&mut result[(MODBYTES * 3)..]);
result
}
/// Take a 96 or 192 byte array and convert to G2 point (x, y)
///
/// See https://github.com/zkcrypto/pairing/blob/master/src/bls12_381/README.md#serialization
pub fn deserialize_g2(g2_bytes: &[u8]) -> Result<ECP2, AmclError> {
if g2_bytes.len() == 0 {
return Err(AmclError::InvalidG2Size);
}
if g2_bytes[0] & COMPRESION_FLAG == 0 {
deserialize_uncompressed_g2(g2_bytes)
} else {
deserialize_compressed_g2(g2_bytes)
}
}
// Decompress a G2 point from x-coordinate
fn deserialize_compressed_g2(g2_bytes: &[u8]) -> Result<ECP2, AmclError> {
if g2_bytes.len() != G2_BYTES {
return Err(AmclError::InvalidG2Size);
}
// Check infinity flag
if g2_bytes[0] & INFINITY_FLAG != 0 {
// Trailing bits should all be 0.
if g2_bytes[0] & 0b_0011_1111 != 0 {
return Err(AmclError::InvalidPoint);
}
for item in g2_bytes.iter().skip(1) {
if *item != 0 {
return Err(AmclError::InvalidPoint);
}
}
return Ok(ECP2::new()); // infinity
}
let y_flag: bool = (g2_bytes[0] & Y_FLAG) > 0;
// Zero flags
let mut g2_bytes = g2_bytes.to_owned();
g2_bytes[0] = g2_bytes[0] & 0b_0001_1111;
// Convert from array to FP2
let x_imaginary = Big::from_bytes(&g2_bytes[0..MODBYTES]);
let x_real = Big::from_bytes(&g2_bytes[MODBYTES..]);
// Require elements less than field modulus
let m = Big::new_ints(&MODULUS);
if x_imaginary >= m || x_real >= m {
return Err(AmclError::InvalidPoint);
}
let x = FP2::new_bigs(x_real, x_imaginary);
// Convert to G2 from x-coordinate
let point = ECP2::new_fp2(&x);
if point.is_infinity() {
return Err(AmclError::InvalidPoint);
}
// Confirm y value
let mut point_neg = point.clone();
point_neg.neg();
if (zcash_cmp_fp2(&mut point.gety(), &mut point_neg.gety()) > 0) != y_flag {
Ok(point_neg)
} else {
Ok(point)
}
}
// Decompress a G2 point from (x, y)
fn deserialize_uncompressed_g2(g2_bytes: &[u8]) -> Result<ECP2, AmclError> {
if g2_bytes.len() != G2_BYTES * 2 {
return Err(AmclError::InvalidG2Size);
}
// Check infinity flag
if g2_bytes[0] & INFINITY_FLAG != 0 {
// Trailing bits should all be 0.
if g2_bytes[0] & 0b_0011_1111 != 0 {
return Err(AmclError::InvalidPoint);
}
for item in g2_bytes.iter().skip(1) {
if *item != 0 {
return Err(AmclError::InvalidPoint);
}
}
return Ok(ECP2::new()); // infinity
}
if (g2_bytes[0] & Y_FLAG) > 0 {
return Err(AmclError::InvalidYFlag);
}
// Zero flags
let mut g2_bytes = g2_bytes.to_owned();
g2_bytes[0] = g2_bytes[0] & 0b_0001_1111;
// Convert from array to FP2
let x_imaginary = Big::from_bytes(&g2_bytes[..MODBYTES]);
let x_real = Big::from_bytes(&g2_bytes[MODBYTES..(MODBYTES * 2)]);
let y_imaginary = Big::from_bytes(&g2_bytes[(MODBYTES * 2)..(MODBYTES * 3)]);
let y_real = Big::from_bytes(&g2_bytes[(MODBYTES * 3)..]);
// Require elements less than field modulus
let m = Big::new_ints(&MODULUS);
if x_imaginary >= m || x_real >= m || y_imaginary >= m || y_real >= m {
return Err(AmclError::InvalidPoint);
}
let x = FP2::new_bigs(x_real, x_imaginary);
let y = FP2::new_bigs(y_real, y_imaginary);
// Convert to G2 from x-coordinate
let point = ECP2::new_fp2s(x, y);
if point.is_infinity() {
return Err(AmclError::InvalidPoint);
}
Ok(point)
}
/*************************************************************************************************
* Core BLS Functions when signatures are on G1
*
* https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-2
*************************************************************************************************/
/// Generate key pair - (secret key, public key)
pub(crate) fn key_pair_generate_g1(rng: &mut RAND) -> ([u8; SECRET_KEY_BYTES], [u8; G2_BYTES]) {
// Fill random bytes
let mut ikm = [0u8; SECRET_KEY_BYTES];
for byte in ikm.iter_mut() {
*byte = rng.getbyte();
}
// Generate key pair
let secret_key = key_generate(&ikm, &[]);
let public_key =
secret_key_to_public_key_g1(&secret_key).expect("Valid secret key was generated");
(secret_key, public_key)
}
/// Secret Key To Public Key
///
/// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-2.4
pub(crate) fn secret_key_to_public_key_g1(secret_key: &[u8]) -> Result<[u8; G2_BYTES], AmclError> {
let secret_key = secret_key_from_bytes(secret_key)?;
let g = ECP2::generator();
let public_key = pair::g2mul(&g, &secret_key);
Ok(serialize_g2(&public_key))
}
// CoreSign
//
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-2.7
pub(crate) fn core_sign_g1(
secret_key: &[u8],
msg: &[u8],
dst: &[u8],
) -> Result<[u8; G1_BYTES], AmclError> {
let secret_key = secret_key_from_bytes(secret_key)?;
let hash = hash_to_curve_g1(msg, dst);
let signature = pair::g1mul(&hash, &secret_key);
Ok(serialize_g1(&signature))
}
// CoreVerify
//
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-2.7
pub(crate) fn core_verify_g1(public_key: &[u8], msg: &[u8], signature: &[u8], dst: &[u8]) -> bool {
let public_key = deserialize_g2(public_key);
let signature = deserialize_g1(signature);
if public_key.is_err() || signature.is_err() {
return false;
}
let public_key = public_key.unwrap();
let signature = signature.unwrap();
// Subgroup checks for signature and public key
if !subgroup_check_g1(&signature) || !subgroup_check_g2(&public_key) || public_key.is_infinity() {
return false;
}
// Hash msg and negate generator for pairing
let hash = hash_to_curve_g1(msg, dst);
let mut g = ECP2::generator();
g.neg();
// Pair e(H(msg), pk) * e(signature, -g)
let mut r = pair::initmp();
pair::another(&mut r, &g, &signature);
pair::another(&mut r, &public_key, &hash);
let mut v = pair::miller(&r);
v = pair::fexp(&v);
// True if pairing output is 1
v.is_unity()
}
/// Aggregate
///
/// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-2.8
pub(crate) fn aggregate_g1(points: &[&[u8]]) -> Result<[u8; G1_BYTES], AmclError> {
if points.len() == 0 {
return Err(AmclError::AggregateEmptyPoints);
}
let mut aggregate = deserialize_g1(&points[0])?;
for point in points.iter().skip(1) {
aggregate.add(&deserialize_g1(&point)?);
}
Ok(serialize_g1(&aggregate))
}
// CoreAggregateVerify
//
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-2.9
pub(crate) fn core_aggregate_verify_g1(
public_keys: &[&[u8]],
msgs: &[&[u8]],
signature: &[u8],
dst: &[u8],
) -> bool {
// Preconditions
if public_keys.len() == 0 || public_keys.len() != msgs.len() {
return false;
}
let signature = deserialize_g1(signature);
if signature.is_err() {
return false;
}
let signature = signature.unwrap();
// Subgroup checks for signature
if !subgroup_check_g1(&signature) {
return false;
}
// Pair e(signature, -g)
let mut g = ECP2::generator();
g.neg();
let mut r = pair::initmp();
pair::another(&mut r, &g, &signature);
for (i, public_key) in public_keys.iter().enumerate() {
let public_key = deserialize_g2(public_key);
if public_key.is_err() {
return false;
}
let public_key = public_key.unwrap();
if !subgroup_check_g2(&public_key) || public_key.is_infinity() {
return false;
}
// Pair *= e(pk[i], H(msgs[i]))
let hash = hash_to_curve_g1(msgs[i], dst);
pair::another(&mut r, &public_key, &hash);
}
// True if pairing output is 1
let mut v = pair::miller(&r);
v = pair::fexp(&v);
v.is_unity()
}
/*************************************************************************************************
* Core BLS Functions when signatures are on G2
*
* https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-2
*************************************************************************************************/
/// Generate key pair - (secret key, public key)
pub(crate) fn key_pair_generate_g2(rng: &mut RAND) -> ([u8; SECRET_KEY_BYTES], [u8; G1_BYTES]) {
// Fill random bytes
let mut ikm = [0u8; SECRET_KEY_BYTES];
for byte in ikm.iter_mut() {
*byte = rng.getbyte();
}
// Generate key pair
let secret_key = key_generate(&ikm, &[]);
let public_key =
secret_key_to_public_key_g2(&secret_key).expect("Valid secret key was generated");
(secret_key, public_key)
}
/// Secret Key To Public Key
///
/// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-2.4
pub(crate) fn secret_key_to_public_key_g2(secret_key: &[u8]) -> Result<[u8; G1_BYTES], AmclError> {
let secret_key = secret_key_from_bytes(secret_key)?;
let g = ECP::generator();
let public_key = pair::g1mul(&g, &secret_key);
// Convert to bytes
Ok(serialize_g1(&public_key))
}
// CoreSign
//
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-2.7
pub(crate) fn core_sign_g2(
secret_key: &[u8],
msg: &[u8],
dst: &[u8],
) -> Result<[u8; G2_BYTES], AmclError> {
let secret_key = secret_key_from_bytes(secret_key)?;
let hash = hash_to_curve_g2(msg, dst);
let signature = pair::g2mul(&hash, &secret_key);
Ok(serialize_g2(&signature))
}
// CoreVerify
//
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-2.7
pub(crate) fn core_verify_g2(public_key: &[u8], msg: &[u8], signature: &[u8], dst: &[u8]) -> bool {
let public_key = deserialize_g1(public_key);
let signature = deserialize_g2(signature);
if public_key.is_err() || signature.is_err() {
return false;
}
let public_key = public_key.unwrap();
let signature = signature.unwrap();
// Subgroup checks for signature and public key
if !subgroup_check_g1(&public_key) || public_key.is_infinity() || !subgroup_check_g2(&signature) {
return false;
}
// Hash msg and negate generator for pairing
let hash = hash_to_curve_g2(msg, dst);
let mut g = ECP::generator();
g.neg();
// Pair e(H(msg), pk) * e(signature, -g)
let mut r = pair::initmp();
pair::another(&mut r, &signature, &g);
pair::another(&mut r, &hash, &public_key);
let mut v = pair::miller(&r);
v = pair::fexp(&v);
// True if pairing output is 1
v.is_unity()
}
/// Aggregate
///
/// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-2.8
pub(crate) fn aggregate_g2(points: &[&[u8]]) -> Result<[u8; G2_BYTES], AmclError> {
if points.len() == 0 {
return Err(AmclError::AggregateEmptyPoints);
}
let mut aggregate = deserialize_g2(&points[0])?;
for point in points.iter().skip(1) {
aggregate.add(&deserialize_g2(&point)?);
}
Ok(serialize_g2(&aggregate))
}
// CoreAggregateVerify
//
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-2.9
pub(crate) fn core_aggregate_verify_g2(
public_keys: &[&[u8]],
msgs: &[&[u8]],
signature: &[u8],
dst: &[u8],
) -> bool {
let signature = deserialize_g2(signature);
if signature.is_err() {
return false;
}
let signature = signature.unwrap();
// Preconditions
if public_keys.len() == 0 || public_keys.len() != msgs.len() {
return false;
}
// Subgroup checks for signature
if !subgroup_check_g2(&signature) {
return false;
}
// Pair e(signature, -g)
let mut g = ECP::generator();
g.neg();
let mut r = pair::initmp();
pair::another(&mut r, &signature, &g);
for (i, public_key) in public_keys.iter().enumerate() {
let public_key = deserialize_g1(public_key);
if public_key.is_err() {
return false;
}
let public_key = public_key.unwrap();
// Subgroup check for public key
if !subgroup_check_g1(&public_key) || public_key.is_infinity() {
return false;
}
// Pair *= e(pk[i], H(msgs[i]))
let hash = hash_to_curve_g2(msgs[i], dst);
pair::another(&mut r, &hash, &public_key);
}
// True if pairing output is 1
let mut v = pair::miller(&r);
v = pair::fexp(&v);
v.is_unity()
}
/*************************************************************************************************
* Functions for hashing to curve when signatures are on G1
*************************************************************************************************/
/// Hash to Curve
///
/// Takes a message as input and converts it to a Curve Point
/// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-09#section-3
pub fn hash_to_curve_g1(msg: &[u8], dst: &[u8]) -> ECP {
let u =
hash_to_field_fp(msg, 2, dst).expect("hash to field should not fail for given parameters");
let mut q0 = map_to_curve_g1(u[0].clone());
let q1 = map_to_curve_g1(u[1].clone());
q0.add(&q1);
let p = q0.mul(&Big::new_ints(&H_EFF_G1));
p
}
// Simplified SWU for Pairing-Friendly Curves
//
// Take a field point and map it to a Curve Point.
// SSWU - https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-09#section-6.6.2
// ISO11 - https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-09#appendix-C.2
fn map_to_curve_g1(u: FP) -> ECP {
let (x, y) = simplified_swu_fp(u);
iso11_to_ecp(&x, &y)
}
/*************************************************************************************************
* Functions for hashing to curve when signatures are on G2
*************************************************************************************************/
/// Hash to Curve
///
/// Takes a message as input and converts it to a Curve Point
/// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-09#section-3
pub fn hash_to_curve_g2(msg: &[u8], dst: &[u8]) -> ECP2 {
let u =
hash_to_field_fp2(msg, 2, dst).expect("hash to field should not fail for given parameters");
let mut q0 = map_to_curve_g2(u[0].clone());
let q1 = map_to_curve_g2(u[1].clone());
q0.add(&q1);
q0.clear_cofactor();
q0
}
// Simplified SWU for Pairing-Friendly Curves
//
// Take a field point and map it to a Curve Point.
// SSWU - https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-09#section-6.6.2
// ISO3 - https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-09#appendix-C.3
fn map_to_curve_g2(u: FP2) -> ECP2 {
let (x, y) = simplified_swu_fp2(u);
iso3_to_ecp2(&x, &y)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::*;
const TEST_DST: &[u8] = b"BLS_TEST_";
#[test]
fn test_hash_to_curve_g2() {
const H2C_SUITE_G2: &str = "BLS12381G2_XMD:SHA-256_SSWU_RO_";
// Read hash to curve test vector
let reader = json_reader(H2C_SUITE_G2);
let test_vectors: Bls12381Ro = serde_json::from_reader(reader).unwrap();
// Iterate through each individual case
for case in test_vectors.vectors {
// Execute hash to curve
let u = hash_to_field_fp2(case.msg.as_bytes(), 2, test_vectors.dst.as_bytes()).unwrap();
let q0 = map_to_curve_g2(u[0].clone());
let q1 = map_to_curve_g2(u[1].clone());
let mut r = q0.clone();
r.add(&q1);
let mut p = r.clone();
p.clear_cofactor();
// Verify against hash_to_curve()
let hash_to_curve_p =
hash_to_curve_g2(case.msg.as_bytes(), test_vectors.dst.as_bytes());
assert_eq!(hash_to_curve_p, p);
// Verify hash to curve outputs
// Check u
assert_eq!(case.u.len(), u.len());
for (i, u_str) in case.u.iter().enumerate() {
// Convert case 'u[i]' to FP2
let u_str_parts: Vec<&str> = u_str.split(',').collect();
let a = Big::from_bytes(&hex::decode(&u_str_parts[0].get(2..).unwrap()).unwrap());
let b = Big::from_bytes(&hex::decode(&u_str_parts[1].get(2..).unwrap()).unwrap());
let expected_u_i = FP2::new_bigs(a, b);
// Verify u[i]
assert_eq!(expected_u_i, u[i]);
}
// Check Q0
let x_str_parts: Vec<&str> = case.Q0.x.split(',').collect();
let a = Big::from_bytes(&hex::decode(&x_str_parts[0].get(2..).unwrap()).unwrap());
let b = Big::from_bytes(&hex::decode(&x_str_parts[1].get(2..).unwrap()).unwrap());
let expected_x = FP2::new_bigs(a, b);
let y_str_parts: Vec<&str> = case.Q0.y.split(',').collect();
let a = Big::from_bytes(&hex::decode(&y_str_parts[0].get(2..).unwrap()).unwrap());
let b = Big::from_bytes(&hex::decode(&y_str_parts[1].get(2..).unwrap()).unwrap());
let expected_y = FP2::new_bigs(a, b);
let expected_q0 = ECP2::new_fp2s(expected_x, expected_y);
assert_eq!(expected_q0, q0);
// Check Q1
let x_str_parts: Vec<&str> = case.Q1.x.split(',').collect();
let a = Big::from_bytes(&hex::decode(&x_str_parts[0].get(2..).unwrap()).unwrap());
let b = Big::from_bytes(&hex::decode(&x_str_parts[1].get(2..).unwrap()).unwrap());
let expected_x = FP2::new_bigs(a, b);
let y_str_parts: Vec<&str> = case.Q1.y.split(',').collect();
let a = Big::from_bytes(&hex::decode(&y_str_parts[0].get(2..).unwrap()).unwrap());
let b = Big::from_bytes(&hex::decode(&y_str_parts[1].get(2..).unwrap()).unwrap());
let expected_y = FP2::new_bigs(a, b);
let expected_q1 = ECP2::new_fp2s(expected_x, expected_y);
assert_eq!(expected_q1, q1);
// Check P
let x_str_parts: Vec<&str> = case.P.x.split(',').collect();
let a = Big::from_bytes(&hex::decode(&x_str_parts[0].get(2..).unwrap()).unwrap());
let b = Big::from_bytes(&hex::decode(&x_str_parts[1].get(2..).unwrap()).unwrap());
let expected_x = FP2::new_bigs(a, b);
let y_str_parts: Vec<&str> = case.P.y.split(',').collect();
let a = Big::from_bytes(&hex::decode(&y_str_parts[0].get(2..).unwrap()).unwrap());
let b = Big::from_bytes(&hex::decode(&y_str_parts[1].get(2..).unwrap()).unwrap());
let expected_y = FP2::new_bigs(a, b);
let expected_p = ECP2::new_fp2s(expected_x, expected_y);
assert_eq!(expected_p, p);
}
}
#[test]
fn test_hash_to_curve_g1() {
const H2C_SUITE_G1: &str = "BLS12381G1_XMD:SHA-256_SSWU_RO_";
// Read hash to curve test vector
let reader = json_reader(H2C_SUITE_G1);
let test_vectors: Bls12381Ro = serde_json::from_reader(reader).unwrap();
// Iterate through each individual case
for case in test_vectors.vectors {
// Execute hash to curve
let u = hash_to_field_fp(case.msg.as_bytes(), 2, test_vectors.dst.as_bytes()).unwrap();
let q0 = map_to_curve_g1(u[0].clone());
let q1 = map_to_curve_g1(u[1].clone());
let mut r = q0.clone();
r.add(&q1);
let p = r.mul(&Big::new_ints(&H_EFF_G1));
// Verify against hash_to_curve()
let hash_to_curve_p =
hash_to_curve_g1(case.msg.as_bytes(), test_vectors.dst.as_bytes());
assert_eq!(hash_to_curve_p, p);
// Verify hash to curve outputs
// Check u
assert_eq!(case.u.len(), u.len());
for (i, u_str) in case.u.iter().enumerate() {
// Convert case 'u[i]' to FP
let a = Big::from_bytes(&hex::decode(&u_str.get(2..).unwrap()).unwrap());
let expected_u_i = FP::new_big(a);
// Verify u[i]
assert_eq!(expected_u_i, u[i]);
}
// Check Q0
let a = Big::from_bytes(&hex::decode(&case.Q0.x.get(2..).unwrap()).unwrap());
let expected_x = FP::new_big(a);
let a = Big::from_bytes(&hex::decode(&case.Q0.y.get(2..).unwrap()).unwrap());
let expected_y = FP::new_big(a);
let expected_q0 = ECP::new_fps(expected_x, expected_y);
assert_eq!(expected_q0, q0);
// Check Q1
let a = Big::from_bytes(&hex::decode(&case.Q1.x.get(2..).unwrap()).unwrap());
let expected_x = FP::new_big(a);
let a = Big::from_bytes(&hex::decode(&case.Q1.y.get(2..).unwrap()).unwrap());
let expected_y = FP::new_big(a);
let expected_q1 = ECP::new_fps(expected_x, expected_y);
assert_eq!(expected_q1, q1);
// Check P
let a = Big::from_bytes(&hex::decode(&case.P.x.get(2..).unwrap()).unwrap());
let expected_x = FP::new_big(a);
let a = Big::from_bytes(&hex::decode(&case.P.y.get(2..).unwrap()).unwrap());
let expected_y = FP::new_big(a);
let expected_p = ECP::new_fps(expected_x, expected_y);
assert_eq!(expected_p, p);
}
}
#[test]
fn test_secret_key_from_bytes() {
let bytes = [0u8; 32];
let sk = secret_key_from_bytes(&bytes);
assert_eq!(sk, Err(AmclError::InvalidSecretKeyRange));
let bytes = [255u8; 32];
let sk = secret_key_from_bytes(&bytes);
assert_eq!(sk, Err(AmclError::InvalidSecretKeyRange));
let mut bytes = [0u8; 32];
bytes[31] = 1;
let sk = secret_key_from_bytes(&bytes).unwrap();
assert!(sk.is_unity());
let mut bytes = [255u8; 32];
bytes[0] = 0;
let sk = secret_key_from_bytes(&bytes).unwrap();
let mut sk_check = Big::new_int(1);
sk_check.shl(31 * 8);
sk_check.dec(1);
sk_check.norm();
assert_eq!(sk, sk_check);
}
#[test]
fn test_secret_key_generation() {
let ikm = [1u8; 32];
let sk = key_generate(&ikm, &[]);
let sk_big = secret_key_from_bytes(&sk).unwrap();
let sk_round_trip = secret_key_to_bytes(&sk_big);
assert_eq!(sk, sk_round_trip);
}
#[test]
fn test_key_pair_generation_g1() {
let mut rng = create_rng();
let (sk, pk) = key_pair_generate_g1(&mut rng);
let sk_big = secret_key_from_bytes(&sk).unwrap();
let pk_ecp = deserialize_g2(&pk).unwrap();
let g = ECP2::generator();
let pk_expected = pair::g2mul(&g, &sk_big);
assert_eq!(pk_expected, pk_ecp);
}
#[test]
fn test_key_pair_generation_g2() {
let mut rng = create_rng();
let (sk, pk) = key_pair_generate_g2(&mut rng);
let sk_big = secret_key_from_bytes(&sk).unwrap();
let pk_ecp = deserialize_g1(&pk).unwrap();
let g = ECP::generator();
let pk_expected = pair::g1mul(&g, &sk_big);
assert_eq!(pk_expected, pk_ecp);
}
#[test]
fn test_bls_verify_g1() {
let mut rng = create_rng();
let (sk, pk) = key_pair_generate_g1(&mut rng);
let msg = [2u8; 32];
let signature = core_sign_g1(&sk, &msg, TEST_DST).unwrap();
let valid = core_verify_g1(&pk, &msg, &signature, TEST_DST);
assert!(valid);
}
#[test]
fn test_bls_verify_g2() {
let mut rng = create_rng();
let (sk, pk) = key_pair_generate_g2(&mut rng);
let msg = [2u8; 32];
let signature = core_sign_g2(&sk, &msg, TEST_DST).unwrap();
let valid = core_verify_g2(&pk, &msg, &signature, TEST_DST);
assert!(valid);
}
#[test]
fn test_bls_aggregate_verify_g1() {
let n = 10;
let mut rng = create_rng();
let mut public_keys: Vec<Vec<u8>> = vec![];
let mut msgs: Vec<Vec<u8>> = vec![];
let mut signatures: Vec<Vec<u8>> = vec![];
for i in 0..n {
// Generate keypair
let key_pair = key_pair_generate_g1(&mut rng);
public_keys.push(key_pair.1.to_vec());
// Sign message
msgs.push(vec![i as u8; 32]);
let signature = core_sign_g1(&key_pair.0, &msgs[i], TEST_DST).unwrap();
signatures.push(signature.to_vec());
}
let signatures_refs: Vec<&[u8]> = signatures.iter().map(|sig| sig.as_slice()).collect();
let aggregate = aggregate_g1(&signatures_refs).unwrap();
let public_keys_refs: Vec<&[u8]> = public_keys.iter().map(|pk| pk.as_slice()).collect();
let msgs_refs: Vec<&[u8]> = msgs.iter().map(|msg| msg.as_slice()).collect();
let valid = core_aggregate_verify_g1(&public_keys_refs, &msgs_refs, &aggregate, TEST_DST);
assert!(valid);
}
#[test]
fn test_bls_aggregate_verify_g2() {
let n = 10;
let mut rng = create_rng();
let mut public_keys: Vec<Vec<u8>> = vec![];
let mut msgs: Vec<Vec<u8>> = vec![];
let mut signatures: Vec<Vec<u8>> = vec![];
for i in 0..n {
// Generate keypair
let key_pair = key_pair_generate_g2(&mut rng);
public_keys.push(key_pair.1.to_vec());
// Sign message
msgs.push(vec![i as u8; 32]);
let signature = core_sign_g2(&key_pair.0, &msgs[i], TEST_DST).unwrap();
signatures.push(signature.to_vec());
}
let signatures_refs: Vec<&[u8]> = signatures.iter().map(|sig| sig.as_slice()).collect();
let aggregate = aggregate_g2(&signatures_refs).unwrap();
let public_keys_refs: Vec<&[u8]> = public_keys.iter().map(|pk| pk.as_slice()).collect();
let msgs_refs: Vec<&[u8]> = msgs.iter().map(|msg| msg.as_slice()).collect();
let valid = core_aggregate_verify_g2(&public_keys_refs, &msgs_refs, &aggregate, TEST_DST);
assert!(valid);
}
#[test]
fn serde_g1_round_trip() {
let mut rng = create_rng();
let n = 5;
for _ in 0..n {
let (_, compressed) = key_pair_generate_g2(&mut rng);
let decompressed = deserialize_g1(&compressed).unwrap();
let compressed_result = serialize_g1(&decompressed).to_vec();
assert_eq!(compressed.to_vec(), compressed_result);
}
}
#[test]
fn serde_g2_round_trip() {
let mut rng = create_rng();
let n = 5;
for _ in 0..n {
let (_, compressed) = key_pair_generate_g1(&mut rng);
let decompressed = deserialize_g2(&compressed).unwrap();
let compressed_result = serialize_g2(&decompressed).to_vec();
assert_eq!(compressed.to_vec(), compressed_result);
}
}
#[test]
fn serde_g1_known_input() {
// Input 1
let compressed = hex::decode("b53d21a4cfd562c469cc81514d4ce5a6b577d8403d32a394dc265dd190b47fa9f829fdd7963afdf972e5e77854051f6f").unwrap();
let decompressed = deserialize_g1(&compressed).unwrap();
let compressed_result = serialize_g1(&decompressed).to_vec();
assert_eq!(compressed, compressed_result);
// Input 2
let compressed = hex::decode("b301803f8b5ac4a1133581fc676dfedc60d891dd5fa99028805e5ea5b08d3491af75d0707adab3b70c6a6a580217bf81").unwrap();
let decompressed = deserialize_g1(&compressed).unwrap();
let compressed_result = serialize_g1(&decompressed).to_vec();
assert_eq!(compressed, compressed_result);
// Input 3
let compressed = hex::decode("a491d1b0ecd9bb917989f0e74f0dea0422eac4a873e5e2644f368dffb9a6e20fd6e10c1b77654d067c0618f6e5a7f79a").unwrap();
let decompressed = deserialize_g1(&compressed).unwrap();
let compressed_result = serialize_g1(&decompressed).to_vec();
assert_eq!(compressed, compressed_result);
}
#[test]
fn serde_g2_known_input() {
// Input 1
let compressed = hex::decode("a666d31d7e6561371644eb9ca7dbcb87257d8fd84a09e38a7a491ce0bbac64a324aa26385aebc99f47432970399a2ecb0def2d4be359640e6dae6438119cbdc4f18e5e4496c68a979473a72b72d3badf98464412e9d8f8d2ea9b31953bb24899").unwrap();
let decompressed = deserialize_g2(&compressed).unwrap();
let compressed_result = serialize_g2(&decompressed).to_vec();
assert_eq!(compressed, compressed_result);
// Input 2
let compressed = hex::decode("a63e88274adb7a98d112c16f7057f388786496c8f57e03ee9052b46b15eb0166645008f8cc929eb4475e386f3e6f1df81181e97fac61e371a22f34a4622f7e343ca0d99846b175a92ad1bf1df6fd4d0800e4edb7c2eb3d8437ed10cbc2d88823").unwrap();
let decompressed = deserialize_g2(&compressed).unwrap();
let compressed_result = serialize_g2(&decompressed).to_vec();
assert_eq!(compressed, compressed_result);
// Input 3
let compressed = hex::decode("b090fbc9d5c6c80fec73c567202a75664cd00c2592e472a4d81d2ed4b6a166311e809ca25eb88c5d0189cbf1baa8ea7918ca20f0b66678c0230e65eb4ebb3d621940984f71eb5481453e4489dafcc7f6ee2c863b76671467002a8f2392063005").unwrap();
let decompressed = deserialize_g2(&compressed).unwrap();
let compressed_result = serialize_g2(&decompressed).to_vec();
assert_eq!(compressed, compressed_result);
}
#[test]
fn serde_g1_uncompressed_round_trip() {
let mut rng = create_rng();
let n = 5;
let g = ECP::generator();
let r = Big::new_ints(&CURVE_ORDER);
for _ in 0..n {
let num = Big::randomnum(&r, &mut rng);
let point = pair::g1mul(&g, &num);
let uncompressed = serialize_uncompressed_g1(&point);
let point_round_trip = deserialize_g1(&uncompressed).unwrap();
assert_eq!(point, point_round_trip);
}
}
#[test]
fn serde_g2_uncompressed_round_trip() {
let mut rng = create_rng();
let n = 5;
let g = ECP2::generator();
let r = Big::new_ints(&CURVE_ORDER);
for _ in 0..n {
let num = Big::randomnum(&r, &mut rng);
let point = pair::g2mul(&g, &num);
let uncompressed = serialize_uncompressed_g2(&point);
let point_round_trip = deserialize_g2(&uncompressed).unwrap();
assert_eq!(point, point_round_trip);
}
}
}