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
use std::{fmt, ops};
use secp256k1::key;
use secp256k1::{Message as SecpMessage, RecoveryId, RecoverableSignature, Error as SecpError, Signature as SecpSignature};
use hex::ToHex;
use crypto::dhash160;
use hash::{H264, H520};
use {AddressHash, Error, CompactSignature, Signature, Message, SECP256K1};

/// Secret public key
pub enum Public {
	/// Normal version of public key
	Normal(H520),
	/// Compressed version of public key
	Compressed(H264),
}

impl Public {
	pub fn from_slice(data: &[u8]) -> Result<Self, Error> {
		match data.len() {
			33 => {
				let mut public = H264::default();
				public.copy_from_slice(data);
				Ok(Public::Compressed(public))
			},
			65 => {
				let mut public = H520::default();
				public.copy_from_slice(data);
				Ok(Public::Normal(public))
			},
			_ => Err(Error::InvalidPublic)
		}
	}

	pub fn address_hash(&self) -> AddressHash {
		dhash160(self)
	}

	pub fn verify(&self, message: &Message, signature: &Signature) -> Result<bool, Error> {
		let context = &SECP256K1;
		let public = try!(key::PublicKey::from_slice(context, self));
		let mut signature = try!(SecpSignature::from_der_lax(context, signature));
		signature.normalize_s(context);
		let message = try!(SecpMessage::from_slice(&**message));
		match context.verify(&message, &signature, &public) {
			Ok(_) => Ok(true),
			Err(SecpError::IncorrectSignature) => Ok(false),
			Err(x) => Err(x.into()),
		}
	}

	pub fn recover_compact(message: &Message, signature: &CompactSignature) -> Result<Self, Error> {
		let context = &SECP256K1;
		let recovery_id = (signature[0] - 27) & 3;
		let compressed = (signature[0] - 27) & 4 != 0;
		let recovery_id = try!(RecoveryId::from_i32(recovery_id as i32));
		let signature = try!(RecoverableSignature::from_compact(context, &signature[1..65], recovery_id));
		let message = try!(SecpMessage::from_slice(&**message));
		let pubkey = try!(context.recover(&message, &signature));
		let serialized = pubkey.serialize_vec(context, compressed);
		let public = if compressed {
			let mut public = H264::default();
			public.copy_from_slice(&serialized[0..33]);
			Public::Compressed(public)
		} else {
			let mut public = H520::default();
			public.copy_from_slice(&serialized[0..65]);
			Public::Normal(public)
		};
		Ok(public)
	}
}

impl ops::Deref for Public {
	type Target = [u8];

	fn deref(&self) -> &Self::Target {
		match *self {
			Public::Normal(ref hash) => &**hash,
			Public::Compressed(ref hash) => &**hash,
		}
	}
}

impl PartialEq for Public {
	fn eq(&self, other: &Self) -> bool {
		let s_slice: &[u8] = self;
		let o_slice: &[u8] = other;
		s_slice == o_slice
	}
}

impl fmt::Debug for Public {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		match *self {
			Public::Normal(ref hash) => writeln!(f, "normal: {}", hash.to_hex::<String>()),
			Public::Compressed(ref hash) => writeln!(f, "compressed: {}", hash.to_hex::<String>()),
		}
	}
}

impl fmt::Display for Public {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		self.to_hex::<String>().fmt(f)
	}
}