rc_crypto/
signature.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5// This file contains code that was copied from the ring crate which is under
6// the ISC license, reproduced below:
7
8// Copyright 2015-2017 Brian Smith.
9
10// Permission to use, copy, modify, and/or distribute this software for any
11// purpose with or without fee is hereby granted, provided that the above
12// copyright notice and this permission notice appear in all copies.
13
14// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
15// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
17// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
19// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21
22use crate::Result;
23use nss::{ec::Curve, ec::PublicKey, pbkdf2::HashAlgorithm};
24
25/// A signature verification algorithm.
26pub struct VerificationAlgorithm {
27    curve: Curve,
28    digest_alg: HashAlgorithm,
29}
30
31pub static ECDSA_P256_SHA256: VerificationAlgorithm = VerificationAlgorithm {
32    curve: Curve::P256,
33    digest_alg: HashAlgorithm::SHA256,
34};
35
36pub static ECDSA_P384_SHA384: VerificationAlgorithm = VerificationAlgorithm {
37    curve: Curve::P384,
38    digest_alg: HashAlgorithm::SHA384,
39};
40
41/// An unparsed public key for signature operations.
42pub struct UnparsedPublicKey<'a> {
43    alg: &'static VerificationAlgorithm,
44    bytes: &'a [u8],
45}
46
47impl<'a> UnparsedPublicKey<'a> {
48    pub fn new(algorithm: &'static VerificationAlgorithm, bytes: &'a [u8]) -> Self {
49        Self {
50            alg: algorithm,
51            bytes,
52        }
53    }
54
55    pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<()> {
56        let pub_key = PublicKey::from_bytes(self.alg.curve, self.bytes)?;
57        Ok(pub_key.verify(message, signature, self.alg.digest_alg)?)
58    }
59
60    pub fn algorithm(&self) -> &'static VerificationAlgorithm {
61        self.alg
62    }
63
64    pub fn bytes(&self) -> &'a [u8] {
65        self.bytes
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72    use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
73    use nss::ensure_initialized;
74
75    #[test]
76    fn test_ecdsa_p384_sha384_verify() {
77        ensure_initialized();
78        // Test generated with JS DOM's WebCrypto.
79        let pub_key_bytes = URL_SAFE_NO_PAD.decode(
80            "BMZj_xHOfLQn5DIEQcYUkyASDWo8O30gWdkWXHHHWN5owKhGWplYHEb4PLf3DkFTg_smprr-ApdULy3NV10x8IZ0EfVaUZdXvTquH1kiw2PxD7fhqiozMXUaSuZI5KBE6w",
81        ).unwrap();
82        let message = URL_SAFE_NO_PAD.decode(
83            "F9MQDmEEdvOfm-NkCRrXqG-aVA9kq0xqtjvtWLndmmt6bO2gfLE2CVDDLzJYds0n88uz27c5JkzdsLpm5HP3aLFgD8bgnGm-EgdBpm99CRiIm7mAMbb0-NRAyUxeoGmdgJPVQLWFNoHRwzKV2wZ0Bk-Bq7jkeDHmDfnx-CJKVMQ",
84        )
85        .unwrap();
86        let signature = URL_SAFE_NO_PAD.decode(
87            "XLZmtJweW4qx0u0l6EpfmB5z-S-CNj4mrl9d7U0MuftdNPhmlNacV4AKR-i4uNn0TUIycU7GsfIjIqxuiL9WdAnfq_KH_SJ95mduqXgWNKlyt8JgMLd4h-jKOllh4erh",
88        )
89        .unwrap();
90        let public_key =
91            crate::signature::UnparsedPublicKey::new(&ECDSA_P384_SHA384, &pub_key_bytes);
92
93        // Failure case: Wrong key algorithm.
94        let public_key_wrong_alg =
95            crate::signature::UnparsedPublicKey::new(&ECDSA_P256_SHA256, &pub_key_bytes);
96        assert!(public_key_wrong_alg.verify(&message, &signature).is_err());
97
98        // Failure case: Add garbage to signature.
99        let mut garbage_signature = signature.clone();
100        garbage_signature.push(42);
101        assert!(public_key.verify(&message, &garbage_signature).is_err());
102
103        // Failure case: Flip a bit in message.
104        let mut garbage_message = message.clone();
105        garbage_message[42] = 42;
106        assert!(public_key.verify(&garbage_message, &signature).is_err());
107
108        // Happy case.
109        assert!(public_key.verify(&message, &signature).is_ok());
110    }
111}