devolutions_crypto/signature/
mod.rs

1//! Module for signing and verifying data.
2//!
3//! ```rust
4//! use std::convert::TryInto;
5//!
6//! use devolutions_crypto::signing_key::{generate_signing_keypair, SigningKeyVersion, SigningKeyPair, SigningPublicKey};
7//! use devolutions_crypto::signature::{sign, Signature, SignatureVersion};
8//!
9//! let keypair: SigningKeyPair = generate_signing_keypair(SigningKeyVersion::Latest);
10//! let public_key: SigningPublicKey = keypair.get_public_key();
11//!
12//! // You can sign data using the keypair.
13//! let signature: Signature = sign(b"this is some test data", &keypair, SignatureVersion::Latest);
14//!
15//! // You can then verify if the signature is valid
16//! assert!(signature.verify(b"this is some test data", &public_key));
17//! assert!(!signature.verify(b"this is some wrong test data", &public_key));
18//!
19//! // You can serialize the signature to and from a byte array.
20//! let signature_bytes: Vec<u8> = signature.into();
21//!
22//! let signature: Signature = signature_bytes.as_slice().try_into().expect("This signature should be valid");
23//!
24//! assert!(signature.verify(b"this is some test data", &public_key));
25//! assert!(!signature.verify(b"this is some wrong test data", &public_key));
26//! ```
27mod signature_v1;
28
29use super::DataType;
30use super::Error;
31use super::Header;
32use super::HeaderType;
33use super::Result;
34use super::SignatureSubtype;
35pub use super::SignatureVersion;
36
37use super::signing_key::{SigningKeyPair, SigningPublicKey};
38
39use signature_v1::SignatureV1;
40
41use std::borrow::Borrow;
42use std::convert::TryFrom;
43
44#[cfg(feature = "fuzz")]
45use arbitrary::Arbitrary;
46
47/// A versionned signature. Can be used to validate if some data has been tampered.
48#[derive(Clone, Debug)]
49#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
50pub struct Signature {
51    pub(crate) header: Header<Signature>,
52    payload: SignaturePayload,
53}
54
55impl HeaderType for Signature {
56    type Version = SignatureVersion;
57    type Subtype = SignatureSubtype;
58
59    fn data_type() -> DataType {
60        DataType::Signature
61    }
62}
63
64#[derive(Clone, Debug)]
65#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
66enum SignaturePayload {
67    V1(SignatureV1),
68}
69
70/// Sign some data with a keypair so that anyone knowing the public part of it can verify the signature.
71/// # Arguments
72///  * `data` - The data you want to sign
73///  * `keypair` - The keypair to use to sign the data. Note that the public part of the keypair is also required to sign the data.
74///  * `version` - Version of the signature scheme to use. Use `SignatureVersion::Latest` if you're not dealing with shared data.
75/// # Returns
76/// Returns a `Signature` that can be used to verify if the data has been tempered with or if.
77/// # Example
78/// ```rust
79/// use devolutions_crypto::signing_key::{generate_signing_keypair, SigningKeyVersion, SigningKeyPair};
80/// use devolutions_crypto::signature::{sign, Signature, SignatureVersion};
81///
82/// let keypair: SigningKeyPair = generate_signing_keypair(SigningKeyVersion::Latest);
83/// let signature: Signature = sign(b"this is some test data", &keypair, SignatureVersion::Latest);
84/// ```
85pub fn sign(data: &[u8], keypair: &SigningKeyPair, version: SignatureVersion) -> Signature {
86    let mut header = Header::default();
87
88    let payload = match version {
89        SignatureVersion::V1 | SignatureVersion::Latest => {
90            header.version = SignatureVersion::V1;
91            SignaturePayload::V1(SignatureV1::sign(data, keypair))
92        }
93    };
94
95    Signature { header, payload }
96}
97
98impl Signature {
99    /// Verify if the signature matches with the specified data and key.
100    /// # Arguments
101    ///  * `data` - The data that's signed.
102    ///  * `public_key` - The public part of the keypair used to sign the data.
103    /// # Returns
104    /// Returns true if the signature is valid and false if it doesn't.
105    /// # Example
106    /// ```rust
107    /// use devolutions_crypto::signing_key::{generate_signing_keypair, SigningKeyVersion, SigningKeyPair};
108    /// use devolutions_crypto::signature::{sign, Signature, SignatureVersion};
109    ///
110    /// let keypair: SigningKeyPair = generate_signing_keypair(SigningKeyVersion::Latest);
111    /// let signature: Signature = sign(b"this is some test data", &keypair, SignatureVersion::Latest);
112    ///
113    /// assert!(signature.verify(b"this is some test data", &keypair.get_public_key()));
114    /// ```
115    pub fn verify(&self, data: &[u8], public_key: &SigningPublicKey) -> bool {
116        match &self.payload {
117            SignaturePayload::V1(x) => x.verify(data, public_key),
118        }
119    }
120}
121
122impl From<Signature> for Vec<u8> {
123    /// Serialize the structure into a `Vec<u8>`, for storage, transmission or use in another language.
124    fn from(data: Signature) -> Self {
125        let mut header: Self = data.header.borrow().into();
126        let mut payload: Self = data.payload.into();
127        header.append(&mut payload);
128        header
129    }
130}
131
132impl TryFrom<&[u8]> for Signature {
133    type Error = Error;
134
135    /// Parses the data. Can return an Error of the data is invalid or unrecognized.
136    fn try_from(data: &[u8]) -> Result<Self> {
137        if data.len() < Header::len() {
138            return Err(Error::InvalidLength);
139        };
140
141        let header = Header::try_from(&data[0..Header::len()])?;
142
143        let payload = match header.version {
144            SignatureVersion::V1 => {
145                SignaturePayload::V1(SignatureV1::try_from(&data[Header::len()..])?)
146            }
147            _ => return Err(Error::UnknownVersion),
148        };
149
150        Ok(Self { header, payload })
151    }
152}
153
154impl From<SignaturePayload> for Vec<u8> {
155    fn from(data: SignaturePayload) -> Self {
156        match data {
157            SignaturePayload::V1(x) => x.into(),
158        }
159    }
160}
161
162#[test]
163fn test_signature_v1() {
164    use std::convert::TryInto;
165
166    let data = b"this is a test";
167    let wrong_data = b"this is wrong";
168
169    let keypair = crate::signing_key::generate_signing_keypair(crate::SigningKeyVersion::V1);
170    let public = keypair.get_public_key();
171
172    let keypair2 = crate::signing_key::generate_signing_keypair(crate::SigningKeyVersion::V1);
173    let public2 = keypair2.get_public_key();
174
175    let sig1 = sign(data, &keypair, SignatureVersion::V1);
176
177    assert!(sig1.verify(data, &public));
178    assert!(!sig1.verify(data, &public2));
179    assert!(!sig1.verify(wrong_data, &public));
180    assert!(!sig1.verify(wrong_data, &public2));
181
182    let keypair_bytes: Vec<u8> = keypair.into();
183    let public_bytes: Vec<u8> = public.into();
184
185    let keypair_parsed: SigningKeyPair = (keypair_bytes.as_slice()).try_into().unwrap();
186    let public_parsed: SigningPublicKey = (public_bytes.as_slice()).try_into().unwrap();
187
188    let sig2 = sign(data, &keypair_parsed, SignatureVersion::V1);
189    assert!(sig1.verify(data, &public_parsed));
190    assert!(sig2.verify(data, &public_parsed));
191}