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}