Skip to content

Instantly share code, notes, and snippets.

@seungjin
Created February 28, 2023 06:39
Show Gist options
  • Save seungjin/f706bca4137d236d6076938dc779ca7a to your computer and use it in GitHub Desktop.
Save seungjin/f706bca4137d236d6076938dc779ca7a to your computer and use it in GitHub Desktop.
use base64::{engine::general_purpose, Engine as _};
use chrono::Utc;
use http::HeaderMap;
use openssl::hash::MessageDigest;
use openssl::pkey::{PKey, Private};
use openssl::sha::Sha256;
use openssl::sign::Signer;
use std::collections::HashMap;
use std::fs;
use toolbelt::http_request as hr;
pub async fn send() {
let document = read_file();
let date = Utc::now().format("%a, %d %b %Y %H:%M:%S GMT").to_string();
let mut hasher = Sha256::new();
hasher.update(document.as_bytes());
let digest = format! {"SHA-256={}",
general_purpose::STANDARD.encode(hasher.finish())
};
let signed_string = format!(
"(request-target): post /inbox\nhost: mstd.seungjin.net\ndate: {}\ndigest: {}",
date, digest
);
let signature = signature(signed_string);
/*
In ruby
header = 'keyId="https://my-example.com/actor",headers="(request-target) host date",signature="' + signature + '"'
*/
let sig_header = format!(
"keyId=\"https://ap.dev.seungjin.net/actor#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest\",signature=\"{}\"",
signature
);
/*
HTTP.headers({ 'Host': 'mastodon.social', 'Date': date, 'Signature': header })
.post('https://mastodon.social/inbox', body: document)
*/
let mut headers = HashMap::new();
headers.insert("Host".to_string(), "mstd.seungjin.net".to_string());
headers.insert("Date".to_string(), date);
headers.insert(
"Accept".to_string(),
"application/activity+json".to_string(),
);
headers.insert("Digest".to_string(), digest);
headers.insert("Signature".to_string(), sig_header);
let a = hr::post(
"https://mstd.seungjin.net/inbox".to_string(),
headers,
document,
)
.await
.unwrap();
println!("Status: {:#?}", a.status());
}
pub fn read_file() -> String {
let file_path = "./create-hello-world.json";
fs::read_to_string(file_path).expect("should have been able to read the file")
}
fn keypair() -> PKey<Private> {
// OpenSSL::PKey::RSA.new(File.read('private.pem')) in Ruby
let priv_pem = fs::read("./private.pem").unwrap();
PKey::private_key_from_pem(&priv_pem).unwrap()
}
fn signature(signed_signature: String) -> String {
let keypair = keypair();
// signature = Base64.strict_encode64(
// keypair.sign(OpenSSL::Digest::SHA256.new, signed_string)
// ) in Ruby
let mut signer = Signer::new(MessageDigest::sha256(), &keypair).unwrap();
signer.update(signed_signature.as_bytes()).unwrap();
let signature = signer.sign_to_vec().unwrap();
// Encode the signature as a base64 string
let encoded_signature = general_purpose::STANDARD.encode(&signature);
encoded_signature
}
mod test {
use super::*;
#[tokio::test]
async fn test_send() {
send().await;
}
#[test]
fn test_signature() {
signature("rasars".to_owned());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment