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
//! ECDSA verifier

use super::{recoverable, Error, Signature};
use crate::{AffinePoint, CompressedPoint, EncodedPoint, ProjectivePoint, Scalar, Secp256k1};
use core::convert::TryFrom;
use ecdsa_core::{hazmat::VerifyPrimitive, signature};
use elliptic_curve::{consts::U32, ops::Invert, sec1::ToEncodedPoint};
use signature::{digest::Digest, DigestVerifier};

#[cfg(feature = "sha256")]
use signature::PrehashSignature;

/// ECDSA/secp256k1 verification key (i.e. public key)
#[cfg_attr(docsrs, doc(cfg(feature = "ecdsa")))]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct VerifyKey {
    /// Core ECDSA verify key
    pub(super) key: ecdsa_core::VerifyKey<Secp256k1>,
}

impl VerifyKey {
    /// Initialize [`VerifyKey`] from a SEC1-encoded public key.
    pub fn new(bytes: &[u8]) -> Result<Self, Error> {
        ecdsa_core::VerifyKey::new(bytes).map(|key| VerifyKey { key })
    }

    /// Initialize [`VerifyKey`] from a SEC1 [`EncodedPoint`].
    // TODO(tarcieri): switch to using `FromEncodedPoint` trait?
    pub fn from_encoded_point(public_key: &EncodedPoint) -> Result<Self, Error> {
        ecdsa_core::VerifyKey::from_encoded_point(public_key).map(|key| VerifyKey { key })
    }

    /// Serialize this [`VerifyKey`] as a SEC1-encoded bytestring
    /// (with point compression applied)
    pub fn to_bytes(&self) -> CompressedPoint {
        let mut result = [0u8; 33];
        result.copy_from_slice(EncodedPoint::from(self).as_ref());
        result
    }
}

#[cfg(feature = "sha256")]
impl<S> signature::Verifier<S> for VerifyKey
where
    S: PrehashSignature,
    Self: DigestVerifier<S::Digest, S>,
{
    fn verify(&self, msg: &[u8], signature: &S) -> Result<(), Error> {
        self.verify_digest(S::Digest::new().chain(msg), signature)
    }
}

impl<D> DigestVerifier<D, Signature> for VerifyKey
where
    D: Digest<OutputSize = U32>,
{
    fn verify_digest(&self, digest: D, signature: &Signature) -> Result<(), Error> {
        self.key.verify_digest(digest, signature)
    }
}

impl<D> DigestVerifier<D, recoverable::Signature> for VerifyKey
where
    D: Digest<OutputSize = U32>,
{
    fn verify_digest(&self, digest: D, signature: &recoverable::Signature) -> Result<(), Error> {
        self.key.verify_digest(digest, &Signature::from(*signature))
    }
}

impl From<&AffinePoint> for VerifyKey {
    fn from(affine_point: &AffinePoint) -> VerifyKey {
        VerifyKey::from_encoded_point(&affine_point.to_encoded_point(false)).unwrap()
    }
}

impl From<&VerifyKey> for EncodedPoint {
    fn from(verify_key: &VerifyKey) -> EncodedPoint {
        verify_key.to_encoded_point(true)
    }
}

impl ToEncodedPoint<Secp256k1> for VerifyKey {
    fn to_encoded_point(&self, compress: bool) -> EncodedPoint {
        self.key.to_encoded_point(compress)
    }
}

impl TryFrom<&EncodedPoint> for VerifyKey {
    type Error = Error;

    fn try_from(encoded_point: &EncodedPoint) -> Result<Self, Error> {
        Self::from_encoded_point(encoded_point)
    }
}

impl VerifyPrimitive<Secp256k1> for AffinePoint {
    fn verify_prehashed(&self, z: &Scalar, signature: &Signature) -> Result<(), Error> {
        let r = signature.r();
        let s = signature.s();

        // Ensure signature is "low S" normalized ala BIP 0062
        if s.is_high().into() {
            return Err(Error::new());
        }

        let s_inv = s.invert().unwrap();
        let u1 = z * &s_inv;
        let u2 = *r * s_inv;

        let x = ((ProjectivePoint::generator() * u1) + (ProjectivePoint::from(*self) * u2))
            .to_affine()
            .x;

        if Scalar::from_bytes_reduced(&x.to_bytes()).eq(&r) {
            Ok(())
        } else {
            Err(Error::new())
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::{test_vectors::ecdsa::ECDSA_TEST_VECTORS, Secp256k1};
    ecdsa_core::new_verification_test!(Secp256k1, ECDSA_TEST_VECTORS);
}