identity_ecdsa_verifier/
secp256k1.rs

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
// Copyright 2020-2024 IOTA Stiftung, Filancore GmbH
// SPDX-License-Identifier: Apache-2.0

use std::ops::Deref;

use identity_verification::jwk::JwkParamsEc;
use identity_verification::jws::SignatureVerificationError;
use identity_verification::jws::SignatureVerificationErrorKind;
use identity_verification::jwu::{self};
use k256::ecdsa::Signature;
use k256::ecdsa::VerifyingKey;
use k256::elliptic_curve::sec1::FromEncodedPoint;
use k256::elliptic_curve::subtle::CtOption;
use k256::EncodedPoint;
use k256::PublicKey;

/// A verifier that can handle the
/// [`JwsAlgorithm::ES256K`](identity_verification::jws::JwsAlgorithm::ES256K)
/// algorithm.
#[derive(Debug, Default)]
#[non_exhaustive]
pub struct Secp256K1Verifier {}

impl Secp256K1Verifier {
  /// Verify a JWS signature secured with the
  /// [`JwsAlgorithm::ES256K`](identity_verification::jws::JwsAlgorithm::ES256K)
  /// algorithm.
  ///
  /// This function is useful when one is building a
  /// [`JwsVerifier`](identity_verification::jws::JwsVerifier) that
  /// handles the
  /// [`JwsAlgorithm::ES256K`](identity_verification::jws::JwsAlgorithm::ES256K)
  /// in the same manner as the [`Secp256K1Verifier`] hence extending its
  /// capabilities.
  ///
  /// # Warning
  ///
  /// This function does not check whether `alg = ES256K` in the protected
  /// header. Callers are expected to assert this prior to calling the
  /// function.
  pub fn verify(
    input: &identity_verification::jws::VerificationInput,
    public_key: &identity_verification::jwk::Jwk,
  ) -> Result<(), SignatureVerificationError> {
    // Obtain a K256 public key.
    let params: &JwkParamsEc = public_key
      .try_ec_params()
      .map_err(|_| SignatureVerificationErrorKind::UnsupportedKeyType)?;

    // Concatenate x and y coordinates as required by
    // EncodedPoint::from_untagged_bytes.
    let public_key_bytes = jwu::decode_b64(&params.x)
      .map_err(|err| {
        SignatureVerificationError::new(SignatureVerificationErrorKind::KeyDecodingFailure).with_source(err)
      })?
      .into_iter()
      .chain(jwu::decode_b64(&params.y).map_err(|err| {
        SignatureVerificationError::new(SignatureVerificationErrorKind::KeyDecodingFailure).with_source(err)
      })?)
      .collect();

    // The JWK contains the uncompressed x and y coordinates, so we can create the
    // encoded point directly without prefixing an SEC1 tag.
    let encoded_point: EncodedPoint = EncodedPoint::from_untagged_bytes(&public_key_bytes);
    let public_key: PublicKey = {
      let opt_public_key: CtOption<PublicKey> = PublicKey::from_encoded_point(&encoded_point);
      if opt_public_key.is_none().into() {
        return Err(SignatureVerificationError::new(
          SignatureVerificationErrorKind::KeyDecodingFailure,
        ));
      } else {
        opt_public_key.unwrap()
      }
    };

    let verifying_key: VerifyingKey = VerifyingKey::from(public_key);

    let mut signature: Signature = Signature::try_from(input.decoded_signature.deref()).map_err(|err| {
      SignatureVerificationError::new(SignatureVerificationErrorKind::InvalidSignature).with_source(err)
    })?;

    if let Some(normalized) = signature.normalize_s() {
      signature = normalized;
    }

    match signature::Verifier::verify(&verifying_key, &input.signing_input, &signature) {
      Ok(()) => Ok(()),
      Err(err) => {
        Err(SignatureVerificationError::new(SignatureVerificationErrorKind::InvalidSignature).with_source(err))
      }
    }
  }
}