forked from itycodes/HitLock
parent
f153a8b9c7
commit
52241b91bb
@ -0,0 +1,43 @@
|
||||
use std::{fmt::Display, path::Path};
|
||||
|
||||
pub use ureq::http::{HeaderMap, StatusCode};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Method {
|
||||
GET,
|
||||
HEAD,
|
||||
OPTIONS,
|
||||
TRACE,
|
||||
PUT,
|
||||
DELETE,
|
||||
POST,
|
||||
PATCH,
|
||||
CONNECT,
|
||||
}
|
||||
|
||||
impl Display for Method {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Method::GET => write!(f, "get"),
|
||||
Method::HEAD => write!(f, "head"),
|
||||
Method::OPTIONS => write!(f, "options"),
|
||||
Method::TRACE => write!(f, "trace"),
|
||||
Method::PUT => write!(f, "put"),
|
||||
Method::DELETE => write!(f, "delete"),
|
||||
Method::POST => write!(f, "post"),
|
||||
Method::PATCH => write!(f, "patch"),
|
||||
Method::CONNECT => write!(f, "connect"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Target<'a> {
|
||||
pub host: &'a str,
|
||||
pub path: &'a Path,
|
||||
}
|
||||
|
||||
pub struct HttpSignature {
|
||||
pub signature: String,
|
||||
pub digest: String,
|
||||
pub date: String,
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
use std::{fs, io, path::PathBuf};
|
||||
|
||||
use base64::prelude::*;
|
||||
use openssl::{
|
||||
hash::MessageDigest,
|
||||
pkey::{PKey, Private, Public},
|
||||
sign::Signer,
|
||||
};
|
||||
|
||||
pub struct Key {
|
||||
pub(crate) privkey: PKey<Private>,
|
||||
pub(crate) pubkey: PKey<Public>,
|
||||
pub(crate) key_id: String,
|
||||
}
|
||||
|
||||
impl Key {
|
||||
pub fn from_files(
|
||||
privkey_file: PathBuf,
|
||||
pubkey_file: PathBuf,
|
||||
key_id: String,
|
||||
) -> Result<Self, io::Error> {
|
||||
let privkey_file = fs::read(privkey_file)?;
|
||||
let pubkey_file = fs::read(pubkey_file)?;
|
||||
|
||||
Ok(Self::from_bytes(&privkey_file, &pubkey_file, key_id))
|
||||
}
|
||||
|
||||
pub fn from_bytes(privkey: &[u8], pubkey: &[u8], key_id: String) -> Self {
|
||||
let privkey = PKey::private_key_from_pem(privkey).expect("Failed decoding private key pem");
|
||||
let pubkey = PKey::public_key_from_pem(pubkey).expect("Failed decoding public key pem");
|
||||
|
||||
Self {
|
||||
privkey,
|
||||
pubkey,
|
||||
key_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sign(&self, bytes: &[u8]) -> String {
|
||||
let mut signer = Signer::new(MessageDigest::sha256(), &self.privkey).unwrap();
|
||||
signer.update(bytes).unwrap();
|
||||
BASE64_STANDARD.encode(signer.sign_to_vec().unwrap())
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
use std::time::SystemTime;
|
||||
|
||||
use base64::prelude::*;
|
||||
use http::{HeaderMap, HttpSignature, Method, StatusCode, Target};
|
||||
use httpdate::fmt_http_date;
|
||||
use key::Key;
|
||||
use log::debug;
|
||||
use sha2::Digest;
|
||||
|
||||
pub mod http;
|
||||
pub mod key;
|
||||
|
||||
fn sha256sum(bytes: &[u8]) -> String {
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
|
||||
hasher.update(bytes);
|
||||
|
||||
return BASE64_STANDARD.encode(hasher.finalize());
|
||||
}
|
||||
|
||||
pub fn sign_request(
|
||||
key: Key,
|
||||
Target { host, path }: Target,
|
||||
method: Method,
|
||||
body: &[u8],
|
||||
) -> HttpSignature {
|
||||
let date = fmt_http_date(SystemTime::now());
|
||||
|
||||
let digest = format!("SHA-256={}", sha256sum(body));
|
||||
debug!("digest: {}", digest);
|
||||
|
||||
let mut headers = String::new();
|
||||
headers.push_str(format!("(request-target): {} {}\n", method, path.display()).as_str());
|
||||
headers.push_str(format!("host: {}\n", host).as_str());
|
||||
headers.push_str(format!("date: {}\n", date).as_str());
|
||||
headers.push_str(format!("digest: {}", digest).as_str());
|
||||
|
||||
debug!("signed_string: {:?}", headers);
|
||||
|
||||
let signature = key.sign(headers.as_bytes());
|
||||
|
||||
let header = format!(
|
||||
"keyId=\"{}\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest\",signature=\"{}\"",
|
||||
key.key_id, signature
|
||||
);
|
||||
|
||||
debug!("header: {}", header);
|
||||
|
||||
HttpSignature {
|
||||
signature: header,
|
||||
digest,
|
||||
date,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn signed_post(
|
||||
key: Key,
|
||||
target @ Target { host, path }: Target,
|
||||
mut extra_headers: HeaderMap,
|
||||
body: &[u8],
|
||||
) -> (StatusCode, Vec<u8>) {
|
||||
let HttpSignature {
|
||||
signature,
|
||||
digest,
|
||||
date,
|
||||
} = sign_request(key, target, Method::POST, body);
|
||||
|
||||
let url = format!("https://{}{}", host, path.display());
|
||||
|
||||
let mut req = ureq::post(url);
|
||||
{
|
||||
let headers = req.headers_mut().unwrap();
|
||||
headers.extend(extra_headers.drain());
|
||||
}
|
||||
let response = req
|
||||
.header("Signature", signature)
|
||||
.header("host", host)
|
||||
.header("date", date)
|
||||
.header("digest", digest)
|
||||
.send(body)
|
||||
.expect("Failed to send request");
|
||||
|
||||
(
|
||||
response.status(),
|
||||
response
|
||||
.into_body()
|
||||
.read_to_vec()
|
||||
.expect("Failed to read the response as bytes"),
|
||||
)
|
||||
}
|
@ -1,86 +1,37 @@
|
||||
use base64::prelude::*;
|
||||
use httpdate::fmt_http_date;
|
||||
use openssl::hash::MessageDigest;
|
||||
use openssl::pkey::PKey;
|
||||
use openssl::sign::Signer;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::Digest;
|
||||
use std::time::SystemTime;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct CreateActivity {
|
||||
street: String,
|
||||
city: String,
|
||||
}
|
||||
|
||||
fn sha256sum(bytes: &[u8]) -> String {
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
|
||||
hasher.update(bytes);
|
||||
|
||||
return BASE64_STANDARD.encode(hasher.finalize());
|
||||
}
|
||||
use log::info;
|
||||
use noteposter::{http::Target, key::Key, signed_post};
|
||||
use std::path::Path;
|
||||
use ureq::http::{HeaderMap, HeaderValue, header::CONTENT_TYPE};
|
||||
|
||||
fn main() {
|
||||
let host = "estradiol.city";
|
||||
let path = "/users/ity/inbox";
|
||||
|
||||
let url = format!("https://{}{}", host, path);
|
||||
let path = Path::new("/users/ity/inbox");
|
||||
|
||||
let key_id = "https://testing4host.com/users/test71/#main-key";
|
||||
|
||||
let json = include_bytes!("note.json");
|
||||
|
||||
let digest = format!("SHA-256={}", sha256sum(json));
|
||||
println!("digest: {}", digest);
|
||||
|
||||
let date = fmt_http_date(SystemTime::now());
|
||||
|
||||
let mut headers = String::new();
|
||||
headers.push_str(format!("(request-target): post {}\n", path).as_str());
|
||||
headers.push_str(format!("host: {}\n", host).as_str());
|
||||
headers.push_str(format!("date: {}\n", date).as_str());
|
||||
headers.push_str(format!("digest: {}", digest).as_str());
|
||||
|
||||
println!("signed_string: {:?}", headers);
|
||||
|
||||
let privkey_file = include_str!("private.pem");
|
||||
let pubkey_file = include_str!("public.pem");
|
||||
|
||||
let privkey =
|
||||
PKey::private_key_from_pem(privkey_file.as_bytes()).expect("Failed decoding private.pem");
|
||||
let _pubkey =
|
||||
PKey::public_key_from_pem(pubkey_file.as_bytes()).expect("Failed decoding public.pem");
|
||||
|
||||
let mut signer = Signer::new(MessageDigest::sha256(), &privkey).unwrap();
|
||||
signer.update(headers.as_bytes()).unwrap();
|
||||
let signature = BASE64_STANDARD.encode(signer.sign_to_vec().unwrap());
|
||||
|
||||
let header = format!(
|
||||
"keyId=\"{}\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest\",signature=\"{}\"",
|
||||
key_id, signature
|
||||
let key = Key::from_files(
|
||||
"private.pem".into(),
|
||||
"public.pem".into(),
|
||||
key_id.to_string(),
|
||||
)
|
||||
.expect("Failed to read key files");
|
||||
|
||||
let (_status, res) = signed_post(
|
||||
key,
|
||||
Target { host, path },
|
||||
{
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
CONTENT_TYPE,
|
||||
HeaderValue::from_static("application/activity+json"),
|
||||
);
|
||||
headers
|
||||
},
|
||||
include_bytes!("note.json"),
|
||||
);
|
||||
|
||||
println!("header: {}", header);
|
||||
|
||||
let response = ureq::post(url)
|
||||
.header("Content-Type", "application/activity+json")
|
||||
.header("Signature", header)
|
||||
.header("host", host)
|
||||
.header("date", date)
|
||||
.header("digest", digest)
|
||||
.send(json);
|
||||
|
||||
match response {
|
||||
Err(v) => println!("{:#?}", v),
|
||||
Ok(v) => {
|
||||
println!("Status: {}", v.status());
|
||||
println!(
|
||||
info!(
|
||||
"Response: {}",
|
||||
v.into_body()
|
||||
.read_to_string()
|
||||
.expect("Invalid response: decode error")
|
||||
String::from_utf8(res).expect("Invalid response: decode error")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in new issue