-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
hyper-request.rs
199 lines (170 loc) · 7.38 KB
/
hyper-request.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
use http::Request;
use http_body_util::Full;
use httpsig_hyper::{prelude::*, *};
type BoxBody = http_body_util::combinators::BoxBody<bytes::Bytes, HyperDigestError>;
type SignatureName = String;
const EDDSA_SECRET_KEY: &str = r##"-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIDSHAE++q1BP7T8tk+mJtS+hLf81B0o6CFyWgucDFN/C
-----END PRIVATE KEY-----
"##;
const EDDSA_PUBLIC_KEY: &str = r##"-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA1ixMQcxO46PLlgQfYS46ivFd+n0CcDHSKUnuhm3i1O0=
-----END PUBLIC KEY-----
"##;
const HMACSHA256_SECRET_KEY: &str =
r##"uzvJfB4u3N0Jy4T7NZ75MDVcr8zSTInedJtkgcu46YW4XByzNJjxBdtjUkdJPBtbmHhIDi6pcl8jsasjlTMtDQ=="##;
const COVERED_COMPONENTS: &[&str] = &["@method", "date", "content-type", "content-digest"];
async fn build_request() -> Request<BoxBody> {
let body = Full::new(&b"{\"hello\": \"world\"}"[..]);
let req = Request::builder()
.method("GET")
.uri("https://example.com/parameters?var=this%20is%20a%20big%0Amultiline%20value&bar=with+plus+whitespace&fa%C3%A7ade%22%3A%20=something")
.header("date", "Sun, 09 May 2021 18:30:00 GMT")
.header("content-type", "application/json")
.header("content-type", "application/json-patch+json")
.body(body)
.unwrap();
req.set_content_digest(&ContentDigestType::Sha256).await.unwrap()
}
/// Sender function that generates a request with a signature
async fn sender_ed25519(req: &mut Request<BoxBody>) {
println!("Signing with ED25519 with key id");
// build signature params that indicates objects to be signed
let covered_components = COVERED_COMPONENTS
.iter()
.map(|v| message_component::HttpMessageComponentId::try_from(*v))
.collect::<Result<Vec<_>, _>>()
.unwrap();
let mut signature_params = HttpSignatureParams::try_new(&covered_components).unwrap();
// set signing/verifying key information, alg and keyid with ed25519
let secret_key = SecretKey::from_pem(EDDSA_SECRET_KEY).unwrap();
signature_params.set_key_info(&secret_key);
// set signature with custom signature name
req
.set_message_signature(&signature_params, &secret_key, Some("siged25519"))
.await
.unwrap();
}
/// Sender function that generates a request with a signature
async fn sender_hs256(req: &mut Request<BoxBody>) {
println!("Signing with HS256 with key id and random nonce");
// build signature params that indicates objects to be signed
let covered_components = COVERED_COMPONENTS
.iter()
.map(|v| message_component::HttpMessageComponentId::try_from(*v))
.collect::<Result<Vec<_>, _>>()
.unwrap();
let mut signature_params = HttpSignatureParams::try_new(&covered_components).unwrap();
// set signing/verifying key information, alg and keyid and random noce with hmac-sha256
let shared_key = SharedKey::from_base64(HMACSHA256_SECRET_KEY).unwrap();
signature_params.set_key_info(&shared_key);
signature_params.set_random_nonce();
req
.set_message_signature(&signature_params, &shared_key, Some("sighs256"))
.await
.unwrap();
}
/// Receiver function that verifies a request with a signature of ed25519
async fn receiver_ed25519<B>(req: &Request<B>) -> HyperSigResult<SignatureName>
where
B: http_body::Body + Send + Sync,
{
println!("Verifying ED25519 signature");
let public_key = PublicKey::from_pem(EDDSA_PUBLIC_KEY).unwrap();
let key_id = public_key.key_id();
// verify signature with checking key_id
req.verify_message_signature(&public_key, Some(&key_id)).await
}
/// Receiver function that verifies a request with a signature of hmac-sha256
async fn receiver_hmac_sha256<B>(req: &Request<B>) -> HyperSigResult<SignatureName>
where
B: http_body::Body + Send + Sync,
{
println!("Verifying HMAC-SHA256 signature");
let shared_key = SharedKey::from_base64(HMACSHA256_SECRET_KEY).unwrap();
let key_id = VerifyingKey::key_id(&shared_key);
// verify signature with checking key_id
req.verify_message_signature(&shared_key, Some(&key_id)).await
}
async fn scenario_multiple_signatures() {
println!("-------------- Scenario: Multiple signatures --------------");
let mut request_from_sender = build_request().await;
println!("Request header before signing:\n{:#?}", request_from_sender.headers());
// sender signs a signature of ed25519 and hmac-sha256
sender_ed25519(&mut request_from_sender).await;
sender_hs256(&mut request_from_sender).await;
println!(
"Request header separately signed by ED25519 and HS256:\n{:#?}",
request_from_sender.headers()
);
let signature_inputs = request_from_sender
.headers()
.get_all("signature-input")
.iter()
.map(|v| v.to_str())
.collect::<Result<Vec<_>, _>>()
.unwrap();
let signatures = request_from_sender
.headers()
.get_all("signature")
.iter()
.map(|v| v.to_str())
.collect::<Result<Vec<_>, _>>()
.unwrap();
assert!(signature_inputs.iter().any(|v| v.starts_with(r##"siged25519=("##)));
assert!(signature_inputs.iter().any(|v| v.starts_with(r##"sighs256=("##)));
assert!(signatures.iter().any(|v| v.starts_with(r##"siged25519=:"##)));
assert!(signatures.iter().any(|v| v.starts_with(r##"sighs256=:"##)));
// receiver verifies the request with signatures
// every signature is independent and verified separately
let verification_res_ed25519 = receiver_ed25519(&request_from_sender).await;
assert!(verification_res_ed25519.is_ok());
println!("ED25519 signature is verified");
let verification_res_hs256 = receiver_hmac_sha256(&request_from_sender).await;
assert!(verification_res_hs256.is_ok());
println!("HMAC-SHA256 signature is verified");
// if needed, content-digest can be verified separately
let verified_request = request_from_sender.verify_content_digest().await;
assert!(verified_request.is_ok());
println!("Content-Digest header is verified");
}
async fn scenario_single_signature_ed25519() {
println!("-------------- Scenario: Single signature with Ed25519 --------------");
let mut request_from_sender = build_request().await;
println!("Request header before signing:\n{:#?}", request_from_sender.headers());
// sender signs a signature of ed25519
sender_ed25519(&mut request_from_sender).await;
println!("Request header signed by ED25519:\n{:#?}", request_from_sender.headers());
let signature_inputs = request_from_sender
.headers()
.get_all("signature-input")
.iter()
.map(|v| v.to_str())
.collect::<Result<Vec<_>, _>>()
.unwrap();
let signatures = request_from_sender
.headers()
.get_all("signature")
.iter()
.map(|v| v.to_str())
.collect::<Result<Vec<_>, _>>()
.unwrap();
assert!(signature_inputs.iter().any(|v| v.starts_with(r##"siged25519=("##)));
assert!(signatures.iter().any(|v| v.starts_with(r##"siged25519=:"##)));
// receiver verifies the request with signatures
// every signature is independent and verified separately
let verification_res_ed25519 = receiver_ed25519(&request_from_sender).await;
assert!(verification_res_ed25519.is_ok());
println!("ED25519 signature is verified");
// if needed, content-digest can be verified separately
let verified_request = request_from_sender.verify_content_digest().await;
assert!(verified_request.is_ok());
println!("Content-Digest header is verified");
}
#[tokio::main]
async fn main() {
scenario_single_signature_ed25519().await;
println!("-------------------------------------------------------------");
scenario_multiple_signatures().await;
println!("-------------------------------------------------------------");
}