cfxkey/
math.rs

1// Copyright 2015-2019 Parity Technologies (UK) Ltd.
2// This file is part of Parity Ethereum.
3
4// Parity Ethereum is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity Ethereum is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity Ethereum.  If not, see <http://www.gnu.org/licenses/>.
16
17use super::{Error, Public, Secret};
18use cfx_types::{BigEndianHash as _, H256, U256};
19use secp256k1::{
20    constants::{CURVE_ORDER, GENERATOR_X, GENERATOR_Y},
21    PublicKey, Scalar, SECP256K1,
22};
23
24/// Whether the public key is valid (parses as a non-infinity secp256k1 point).
25pub fn public_is_valid(public: &Public) -> bool {
26    public_to_pubkey(public).is_ok()
27}
28
29/// Inplace multiply public key by secret key (EC point * scalar)
30pub fn public_mul_secret(
31    public: &mut Public, secret: &Secret,
32) -> Result<(), Error> {
33    let key_secret = secret.to_secp256k1_secret()?;
34    let key_public = public_to_pubkey(public)?;
35    let res = key_public.mul_tweak(SECP256K1, &Scalar::from(key_secret))?;
36    *public = pubkey_to_public(&res);
37    Ok(())
38}
39
40/// Inplace add one public key to another (EC point + EC point)
41pub fn public_add(public: &mut Public, other: &Public) -> Result<(), Error> {
42    let key_public = public_to_pubkey(public)?;
43    let other_public = public_to_pubkey(other)?;
44    let res = key_public.combine(&other_public)?;
45    *public = pubkey_to_public(&res);
46    Ok(())
47}
48
49/// Inplace sub one public key from another (EC point - EC point)
50pub fn public_sub(public: &mut Public, other: &Public) -> Result<(), Error> {
51    let key_other = public_to_pubkey(other)?;
52    let key_public = public_to_pubkey(public)?;
53    let res = key_public.combine(&key_other.negate(SECP256K1))?;
54    *public = pubkey_to_public(&res);
55    Ok(())
56}
57
58/// Replace public key with its negation (EC point = - EC point)
59pub fn public_negate(public: &mut Public) -> Result<(), Error> {
60    let key_public = public_to_pubkey(public)?;
61    *public = pubkey_to_public(&key_public.negate(SECP256K1));
62    Ok(())
63}
64
65/// Return base point of secp256k1
66pub fn generation_point() -> Public {
67    let mut public_sec_raw = [0u8; 65];
68    public_sec_raw[0] = 4;
69    public_sec_raw[1..33].copy_from_slice(&GENERATOR_X);
70    public_sec_raw[33..65].copy_from_slice(&GENERATOR_Y);
71
72    let public_key = PublicKey::from_slice(&public_sec_raw)
73        .expect("constructing using predefined constants; qed");
74    pubkey_to_public(&public_key)
75}
76
77/// Return secp256k1 elliptic curve order
78pub fn curve_order() -> U256 { H256::from_slice(&CURVE_ORDER).into_uint() }
79
80/// Convert a Conflux 64-byte uncompressed public key into a libsecp256k1
81/// `PublicKey` by prepending the SEC1 `0x04` tag.
82pub(crate) fn public_to_pubkey(public: &Public) -> Result<PublicKey, Error> {
83    let mut buf = [4u8; 65];
84    buf[1..65].copy_from_slice(&public[0..64]);
85    Ok(PublicKey::from_slice(&buf)?)
86}
87
88/// Serialize a libsecp256k1 `PublicKey` into a Conflux 64-byte uncompressed
89/// `Public` (drops the leading SEC1 tag).
90pub(crate) fn pubkey_to_public(pk: &PublicKey) -> Public {
91    let serialized = pk.serialize_uncompressed();
92    let mut public = Public::default();
93    public.as_bytes_mut().copy_from_slice(&serialized[1..65]);
94    public
95}
96
97#[cfg(test)]
98mod tests {
99    use super::{
100        super::{KeyPairGenerator, Public, Random},
101        public_add, public_is_valid, public_sub,
102    };
103
104    #[test]
105    fn zero_public_is_rejected() {
106        // The zero (64-byte) public maps to the SEC1-uncompressed point
107        // (x=0, y=0), which does not satisfy y² = x³ + 7 (mod p) and is
108        // therefore not on the secp256k1 curve. libsecp256k1 must reject it
109        // — confirming that `public_is_valid` does not need an explicit
110        // zero-byte check on top of `PublicKey::from_slice`.
111        assert!(!public_is_valid(&Public::zero()));
112    }
113
114    #[test]
115    fn public_addition_is_commutative() {
116        let public1 = Random.generate().unwrap().public().clone();
117        let public2 = Random.generate().unwrap().public().clone();
118
119        let mut left = public1.clone();
120        public_add(&mut left, &public2).unwrap();
121
122        let mut right = public2.clone();
123        public_add(&mut right, &public1).unwrap();
124
125        assert_eq!(left, right);
126    }
127
128    #[test]
129    fn public_addition_is_reversible_with_subtraction() {
130        let public1 = Random.generate().unwrap().public().clone();
131        let public2 = Random.generate().unwrap().public().clone();
132
133        let mut sum = public1.clone();
134        public_add(&mut sum, &public2).unwrap();
135        public_sub(&mut sum, &public2).unwrap();
136
137        assert_eq!(sum, public1);
138    }
139}