Compare commits

..

9 commits

Author SHA1 Message Date
2471cff83e
chore: Remove sign and verify function logs`
Signed-off-by: Awiteb <a@4rs.nl>
2024-07-04 17:38:42 +03:00
f69fa7f370
docs: Fix typos
Signed-off-by: Awiteb <a@4rs.nl>
2024-07-04 16:42:09 +03:00
062e441d8a
feat: Create signature data if there is no request body
The signature data is `Method+uri path` if there is no request body

Signed-off-by: Awiteb <a@4rs.nl>
2024-07-04 16:11:23 +03:00
77858ac8f4
feat: Create verify instance function for the Signature
Signed-off-by: Awiteb <a@4rs.nl>
2024-07-04 16:08:11 +03:00
f4b2514e75
feat: Derive Hash in PublicKey and Signature
Signed-off-by: Awiteb <a@4rs.nl>
2024-07-03 13:12:20 +03:00
e15c693421
chore: Returns nonce cache as Arc
Signed-off-by: Awiteb <a@4rs.nl>
2024-07-03 13:11:16 +03:00
f47bc82b19
feat: Derive Eq and PartialEq to Signature
Signed-off-by: Awiteb <a@4rs.nl>
2024-07-02 18:18:41 +03:00
39337f8d90
feat: Add sign_with_shared_secret function to K256Secret
Signed-off-by: Awiteb <a@4rs.nl>
2024-07-02 18:17:44 +03:00
aeac22250e
chore: Add TwoDifferentKeys error message
Signed-off-by: Awiteb <a@4rs.nl>
2024-07-02 18:15:00 +03:00
6 changed files with 64 additions and 54 deletions

View file

@ -15,7 +15,7 @@ oxidetalis_core = { path = "crates/oxidetalis_core" }
oxidetalis_config = { path = "crates/oxidetalis_config" }
oxidetalis_migrations = { path = "crates/oxidetalis_migrations" }
oxidetalis_entities = { path = "crates/oxidetalis_entities" }
# Shered dependencies
# Shared dependencies
base58 = "0.2.0"
serde = "1.0.203"
thiserror = "1.0.61"

View file

@ -49,10 +49,10 @@ pub enum ApiError {
/// The entered public key is already registered (400 Bad Request)
#[error("The entered public key is already registered")]
DuplicatedUser,
/// The user enterd tow different public keys
/// The user entered two different public keys
/// one in the header and other in the request body
/// (400 Bad Request)
#[error("TODO")]
#[error("You entered two different public keys")]
TwoDifferentKeys,
}

View file

@ -30,7 +30,7 @@ pub trait DepotExt {
/// Returns the server configuration
fn config(&self) -> &Config;
/// Retutns the nonce cache
fn nonce_cache(&self) -> &NonceCache;
fn nonce_cache(&self) -> Arc<NonceCache>;
/// Returns the size of the nonce cache
fn nonce_cache_size(&self) -> &usize;
}
@ -52,9 +52,11 @@ impl DepotExt for Depot {
self.obtain::<Arc<Config>>().expect("Config not found")
}
fn nonce_cache(&self) -> &NonceCache {
self.obtain::<Arc<NonceCache>>()
.expect("Nonce cache not found")
fn nonce_cache(&self) -> Arc<NonceCache> {
Arc::clone(
self.obtain::<Arc<NonceCache>>()
.expect("Nonce cache not found"),
)
}
fn nonce_cache_size(&self) -> &usize {

View file

@ -42,20 +42,18 @@ pub async fn signature_check(
let mut write_err =
|message: &str, status_code| super::write_error(res, ctrl, message.to_owned(), status_code);
if req.body().is_end_stream() {
write_err(
"Request body is empty, the signature need a signed body",
UNAUTHORIZED,
);
return;
}
let json_body = match req.parse_json::<serde_json::Value>().await {
Ok(j) => j.to_string(),
Err(err) => {
write_err(&err.to_string(), UNAUTHORIZED);
return;
let data = if req.body().is_end_stream() {
format!("{}{}", req.method(), req.uri().path())
} else {
match req.parse_json::<serde_json::Value>().await {
Ok(j) => j.to_string(),
Err(err) => {
write_err(&err.to_string(), UNAUTHORIZED);
return;
}
}
};
let signature = match utils::extract_signature(req) {
Ok(s) => s,
Err(err) => {
@ -72,12 +70,12 @@ pub async fn signature_check(
}
};
if !utils::is_valid_nonce(&signature, depot.nonce_cache(), depot.nonce_cache_size())
if !utils::is_valid_nonce(&signature, &depot.nonce_cache(), depot.nonce_cache_size())
|| !utils::is_valid_signature(
&sender_public_key,
&depot.config().server.private_key,
&signature,
json_body.as_bytes(),
data.as_bytes(),
)
{
write_err("Invalid signature", UNAUTHORIZED);

View file

@ -33,7 +33,6 @@ use k256::{
NonZeroScalar,
PublicKey,
};
use logcall::logcall;
use rand::{thread_rng, RngCore};
use crate::types::{
@ -106,6 +105,30 @@ impl K256Secret {
)
}
/// Sign a data with the shared secret.
///
/// The signature is exiplained in the OTMP specification.
pub fn sign_with_shared_secret(data: &[u8], shared_secret: &[u8; 32]) -> CoreSignature {
let mut time_and_nonce = [0u8; 24];
time_and_nonce[0..=7].copy_from_slice(
&SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("SystemTime before UNIX EPOCH!")
.as_secs()
.to_be_bytes(),
);
thread_rng().fill_bytes(&mut time_and_nonce[8..=23]);
let mut hmac_secret = [0u8; 56];
hmac_secret[0..=31].copy_from_slice(shared_secret);
hmac_secret[32..=55].copy_from_slice(&time_and_nonce);
let mut signature = [0u8; 56];
signature[0..=31].copy_from_slice(&hmac_sha256(data, &hmac_secret));
signature[32..=55].copy_from_slice(&time_and_nonce);
CoreSignature::from(signature)
}
/// Returns the public key.
pub fn pubkey(&self) -> CorePublicKey {
CorePublicKey::try_from(
@ -179,45 +202,22 @@ impl K256Secret {
/// Sign a data with the shared secret.
///
/// The signature is exiplained in the OTMP specification.
#[logcall]
/// The signature is explained in the OTMP specification.
pub fn sign(&self, data: &[u8], sign_to: &CorePublicKey) -> CoreSignature {
let mut time_and_nonce = [0u8; 24];
time_and_nonce[0..=7].copy_from_slice(
&SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("SystemTime before UNIX EPOCH!")
.as_secs()
.to_be_bytes(),
);
thread_rng().fill_bytes(&mut time_and_nonce[8..=23]);
let mut hmac_secret = [0u8; 56];
hmac_secret[0..=31].copy_from_slice(&self.shared_secret(sign_to));
hmac_secret[32..=55].copy_from_slice(&time_and_nonce);
let mut signature = [0u8; 56];
signature[0..=31].copy_from_slice(&hmac_sha256(data, &hmac_secret));
signature[32..=55].copy_from_slice(&time_and_nonce);
CoreSignature::from(signature)
Self::sign_with_shared_secret(data, &self.shared_secret(sign_to))
}
/// Verify a signature with the shared secret.
/// Verify the given signature with the signer.
///
/// Note:
/// The time and the nonce will not be checked here
#[logcall]
pub fn verify(&self, data: &[u8], signature: &CoreSignature, signer: &CorePublicKey) -> bool {
let mut hmac_secret = [0u8; 56];
hmac_secret[0..=31].copy_from_slice(&self.shared_secret(signer));
hmac_secret[32..=39].copy_from_slice(signature.timestamp());
hmac_secret[40..=55].copy_from_slice(signature.nonce());
&hmac_sha256(data, &hmac_secret) == signature.hmac_output()
signature.verify(data, &self.shared_secret(signer))
}
}
fn hmac_sha256(data: &[u8], secret: &[u8]) -> [u8; 32] {
/// Compute the HMAC-SHA256 of the given data with the given secret.
pub(crate) fn hmac_sha256(data: &[u8], secret: &[u8]) -> [u8; 32] {
let mut mac = HmacSha256::new_from_slice(secret).expect("HMAC can take key of any size");
mac.update(data);
mac.finalize().into_bytes().into()

View file

@ -31,13 +31,13 @@ use salvo_oapi::{
ToSchema,
};
use crate::cipher::CipherError;
use crate::cipher::{hmac_sha256, CipherError};
/// Correct length except message
const CORRECT_LENGTH: &str = "The length is correct";
/// K256 public key
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct PublicKey([u8; 33]);
/// K256 private key
@ -45,7 +45,7 @@ pub struct PublicKey([u8; 33]);
pub struct PrivateKey([u8; 32]);
/// OTMP signature
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct Signature {
hmac_output: [u8; 32],
timestamp: [u8; 8],
@ -90,6 +90,16 @@ impl Signature {
sig[40..=55].copy_from_slice(&self.nonce);
sig
}
/// Verify the signature with the given shared secret.
pub fn verify(&self, data: &[u8], shared_secret: &[u8; 32]) -> bool {
let mut hmac_secret = [0u8; 56];
hmac_secret[0..=31].copy_from_slice(shared_secret);
hmac_secret[32..=39].copy_from_slice(self.timestamp());
hmac_secret[40..=55].copy_from_slice(self.nonce());
&hmac_sha256(data, &hmac_secret) == self.hmac_output()
}
}
/// Public key to base58 string