anza-cryptography-source/syscall/bls12-381-syscall/src/addition.rs
Sam Kim 09198923bb
Add workspace.package information and do minor clean-up (#14)
* remove README.md in the syscall directory

* use workspace dependency in `bls12-381`

* add `workspace.package` information

* use 2021 edition for bls12-381

* inherit workspace.package for `ed25519-pokos`

* cargo fmt
2026-04-26 10:11:05 +09:00

230 lines
6.3 KiB
Rust

use {
crate::{
encoding::{swap_fq_endianness, swap_g2_c0_c1, Endianness, PodG1Point, PodG2Point},
Version,
},
blstrs::{G1Projective, G2Projective},
};
/// Performs point addition on G1: `P1 + P2`.
/// Does not check if points are in the correct subgroup for efficiency.
pub fn bls12_381_g1_addition_unchecked(
_version: Version,
p1: &PodG1Point,
p2: &PodG1Point,
endianness: Endianness,
) -> Option<PodG1Point> {
// skip subgroup check for efficiency
let p1_affine = p1.to_affine_subgroup_unchecked(endianness)?;
let p2_affine = p2.to_affine_subgroup_unchecked(endianness)?;
#[allow(clippy::arithmetic_side_effects)]
let sum_proj = G1Projective::from(p1_affine) + p2_affine;
let sum_affine = sum_proj.to_uncompressed();
let mut result = PodG1Point(sum_affine);
if matches!(endianness, Endianness::LE) {
swap_fq_endianness(&mut result.0);
}
Some(result)
}
/// Performs point addition on G2: `P1 + P2`.
pub fn bls12_381_g2_addition_unchecked(
_version: Version,
p1: &PodG2Point,
p2: &PodG2Point,
endianness: Endianness,
) -> Option<PodG2Point> {
// skip subgroup check for efficiency
let p1_affine = p1.to_affine_subgroup_unchecked(endianness)?;
let p2_affine = p2.to_affine_subgroup_unchecked(endianness)?;
#[allow(clippy::arithmetic_side_effects)]
let sum_proj = G2Projective::from(p1_affine) + p2_affine;
let sum_affine = sum_proj.to_uncompressed();
let mut result = PodG2Point(sum_affine);
if matches!(endianness, Endianness::LE) {
swap_g2_c0_c1(&mut result.0);
swap_fq_endianness(&mut result.0);
}
Some(result)
}
#[cfg(test)]
mod tests {
use {super::*, crate::test_vectors::*, bytemuck::pod_read_unaligned};
fn to_pod_g1(bytes: &[u8]) -> PodG1Point {
pod_read_unaligned(bytes)
}
fn to_pod_g2(bytes: &[u8]) -> PodG2Point {
pod_read_unaligned(bytes)
}
fn run_g1_test(
test_name: &str,
input_be: &[u8],
output_be: &[u8],
input_le: &[u8],
output_le: &[u8],
) {
// G1 Input is [P1 (96) | P2 (96)]
let (p1_be, p2_be) = input_be.split_at(96);
let p1_be = to_pod_g1(p1_be);
let p2_be = to_pod_g1(p2_be);
let expected_be = to_pod_g1(output_be);
// Test Big Endian
let result_be =
bls12_381_g1_addition_unchecked(Version::V0, &p1_be, &p2_be, Endianness::BE);
assert_eq!(
result_be,
Some(expected_be),
"G1 {test_name} BE Test Failed",
);
// Test Little Endian
let (p1_le, p2_le) = input_le.split_at(96);
let p1_le = to_pod_g1(p1_le);
let p2_le = to_pod_g1(p2_le);
let expected_le = to_pod_g1(output_le);
let result_le =
bls12_381_g1_addition_unchecked(Version::V0, &p1_le, &p2_le, Endianness::LE);
assert_eq!(
result_le,
Some(expected_le),
"G1 {test_name} LE Test Failed",
);
}
fn run_g2_test(
test_name: &str,
input_be: &[u8],
output_be: &[u8],
input_le: &[u8],
output_le: &[u8],
) {
// G2 Input is [P1 (192) | P2 (192)]
let (p1_be, p2_be) = input_be.split_at(192);
let p1_be = to_pod_g2(p1_be);
let p2_be = to_pod_g2(p2_be);
let expected_be = to_pod_g2(output_be);
// Test Big Endian
let result_be =
bls12_381_g2_addition_unchecked(Version::V0, &p1_be, &p2_be, Endianness::BE);
assert_eq!(
result_be,
Some(expected_be),
"G2 {test_name} BE Test Failed",
);
// Test Little Endian
let (p1_le, p2_le) = input_le.split_at(192);
let p1_le = to_pod_g2(p1_le);
let p2_le = to_pod_g2(p2_le);
let expected_le = to_pod_g2(output_le);
let result_le =
bls12_381_g2_addition_unchecked(Version::V0, &p1_le, &p2_le, Endianness::LE);
assert_eq!(
result_le,
Some(expected_le),
"G2 {test_name} LE Test Failed",
);
}
#[test]
fn test_g1_addition_random() {
run_g1_test(
"ADD: P (Rand) + Q (Rand)",
INPUT_BE_G1_ADD_RANDOM,
OUTPUT_BE_G1_ADD_RANDOM,
INPUT_LE_G1_ADD_RANDOM,
OUTPUT_LE_G1_ADD_RANDOM,
);
}
#[test]
fn test_g1_addition_doubling() {
run_g1_test(
"ADD: P + P (Doubling)",
INPUT_BE_G1_ADD_DOUBLING,
OUTPUT_BE_G1_ADD_DOUBLING,
INPUT_LE_G1_ADD_DOUBLING,
OUTPUT_LE_G1_ADD_DOUBLING,
);
}
#[test]
fn test_g1_addition_infinity_edge_cases() {
// P + Inf
run_g1_test(
"ADD: P + Inf",
INPUT_BE_G1_ADD_P_PLUS_INF,
OUTPUT_BE_G1_ADD_P_PLUS_INF,
INPUT_LE_G1_ADD_P_PLUS_INF,
OUTPUT_LE_G1_ADD_P_PLUS_INF,
);
// Inf + Inf
run_g1_test(
"ADD: Inf + Inf",
INPUT_BE_G1_ADD_INF_PLUS_INF,
OUTPUT_BE_G1_ADD_INF_PLUS_INF,
INPUT_LE_G1_ADD_INF_PLUS_INF,
OUTPUT_LE_G1_ADD_INF_PLUS_INF,
);
}
#[test]
fn test_g2_addition_random() {
run_g2_test(
"ADD: P (Rand) + Q (Rand)",
INPUT_BE_G2_ADD_RANDOM,
OUTPUT_BE_G2_ADD_RANDOM,
INPUT_LE_G2_ADD_RANDOM,
OUTPUT_LE_G2_ADD_RANDOM,
);
}
#[test]
fn test_g2_addition_doubling() {
run_g2_test(
"ADD: P + P (Doubling)",
INPUT_BE_G2_ADD_DOUBLING,
OUTPUT_BE_G2_ADD_DOUBLING,
INPUT_LE_G2_ADD_DOUBLING,
OUTPUT_LE_G2_ADD_DOUBLING,
);
}
#[test]
fn test_g2_addition_infinity_edge_cases() {
// P + Inf
run_g2_test(
"ADD: P + Inf",
INPUT_BE_G2_ADD_P_PLUS_INF,
OUTPUT_BE_G2_ADD_P_PLUS_INF,
INPUT_LE_G2_ADD_P_PLUS_INF,
OUTPUT_LE_G2_ADD_P_PLUS_INF,
);
// Inf + Inf
run_g2_test(
"ADD: Inf + Inf",
INPUT_BE_G2_ADD_INF_PLUS_INF,
OUTPUT_BE_G2_ADD_INF_PLUS_INF,
INPUT_LE_G2_ADD_INF_PLUS_INF,
OUTPUT_LE_G2_ADD_INF_PLUS_INF,
);
}
}