Skip to content

Commit

Permalink
feat(signature-validation): add ed25519 signature validation to twili…
Browse files Browse the repository at this point in the history
…ght-util
  • Loading branch information
Monadic-Cat committed Jan 19, 2024
1 parent cb6d47d commit f0520c1
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 1 deletion.
7 changes: 6 additions & 1 deletion twilight-util/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ version = "0.15.4"
twilight-model = { default-features = false, optional = true, path = "../twilight-model", version = "0.15.4" }
twilight-validate = { default-features = false, optional = true, path = "../twilight-validate", version = "0.15.3" }

# Signature validation
ed25519-dalek = { version = "2.0.0-rc.2", optional = true, default-features = false}
hex = { version = "0.4.3", optional = true, default-features = false }

[dev-dependencies]
chrono = { default-features = false, features = ["std"], version = "0.4" }
static_assertions = { default-features = false, version = "1" }
Expand All @@ -26,7 +30,8 @@ builder = ["dep:twilight-model", "dep:twilight-validate"]
link = ["dep:twilight-model"]
permission-calculator = ["dep:twilight-model"]
snowflake = ["dep:twilight-model"]
full = ["builder", "link", "permission-calculator", "snowflake"]
signature-validation = ["dep:ed25519-dalek", "dep:hex"]
full = ["builder", "link", "permission-calculator", "snowflake", "signature-validation"]

[package.metadata.docs.rs]
all-features = true
Expand Down
3 changes: 3 additions & 0 deletions twilight-util/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ pub mod permission_calculator;

#[cfg(feature = "snowflake")]
pub mod snowflake;

#[cfg(feature = "signature-validation")]
pub mod signature_validation;
64 changes: 64 additions & 0 deletions twilight-util/src/signature_validation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//! Provides signature validation as is required for bots which work by giving Discord
//! an HTTPS endpoint to send Interactions to.
use ed25519_dalek::{Signature, SignatureError, VerifyingKey};

#[derive(Debug)]
pub struct FromHexError(hex::FromHexError);
#[derive(Debug)]
pub struct SigError(SignatureError);

#[derive(Debug)]
pub enum SignatureValidationFailure {
Hex(FromHexError),
InvalidSignature(SigError),
}
impl std::fmt::Display for SignatureValidationFailure {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl std::error::Error for SignatureValidationFailure {}

#[derive(Debug)]
pub enum KeyError {
Hex(FromHexError),
Sig(SignatureError),
}
impl std::fmt::Display for KeyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl std::error::Error for KeyError {}


pub struct Key(VerifyingKey);
impl Key {
fn from_hex(pub_key: &[u8]) -> Result<Self, KeyError> {
let mut key = [0; 32];
hex::decode_to_slice(pub_key, &mut key).map_err(|e| KeyError::Hex(FromHexError(e)))?;
VerifyingKey::from_bytes(&key)
.map(|key| Self(key))
.map_err(|e| KeyError::Sig(e))
}
}

pub const SIGNATURE_HEADER: &str = "x-signature-ed25519";
pub const TIMESTAMP_HEADER: &str = "x-signature-timestamp";

/// Validates that a signature is valid for a given message body, timestamp, and signing key.
pub fn check_signature(sig: &[u8], timestamp: &[u8], body: &[u8], key: &Key) -> Result<(), SignatureValidationFailure> {
let mut sig_buf = [0; 64];
hex::decode_to_slice(sig, &mut sig_buf).map_err(|e| SignatureValidationFailure::Hex(FromHexError(e)))?;
let sig = Signature::from_bytes(&sig_buf);


let mut buf = Vec::with_capacity(timestamp.len() + body.len());
buf.extend_from_slice(timestamp);
buf.extend_from_slice(body);
match key.0.verify_strict(&buf, &sig) {
Ok(()) => Ok(()),
Err(e) => Err(SignatureValidationFailure::InvalidSignature(SigError(e))),
}
}

0 comments on commit f0520c1

Please sign in to comment.