1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Copyright 2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Multiple primitives for work with public and secret keys and with secp256k1 curve points

use super::{Error, Public, Secret, SECP256K1};
use ethereum_types::{BigEndianHash as _, H256, U256};
use lazy_static::lazy_static;
use secp256k1::constants::CURVE_ORDER as SECP256K1_CURVE_ORDER;
use secp256k1::key;

/// Generation point array combined from X and Y coordinates
/// Equivalent to uncompressed form, see https://tools.ietf.org/id/draft-jivsov-ecc-compact-05.html#rfc.section.3
pub const BASE_POINT_BYTES: [u8; 65] = [
	0x4, // The X coordinate of the generator
	0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87, 0x0b, 0x07, 0x02, 0x9b, 0xfc,
	0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b, 0x16, 0xf8, 0x17, 0x98,
	// The Y coordinate of the generator
	0x48, 0x3a, 0xda, 0x77, 0x26, 0xa3, 0xc4, 0x65, 0x5d, 0xa4, 0xfb, 0xfc, 0x0e, 0x11, 0x08, 0xa8, 0xfd, 0x17, 0xb4,
	0x48, 0xa6, 0x85, 0x54, 0x19, 0x9c, 0x47, 0xd0, 0x8f, 0xfb, 0x10, 0xd4, 0xb8,
];

lazy_static! {
	pub static ref CURVE_ORDER: U256 = H256::from_slice(&SECP256K1_CURVE_ORDER).into_uint();
}

/// In-place multiply public key by secret key (EC point * scalar)
pub fn public_mul_secret(public: &mut Public, secret: &Secret) -> Result<(), Error> {
	let key_secret = secret.to_secp256k1_secret()?;
	let mut key_public = to_secp256k1_public(public)?;
	key_public.mul_assign(&SECP256K1, &key_secret[..])?;
	set_public(public, &key_public);
	Ok(())
}

/// In-place add one public key to another (EC point + EC point)
pub fn public_add(public: &mut Public, other: &Public) -> Result<(), Error> {
	let key_public = to_secp256k1_public(public)?;
	let other_public = to_secp256k1_public(other)?;
	let key_public = key_public.combine(&other_public)?;
	set_public(public, &key_public);
	Ok(())
}

/// In-place sub one public key from another (EC point - EC point)
pub fn public_sub(public: &mut Public, other: &Public) -> Result<(), Error> {
	let mut key_neg_other = to_secp256k1_public(other)?;
	key_neg_other.mul_assign(&SECP256K1, super::MINUS_ONE_KEY)?;

	let mut key_public = to_secp256k1_public(public)?;
	key_public = key_public.combine(&key_neg_other)?;
	set_public(public, &key_public);
	Ok(())
}

/// Replace a public key with its additive inverse (EC point = - EC point)
pub fn public_negate(public: &mut Public) -> Result<(), Error> {
	let mut key_public = to_secp256k1_public(public)?;
	key_public.mul_assign(&SECP256K1, super::MINUS_ONE_KEY)?;
	set_public(public, &key_public);
	Ok(())
}

/// Return the generation point (aka base point) of secp256k1
pub fn generation_point() -> Public {
	let public_key = key::PublicKey::from_slice(&BASE_POINT_BYTES).expect("constructed using constants; qed");
	let mut public = Public::default();
	set_public(&mut public, &public_key);
	public
}

fn to_secp256k1_public(public: &Public) -> Result<key::PublicKey, Error> {
	let public_data = {
		let mut temp = [4u8; 65];
		(&mut temp[1..65]).copy_from_slice(&public[0..64]);
		temp
	};

	Ok(key::PublicKey::from_slice(&public_data)?)
}

fn set_public(public: &mut Public, key_public: &key::PublicKey) {
	let key_public_serialized = key_public.serialize_uncompressed();
	public.as_bytes_mut().copy_from_slice(&key_public_serialized[1..65]);
}

#[cfg(test)]
mod tests {
	use super::super::{Generator, Random, Secret};
	use super::{generation_point, public_add, public_mul_secret, public_negate, public_sub};

	#[test]
	fn public_addition_is_commutative() {
		let public1 = Random.generate().public().clone();
		let public2 = Random.generate().public().clone();

		let mut left = public1.clone();
		public_add(&mut left, &public2).unwrap();

		let mut right = public2.clone();
		public_add(&mut right, &public1).unwrap();

		assert_eq!(left, right);
	}

	#[test]
	fn public_addition_is_reversible_with_subtraction() {
		let public1 = Random.generate().public().clone();
		let public2 = Random.generate().public().clone();

		let mut sum = public1.clone();
		public_add(&mut sum, &public2).unwrap();
		public_sub(&mut sum, &public2).unwrap();

		assert_eq!(sum, public1);
	}

	#[test]
	fn public_negation_is_involutory() {
		let public = Random.generate().public().clone();
		let mut negation = public.clone();
		public_negate(&mut negation).unwrap();
		public_negate(&mut negation).unwrap();

		assert_eq!(negation, public);
	}

	#[test]
	fn generation_point_expected() {
		let point = generation_point();
		// Check the returned value equal to uncompressed form for sec2561k1
		assert_eq!(format!("{:x}", point), "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8");
	}

	#[test]
	fn public_multiplication_verification() {
		let secret =
			Secret::copy_from_str(&"a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap();
		let mut public = generation_point();
		public_mul_secret(&mut public, &secret).unwrap();
		assert_eq!(format!("{:x}", public), "8ce0db0b0359ffc5866ba61903cc2518c3675ef2cf380a7e54bde7ea20e6fa1ab45b7617346cd11b7610001ee6ae5b0155c41cad9527cbcdff44ec67848943a4");
	}
}