Compare commits
9 commits
b8e00c244a
...
2471cff83e
Author | SHA1 | Date | |
---|---|---|---|
2471cff83e | |||
f69fa7f370 | |||
062e441d8a | |||
77858ac8f4 | |||
f4b2514e75 | |||
e15c693421 | |||
f47bc82b19 | |||
39337f8d90 | |||
aeac22250e |
6 changed files with 64 additions and 54 deletions
|
@ -15,7 +15,7 @@ oxidetalis_core = { path = "crates/oxidetalis_core" }
|
||||||
oxidetalis_config = { path = "crates/oxidetalis_config" }
|
oxidetalis_config = { path = "crates/oxidetalis_config" }
|
||||||
oxidetalis_migrations = { path = "crates/oxidetalis_migrations" }
|
oxidetalis_migrations = { path = "crates/oxidetalis_migrations" }
|
||||||
oxidetalis_entities = { path = "crates/oxidetalis_entities" }
|
oxidetalis_entities = { path = "crates/oxidetalis_entities" }
|
||||||
# Shered dependencies
|
# Shared dependencies
|
||||||
base58 = "0.2.0"
|
base58 = "0.2.0"
|
||||||
serde = "1.0.203"
|
serde = "1.0.203"
|
||||||
thiserror = "1.0.61"
|
thiserror = "1.0.61"
|
||||||
|
|
|
@ -49,10 +49,10 @@ pub enum ApiError {
|
||||||
/// The entered public key is already registered (400 Bad Request)
|
/// The entered public key is already registered (400 Bad Request)
|
||||||
#[error("The entered public key is already registered")]
|
#[error("The entered public key is already registered")]
|
||||||
DuplicatedUser,
|
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
|
/// one in the header and other in the request body
|
||||||
/// (400 Bad Request)
|
/// (400 Bad Request)
|
||||||
#[error("TODO")]
|
#[error("You entered two different public keys")]
|
||||||
TwoDifferentKeys,
|
TwoDifferentKeys,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ pub trait DepotExt {
|
||||||
/// Returns the server configuration
|
/// Returns the server configuration
|
||||||
fn config(&self) -> &Config;
|
fn config(&self) -> &Config;
|
||||||
/// Retutns the nonce cache
|
/// Retutns the nonce cache
|
||||||
fn nonce_cache(&self) -> &NonceCache;
|
fn nonce_cache(&self) -> Arc<NonceCache>;
|
||||||
/// Returns the size of the nonce cache
|
/// Returns the size of the nonce cache
|
||||||
fn nonce_cache_size(&self) -> &usize;
|
fn nonce_cache_size(&self) -> &usize;
|
||||||
}
|
}
|
||||||
|
@ -52,9 +52,11 @@ impl DepotExt for Depot {
|
||||||
self.obtain::<Arc<Config>>().expect("Config not found")
|
self.obtain::<Arc<Config>>().expect("Config not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn nonce_cache(&self) -> &NonceCache {
|
fn nonce_cache(&self) -> Arc<NonceCache> {
|
||||||
self.obtain::<Arc<NonceCache>>()
|
Arc::clone(
|
||||||
.expect("Nonce cache not found")
|
self.obtain::<Arc<NonceCache>>()
|
||||||
|
.expect("Nonce cache not found"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn nonce_cache_size(&self) -> &usize {
|
fn nonce_cache_size(&self) -> &usize {
|
||||||
|
|
|
@ -42,20 +42,18 @@ pub async fn signature_check(
|
||||||
let mut write_err =
|
let mut write_err =
|
||||||
|message: &str, status_code| super::write_error(res, ctrl, message.to_owned(), status_code);
|
|message: &str, status_code| super::write_error(res, ctrl, message.to_owned(), status_code);
|
||||||
|
|
||||||
if req.body().is_end_stream() {
|
let data = if req.body().is_end_stream() {
|
||||||
write_err(
|
format!("{}{}", req.method(), req.uri().path())
|
||||||
"Request body is empty, the signature need a signed body",
|
} else {
|
||||||
UNAUTHORIZED,
|
match req.parse_json::<serde_json::Value>().await {
|
||||||
);
|
Ok(j) => j.to_string(),
|
||||||
return;
|
Err(err) => {
|
||||||
}
|
write_err(&err.to_string(), UNAUTHORIZED);
|
||||||
let json_body = match req.parse_json::<serde_json::Value>().await {
|
return;
|
||||||
Ok(j) => j.to_string(),
|
}
|
||||||
Err(err) => {
|
|
||||||
write_err(&err.to_string(), UNAUTHORIZED);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let signature = match utils::extract_signature(req) {
|
let signature = match utils::extract_signature(req) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(err) => {
|
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(
|
|| !utils::is_valid_signature(
|
||||||
&sender_public_key,
|
&sender_public_key,
|
||||||
&depot.config().server.private_key,
|
&depot.config().server.private_key,
|
||||||
&signature,
|
&signature,
|
||||||
json_body.as_bytes(),
|
data.as_bytes(),
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
write_err("Invalid signature", UNAUTHORIZED);
|
write_err("Invalid signature", UNAUTHORIZED);
|
||||||
|
|
|
@ -33,7 +33,6 @@ use k256::{
|
||||||
NonZeroScalar,
|
NonZeroScalar,
|
||||||
PublicKey,
|
PublicKey,
|
||||||
};
|
};
|
||||||
use logcall::logcall;
|
|
||||||
use rand::{thread_rng, RngCore};
|
use rand::{thread_rng, RngCore};
|
||||||
|
|
||||||
use crate::types::{
|
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.
|
/// Returns the public key.
|
||||||
pub fn pubkey(&self) -> CorePublicKey {
|
pub fn pubkey(&self) -> CorePublicKey {
|
||||||
CorePublicKey::try_from(
|
CorePublicKey::try_from(
|
||||||
|
@ -179,45 +202,22 @@ impl K256Secret {
|
||||||
|
|
||||||
/// Sign a data with the shared secret.
|
/// Sign a data with the shared secret.
|
||||||
///
|
///
|
||||||
/// The signature is exiplained in the OTMP specification.
|
/// The signature is explained in the OTMP specification.
|
||||||
#[logcall]
|
|
||||||
pub fn sign(&self, data: &[u8], sign_to: &CorePublicKey) -> CoreSignature {
|
pub fn sign(&self, data: &[u8], sign_to: &CorePublicKey) -> CoreSignature {
|
||||||
let mut time_and_nonce = [0u8; 24];
|
Self::sign_with_shared_secret(data, &self.shared_secret(sign_to))
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify a signature with the shared secret.
|
/// Verify the given signature with the signer.
|
||||||
///
|
///
|
||||||
/// Note:
|
/// Note:
|
||||||
/// The time and the nonce will not be checked here
|
/// The time and the nonce will not be checked here
|
||||||
#[logcall]
|
|
||||||
pub fn verify(&self, data: &[u8], signature: &CoreSignature, signer: &CorePublicKey) -> bool {
|
pub fn verify(&self, data: &[u8], signature: &CoreSignature, signer: &CorePublicKey) -> bool {
|
||||||
let mut hmac_secret = [0u8; 56];
|
signature.verify(data, &self.shared_secret(signer))
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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");
|
let mut mac = HmacSha256::new_from_slice(secret).expect("HMAC can take key of any size");
|
||||||
mac.update(data);
|
mac.update(data);
|
||||||
mac.finalize().into_bytes().into()
|
mac.finalize().into_bytes().into()
|
||||||
|
|
|
@ -31,13 +31,13 @@ use salvo_oapi::{
|
||||||
ToSchema,
|
ToSchema,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::cipher::CipherError;
|
use crate::cipher::{hmac_sha256, CipherError};
|
||||||
|
|
||||||
/// Correct length except message
|
/// Correct length except message
|
||||||
const CORRECT_LENGTH: &str = "The length is correct";
|
const CORRECT_LENGTH: &str = "The length is correct";
|
||||||
|
|
||||||
/// K256 public key
|
/// K256 public key
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
||||||
pub struct PublicKey([u8; 33]);
|
pub struct PublicKey([u8; 33]);
|
||||||
|
|
||||||
/// K256 private key
|
/// K256 private key
|
||||||
|
@ -45,7 +45,7 @@ pub struct PublicKey([u8; 33]);
|
||||||
pub struct PrivateKey([u8; 32]);
|
pub struct PrivateKey([u8; 32]);
|
||||||
|
|
||||||
/// OTMP signature
|
/// OTMP signature
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
||||||
pub struct Signature {
|
pub struct Signature {
|
||||||
hmac_output: [u8; 32],
|
hmac_output: [u8; 32],
|
||||||
timestamp: [u8; 8],
|
timestamp: [u8; 8],
|
||||||
|
@ -90,6 +90,16 @@ impl Signature {
|
||||||
sig[40..=55].copy_from_slice(&self.nonce);
|
sig[40..=55].copy_from_slice(&self.nonce);
|
||||||
sig
|
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
|
/// Public key to base58 string
|
||||||
|
|
Loading…
Reference in a new issue