ruma_signatures/signatures.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 94 95 96 97 98 99 100 101 102 103 104 105
//! Digital signatures and collections of signatures.
use ruma_common::serde::{base64::Standard, Base64};
use crate::{split_id, Algorithm, Error};
/// A digital signature.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Signature {
/// The cryptographic algorithm that generated this signature.
pub(crate) algorithm: Algorithm,
/// The signature data.
pub(crate) signature: Vec<u8>,
/// The "version" of the key identifier for the public key used to generate this signature.
pub(crate) version: String,
}
impl Signature {
/// Creates a signature from raw bytes.
///
/// While a signature can be created directly using struct literal syntax, this constructor can
/// be used to automatically determine the algorithm and version from a key identifier in the
/// form *algorithm:version*, e.g. "ed25519:1".
///
/// This constructor will ensure that the version does not contain characters that violate the
/// guidelines in the specification. Because it may be necessary to represent signatures with
/// versions that don't adhere to these guidelines, it's possible to simply use the struct
/// literal syntax to construct a `Signature` with an arbitrary key.
///
/// # Parameters
///
/// * id: A key identifier, e.g. "ed25519:1".
/// * bytes: The digital signature, as a series of bytes.
///
/// # Errors
///
/// Returns an error if:
///
/// * The key ID specifies an unknown algorithm.
/// * The key ID is malformed.
/// * The key ID contains a version with invalid characters.
pub fn new(id: &str, bytes: &[u8]) -> Result<Self, Error> {
let (algorithm, version) = split_id(id)?;
Ok(Self { algorithm, signature: bytes.to_vec(), version })
}
/// The algorithm used to generate the signature.
pub fn algorithm(&self) -> &Algorithm {
&self.algorithm
}
/// The raw bytes of the signature.
pub fn as_bytes(&self) -> &[u8] {
self.signature.as_slice()
}
/// A base64 encoding of the signature.
///
/// Uses the standard character set with no padding.
pub fn base64(&self) -> String {
Base64::<Standard, _>::new(self.signature.as_slice()).encode()
}
/// The key identifier, a string containing the signature algorithm and the key "version"
/// separated by a colon, e.g. "ed25519:1".
pub fn id(&self) -> String {
format!("{}:{}", self.algorithm, self.version)
}
/// The "version" of the key used for this signature.
///
/// Versions are used as an identifier to distinguish signatures generated from different keys
/// but using the same algorithm on the same homeserver.
pub fn version(&self) -> &str {
&self.version
}
}
#[cfg(test)]
mod tests {
use super::Signature;
#[test]
fn valid_key_id() {
Signature::new("ed25519:abcdef", &[]).unwrap();
}
#[test]
fn invalid_valid_key_id_length() {
Signature::new("ed25519:abcdef:123456", &[]).unwrap_err();
}
#[test]
fn invalid_key_id_version() {
Signature::new("ed25519:abc!def", &[]).unwrap_err();
}
#[test]
fn invalid_key_id_algorithm() {
Signature::new("foobar:abcdef", &[]).unwrap_err();
}
}