From 3f8e0ff6c993f2bf4966e6f9d7ea32a01a03b24d Mon Sep 17 00:00:00 2001 From: sangge-redmi <2251250136@qq.com> Date: Wed, 4 Sep 2024 09:58:38 +0800 Subject: [PATCH] first commit --- .gitignore | 72 ++++++++++++++ Cargo.lock | 201 ++++++++++++++++++++++++++++++++++++++ Cargo.toml | 14 +++ pyproject.toml | 15 +++ src/lib.rs | 254 +++++++++++++++++++++++++++++++++++++++++++++++++ test.py | 24 +++++ 6 files changed, 580 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 pyproject.toml create mode 100644 src/lib.rs create mode 100644 test.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c8f0442 --- /dev/null +++ b/.gitignore @@ -0,0 +1,72 @@ +/target + +# Byte-compiled / optimized / DLL files +__pycache__/ +.pytest_cache/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +.venv/ +env/ +bin/ +build/ +develop-eggs/ +dist/ +eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +include/ +man/ +venv/ +*.egg-info/ +.installed.cfg +*.egg + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt +pip-selfcheck.json + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.cache +nosetests.xml +coverage.xml + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# Rope +.ropeproject + +# Django stuff: +*.log +*.pot + +.DS_Store + +# Sphinx documentation +docs/_build/ + +# PyCharm +.idea/ + +# VSCode +.vscode/ + +# Pyenv +.python-version diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..fbfd4a6 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,201 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ecc_rs" +version = "0.1.1" +dependencies = [ + "num-bigint", + "num-traits", + "pyo3", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "portable-atomic" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pyo3" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831e8e819a138c36e212f3af3fd9eeffed6bf1510a805af35b0edee5ffa59433" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "memoffset", + "once_cell", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e8730e591b14492a8945cdff32f089250b05f5accecf74aeddf9e8272ce1fa8" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e97e919d2df92eb88ca80a037969f44e5e70356559654962cbb3316d00300c6" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb57983022ad41f9e683a599f2fd13c3664d7063a3ac5714cae4b7bee7d3f206" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec480c0c51ddec81019531705acac51bcdbeae563557c982aa8263bb96880372" +dependencies = [ + "heck", + "proc-macro2", + "pyo3-build-config", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unindent" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..fba6383 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "ecc_rs" +version = "0.1.1" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "ecc_rs" +crate-type = ["cdylib"] + +[dependencies] +num-bigint = "0.4.6" +num-traits = "0.2.19" +pyo3 = "0.22.0" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..f1c7689 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,15 @@ +[build-system] +requires = ["maturin>=1.7,<2.0"] +build-backend = "maturin" + +[project] +name = "ecc_rs" +requires-python = ">=3.8" +classifiers = [ + "Programming Language :: Rust", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", +] +dynamic = ["version"] +[tool.maturin] +features = ["pyo3/extension-module"] diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..bb26e6c --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,254 @@ +#![allow(dead_code)] +use num_bigint::BigUint; +use num_traits::{One, Zero}; +use pyo3::prelude::*; + +/// Define curve +#[derive(Debug, Clone)] +struct CurveFp { + name: &'static str, + a: BigUint, + b: BigUint, + p: BigUint, + n: BigUint, + gx: BigUint, + gy: BigUint, +} + +#[derive(Debug, Clone)] +struct Point { + x: BigUint, + y: BigUint, + curve: CurveFp, +} + +// Initialize the SM2 Curve +fn sm2p256v1() -> CurveFp { + CurveFp { + name: "sm2p256v1", + a: BigUint::parse_bytes( + b"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", + 16, + ) + .unwrap(), + b: BigUint::parse_bytes( + b"28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", + 16, + ) + .unwrap(), + p: BigUint::parse_bytes( + b"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", + 16, + ) + .unwrap(), + n: BigUint::parse_bytes( + b"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", + 16, + ) + .unwrap(), + gx: BigUint::parse_bytes( + b"32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", + 16, + ) + .unwrap(), + gy: BigUint::parse_bytes( + b"BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", + 16, + ) + .unwrap(), + } +} + +/// Calculates the greatest common divisor (GCD) of two `BigUint` numbers. +/// +/// The function uses the Euclidean algorithm to compute the GCD. The algorithm is based on +/// the principle that the greatest common divisor of two numbers does not change if the larger +/// number is replaced by its difference with the smaller number. +/// +/// # Arguments +/// +/// * `a` - The first `BigUint` number. +/// * `b` - The second `BigUint` number. +/// +/// # Returns +/// +/// * `BigUint` - The greatest common divisor of `a` and `b`. +/// +/// # Example +/// +/// ``` +/// let a = BigUint::from(60u32); +/// let b = BigUint::from(48u32); +/// let result = gcd(a, b); +/// assert_eq!(result, BigUint::from(12u32)); +/// ``` +fn gcd(a: &BigUint, b: &BigUint) -> BigUint { + let mut c = a.clone(); + let mut d = b.clone(); + while !a.is_zero() { + let temp = c.clone(); + c = d % c; + d = temp; + } + d +} + +/// Computes the modular inverse of `a` under modulo `m`. +/// +/// # Arguments +/// +/// * `a` - A reference to a BigUint representing the number to find the modular inverse of. +/// * `p` - A reference to a BigUint representing the modulus. +/// +/// # Return1 +/// +/// * A BigUint representing the modular inverse of `a` modulo `p`. +/// +/// # Panics +/// +/// This function will panic if the modular inverse does not exist (i.e., if `a` and `p` are not coprime). +fn mod_inverse(a: &BigUint, m: &BigUint) -> BigUint { + let (mut t, mut new_t) = (BigUint::zero(), BigUint::one()); + let (mut r, mut new_r) = (m.clone(), a.clone()); + + while !new_r.is_zero() { + let quotient = &r / &new_r; + + let temp_t = new_t.clone(); + new_t = if t < (quotient.clone() * &temp_t) { + // BigUint can't be negative, + // so we use mod to handle the case where t < quotient * temp_t + (&t + m - &(quotient.clone() * &temp_t) % m) % m + } else { + &t - &(quotient.clone() * &temp_t) + }; + + let temp_r = new_r.clone(); + new_r = &r - &(quotient.clone() * &temp_r); + r = temp_r; + t = temp_t; + } + + if r > BigUint::one() { + panic!("Modular inverse does not exist"); + } + + if t < BigUint::zero() { + t += m; + } + + t +} +fn point_addition(p1: &Point, p2: &Point) -> Point { + let curve = &p1.curve; + let p = &curve.p; + + if p1.x.is_zero() && p1.y.is_zero() { + return p2.clone(); + } + if p2.x.is_zero() && p2.y.is_zero() { + return p1.clone(); + } + + let lambda = if p1.x == p2.x && p1.y == p2.y { + let num = (BigUint::from(3u32) * &p1.x * &p1.x + &curve.a) % p; + let denom = (BigUint::from(2u32) * &p1.y) % p; + (num * mod_inverse(&denom, p)) % p + } else { + let num = ((&p2.y + p) - &p1.y) % p; + let denom = ((&p2.x + p) - &p1.x) % p; + + (num * mod_inverse(&denom, p)) % p + }; + + println!("{lambda}"); + + let x3 = (lambda.clone() * &lambda - &p1.x - &p2.x) % p; + let y3 = (lambda * (&p1.x + p - &x3) - &p1.y) % p; + + Point { + x: x3, + y: y3, + curve: curve.clone(), + } +} + +fn point_multiplication(p: &Point, n: &BigUint) -> Point { + let mut result = Point { + x: BigUint::zero(), + y: BigUint::zero(), + curve: p.curve.clone(), + }; + + let mut addend = p.clone(); + let mut k = n.clone(); + + while !k.is_zero() { + if &k % 2u32 == BigUint::one() { + result = point_addition(&result, &addend); + } + addend = point_addition(&addend, &addend); + k >>= 1; + } + + result +} + +/// SM2 addition +#[pyfunction] +fn add(p1: (String, String), p2: (String, String)) -> (String, String) { + let curve = sm2p256v1(); + + let x1 = BigUint::parse_bytes(p1.0.as_bytes(), 10).unwrap(); + let y1 = BigUint::parse_bytes(p1.1.as_bytes(), 10).unwrap(); + let x2 = BigUint::parse_bytes(p2.0.as_bytes(), 10).unwrap(); + let y2 = BigUint::parse_bytes(p2.1.as_bytes(), 10).unwrap(); + + // 检查 x 和 y 是否小于曲线参数 p + if x1 >= curve.p || y1 >= curve.p { + panic!("Point p1 coordinates are out of range"); + } + if x2 >= curve.p || y2 >= curve.p { + panic!("Point p2 coordinates are out of range"); + } + + let point1 = Point { + x: x1, + y: y1, + curve: curve.clone(), + }; + + let point2 = Point { + x: x2, + y: y2, + curve: curve.clone(), + }; + + let result = point_addition(&point1, &point2); + + (result.x.to_str_radix(10), result.y.to_str_radix(10)) +} + +/// SM2 multiply +#[pyfunction] +fn multiply(point: (String, String), n: String) -> (String, String) { + let curve = sm2p256v1(); + let point = Point { + x: BigUint::parse_bytes(point.0.as_bytes(), 10).unwrap(), + y: BigUint::parse_bytes(point.1.as_bytes(), 10).unwrap(), + curve: curve.clone(), + }; + + let scalar_bn = BigUint::parse_bytes(n.as_bytes(), 10).unwrap(); + let result = point_multiplication(&point, &scalar_bn); + + (result.x.to_str_radix(10), result.y.to_str_radix(10)) +} + +/// A Python module implemented in Rust. +#[pymodule] +fn ecc_rs(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_function(wrap_pyfunction_bound!(multiply, m)?)?; + m.add_function(wrap_pyfunction_bound!(add, m)?)?; + Ok(()) +} diff --git a/test.py b/test.py new file mode 100644 index 0000000..7f26eda --- /dev/null +++ b/test.py @@ -0,0 +1,24 @@ +import ecc_rs + +# Example point coordinates for P1 and P2 as tuples (x1, y1) and (x2, y2) +p1 = ( + "1234567890123456789012345678901234567890123456789012345678901234", + "9876543210987654321098765432109876543210987654321098765432109876", +) +p2 = ( + "2234567890123456789012345678901234567890123456789012345678901234", + "2876543210987654321098765432109876543210987654321098765432109876", +) +print(ecc_rs.__all__) + +# Add the two points +result_x, result_y = ecc_rs.add(p1, p2) +print(f"Resulting Point: x = {result_x}, y = {result_y}") + +# Convert the result to integers if needed +result_x_int = int(result_x) +result_y_int = int(result_y) +print(f"Resulting Point as integers: x = {result_x_int}, y = {result_y_int}") + +result = ecc_rs.multiply(p1, "2") +print(f"Resulting Point: x = {result[0]}, y = {result[1]}")