atrium_crypto/
verify.rs

1//! Verifies a signature for a message using a public key.
2use crate::Algorithm;
3use crate::did::parse_did_key;
4use crate::error::{Error, Result};
5use ecdsa::der::{MaxOverhead, MaxSize};
6use ecdsa::elliptic_curve::{
7    AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve,
8    generic_array::ArrayLength,
9    sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint},
10};
11use ecdsa::hazmat::{DigestPrimitive, VerifyPrimitive};
12use ecdsa::{SignatureSize, VerifyingKey};
13use k256::Secp256k1;
14use p256::NistP256;
15use std::ops::Add;
16
17/// Verify a signature for a message using the given DID key formatted public key.
18///
19/// This function verifies a signature using [`Verifier::default()`].
20///
21/// # Examples
22///
23/// ```
24/// use atrium_crypto::verify::verify_signature;
25///
26/// # fn main() -> atrium_crypto::Result<()> {
27/// let did_key = "did:key:zQ3shtNTBUUCARYFEkRPZQ9NCaM5i5hVHPeEsEKXpmVkR2Upq";
28/// let signature = hex::decode(
29///     "fdaa28ab03d6767c11d71fa39627c770ff62f91ca9661401ca0e2c475ae96a8c27064fbde3c355fa8121d2e8bbcf87a2de308e1d72b9bf4270f1e7cd8a1575ab"
30/// ).unwrap();
31/// assert!(verify_signature(did_key, b"Hello, world!", &signature).is_ok());
32/// assert!(verify_signature(did_key, b"Hello, world?", &signature).is_err());
33/// # Ok(())
34/// # }
35/// ```
36pub fn verify_signature(did_key: &str, msg: &[u8], signature: &[u8]) -> Result<()> {
37    let (alg, public_key) = parse_did_key(did_key)?;
38    Verifier::default().verify(alg, &public_key, msg, signature)
39}
40
41/// Verifier for verifying signatures for a message using a public key.
42///
43/// This verifier can be configured to `allow_malleable` mode, which allows
44/// verifying signatures with "high-S" or DER-encoded ones.
45/// By default, this verifier allows only "low-S" signatures.
46///
47/// See also: [https://github.com/bluesky-social/atproto/pull/1839](https://github.com/bluesky-social/atproto/pull/1839)
48#[derive(Debug, Default)]
49pub struct Verifier {
50    allow_malleable: bool,
51}
52
53impl Verifier {
54    /// Create a new verifier with the given malleable mode.
55    pub fn new(allow_malleable: bool) -> Self {
56        Self { allow_malleable }
57    }
58    /// Verify a signature for a message using the given public key.
59    /// The `algorithm` is used to determine the curve for the public key.
60    pub fn verify(
61        &self,
62        algorithm: Algorithm,
63        public_key: &[u8],
64        msg: &[u8],
65        signature: &[u8],
66    ) -> Result<()> {
67        match algorithm {
68            Algorithm::P256 => self.verify_inner::<NistP256>(public_key, msg, signature),
69            Algorithm::Secp256k1 => self.verify_inner::<Secp256k1>(public_key, msg, signature),
70        }
71    }
72    /// Verify a signature for a message using the given public key.
73    /// Any elliptic curve of the generics implementation of [`ECDSA`](ecdsa) can be used for parameter `C`.
74    pub fn verify_inner<C>(&self, public_key: &[u8], msg: &[u8], bytes: &[u8]) -> Result<()>
75    where
76        C: PrimeCurve + CurveArithmetic + DigestPrimitive,
77        AffinePoint<C>: VerifyPrimitive<C> + FromEncodedPoint<C> + ToEncodedPoint<C>,
78        FieldBytesSize<C>: ModulusSize,
79        SignatureSize<C>: ArrayLength<u8>,
80        MaxSize<C>: ArrayLength<u8>,
81        <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
82    {
83        let verifying_key = VerifyingKey::<C>::from_sec1_bytes(public_key)?;
84        if let Ok(mut signature) = ecdsa::Signature::from_slice(bytes) {
85            if let Some(normalized) = signature.normalize_s() {
86                if !self.allow_malleable {
87                    return Err(Error::LowSSignatureNotAllowed);
88                }
89                signature = normalized
90            }
91            Ok(ecdsa::signature::Verifier::verify(&verifying_key, msg, &signature)?)
92        }
93        // signature may be DER-encoded. If `allow_malleable` is true, try to parse and use it.
94        else if self.allow_malleable {
95            let signature = ecdsa::der::Signature::from_bytes(bytes)?;
96            Ok(ecdsa::signature::Verifier::verify(&verifying_key, msg, &signature)?)
97        } else {
98            Err(Error::InvalidSignature)
99        }
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106    use multibase::Base;
107    use serde::{Deserialize, Serialize};
108    use std::{fs::File, path::PathBuf};
109
110    #[derive(Debug, Serialize, Deserialize)]
111    enum Algorithm {
112        ES256,
113        ES256K,
114    }
115
116    #[derive(Debug, Serialize, Deserialize)]
117    #[serde(rename_all = "camelCase")]
118    struct TestVector {
119        comment: String,
120        message_base64: String,
121        algorithm: Algorithm,
122        public_key_multibase: String,
123        public_key_did: String,
124        signature_base64: String,
125        valid_signature: bool,
126        tags: Vec<String>,
127    }
128
129    fn test_vectors(cond: Option<&str>) -> Vec<TestVector> {
130        let data_path =
131            PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/data/signature-fixtures.json");
132        let file = File::open(data_path).expect("opening test data should succeed");
133        let v = serde_json::from_reader::<_, Vec<TestVector>>(file)
134            .expect("parsing test data should succeed");
135        v.into_iter()
136            .filter(|v| if let Some(s) = cond { v.tags.contains(&s.to_string()) } else { true })
137            .collect()
138    }
139
140    #[test]
141    fn verify() {
142        let vectors = test_vectors(None);
143        assert!(!vectors.is_empty());
144        let verifier = Verifier::default();
145        for vector in vectors {
146            let message = Base::Base64
147                .decode(vector.message_base64)
148                .expect("decoding message should succeed");
149            let signature = Base::Base64
150                .decode(vector.signature_base64)
151                .expect("decoding signature should succeed");
152
153            let (base, decoded_key) = multibase::decode(vector.public_key_multibase)
154                .expect("decoding multibase public key should succeed");
155            assert_eq!(base, Base::Base58Btc);
156            let (alg, parsed_key) =
157                parse_did_key(&vector.public_key_did).expect("parsing DID key should succeed");
158
159            // assert_eq!(decoded_key, parsed_key);
160            match vector.algorithm {
161                Algorithm::ES256 => assert_eq!(alg, crate::Algorithm::P256),
162                Algorithm::ES256K => assert_eq!(alg, crate::Algorithm::Secp256k1),
163            }
164            assert_eq!(
165                verifier.verify(alg, &decoded_key, &message, &signature).is_ok(),
166                vector.valid_signature
167            );
168            assert_eq!(
169                verifier.verify(alg, &parsed_key, &message, &signature).is_ok(),
170                vector.valid_signature
171            );
172        }
173    }
174
175    #[test]
176    fn verify_high_s() {
177        let vectors = test_vectors(Some("high-s"));
178        assert!(vectors.len() >= 2);
179        let verifier = Verifier::new(true);
180        for vector in vectors {
181            let message = Base::Base64
182                .decode(vector.message_base64)
183                .expect("decoding message should succeed");
184            let signature = Base::Base64
185                .decode(vector.signature_base64)
186                .expect("decoding signature should succeed");
187
188            let (base, decoded_key) = multibase::decode(vector.public_key_multibase)
189                .expect("decoding multibase public key should succeed");
190            assert_eq!(base, Base::Base58Btc);
191            let (alg, parsed_key) =
192                parse_did_key(&vector.public_key_did).expect("parsing DID key should succeed");
193
194            // assert_eq!(decoded_key, parsed_key);
195            match vector.algorithm {
196                Algorithm::ES256 => assert_eq!(alg, crate::Algorithm::P256),
197                Algorithm::ES256K => assert_eq!(alg, crate::Algorithm::Secp256k1),
198            }
199            assert!(!vector.valid_signature);
200            assert!(verifier.verify(alg, &decoded_key, &message, &signature).is_ok());
201            assert!(verifier.verify(alg, &parsed_key, &message, &signature).is_ok());
202        }
203    }
204
205    #[test]
206    fn verify_der_encoded() {
207        let vectors = test_vectors(Some("der-encoded"));
208        assert!(vectors.len() >= 2);
209        let verifier = Verifier::new(true);
210        for vector in vectors {
211            let message = Base::Base64
212                .decode(vector.message_base64)
213                .expect("decoding message should succeed");
214            let signature = Base::Base64
215                .decode(vector.signature_base64)
216                .expect("decoding signature should succeed");
217
218            let (base, decoded_key) = multibase::decode(vector.public_key_multibase)
219                .expect("decoding multibase public key should succeed");
220            assert_eq!(base, Base::Base58Btc);
221            let (alg, parsed_key) =
222                parse_did_key(&vector.public_key_did).expect("parsing DID key should succeed");
223
224            // assert_eq!(decoded_key, parsed_key);
225            match vector.algorithm {
226                Algorithm::ES256 => assert_eq!(alg, crate::Algorithm::P256),
227                Algorithm::ES256K => assert_eq!(alg, crate::Algorithm::Secp256k1),
228            }
229            assert!(!vector.valid_signature);
230            assert!(verifier.verify(alg, &decoded_key, &message, &signature).is_ok());
231            assert!(verifier.verify(alg, &parsed_key, &message, &signature).is_ok());
232        }
233    }
234}