3 * _| || |_/ ___| ___ _ __ _ __ ___ | |
4 * |_ .. _\___ \ / _ \ '_ \| '_ \ / _ \| |
5 * |_ _|___) | __/ |_) | |_) | (_) |_|
6 * |_||_| |____/ \___| .__/| .__/ \___/(_)
13 * Copyright (C) The #Seppo contributors. All rights reserved.
15 * This program is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation, either version 3 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
33 Mirage_crypto_rng_lwt.initialize
(module Mirage_crypto_rng.Fortuna
);
34 Unix.chdir
"../../../test/"
38 Http.relpa
"a/b/" "a/b/d/e" |> Assrt.equals_string __LOC__
"d/e";
39 Http.relpa
"a/B/" "a/b/d/e" |> Assrt.equals_string __LOC__
"";
40 let base = Uri.of_string
"a/b/" in
41 Uri.resolve
"" base (Uri.of_string
"c/d/") |> Uri.to_string
|> check
string __LOC__
"a/b/c/d/";
42 Uri.resolve
"" base ((Http.relpa
(base |> Uri.to_string
) ("a/b/c/d/")) |> Uri.of_string
) |> Uri.to_string
|> check
string __LOC__
"a/b/c/d/";
46 let base = "https://example.com:443/a/b/c?d=e#f" |> Uri.of_string
in
49 |> Assrt.equals_string __LOC__
"/a/b/c";
51 "../i.j" |> Uri.of_string
|> Http.reso ~
base |> Uri.to_string
52 |> Assrt.equals_string __LOC__
"https://example.com:443/a/i.j";
54 let re = "https://example.com:443/a/b/C/d.e#ff" |> Uri.of_string
|> Http.abs_to_rel ~
base in
55 re |> Uri.to_string
|> Assrt.equals_string __LOC__
"C/d.e#ff";
57 Uri.make ~path
:"." () |> Http.reso ~
base:Uri.empty
|> Uri.to_string
|> Assrt.equals_string __LOC__
"";
59 let a = Uri.of_string
"https://example.com/path" in
60 assert ("https://example.com/path" |> Uri.of_string
|> Uri.equal
a);
61 assert ("https://EXAMPLE.com/path" |> Uri.of_string
|> Uri.equal
a);
65 "/seppo.cgi" |> Cgi.cd_cgi_bin_twin_path
|> Assrt.equals_string __LOC__
".";
66 "/sub/dir/seppo.cgi" |> Cgi.cd_cgi_bin_twin_path
|> Assrt.equals_string __LOC__
".";
67 "/cgi-bin/seppo.cgi" |> Cgi.cd_cgi_bin_twin_path
|> Assrt.equals_string __LOC__
"..";
68 "/cgi-bin/sub/dir/seppo.cgi" |> Cgi. cd_cgi_bin_twin_path
|> Assrt.equals_string __LOC__
"../../../sub/dir"
70 module Request
= struct
71 let tc_rx_script_name () =
73 if Str.string_match
Cgi.Request.rx_script_name fn
0
75 fn
|> Str.matched_group
5 |> Assrt.equals_string __LOC__ n
;
76 fn
|> Str.matched_group
3 )
79 "/seppo.cgi" |> chk "seppo.cgi" |> Assrt.equals_string __LOC__
"/";
80 "/cgi-bin/seppo.cgi" |> chk "seppo.cgi" |> Assrt.equals_string __LOC__
"/";
81 "/cgi-bin/uhu/seppo.cgi" |> chk "seppo.cgi" |> Assrt.equals_string __LOC__
"/uhu/";
82 "/apchk.cgi" |> chk "apchk.cgi" |> Assrt.equals_string __LOC__
"/";
83 "/cgi-bin/uhu/apchk.cgi" |> chk "apchk.cgi" |> Assrt.equals_string __LOC__
"/uhu/";
87 let r : Cgi.Request.t
= {
88 content_type
= "text/plain";
89 content_length
= None
;
92 path_info
= "/shaarli";
93 query_string
= "post=uhu";
94 request_method
= "GET";
95 remote_addr
= "127.0.0.1";
97 script_name
= "/sub/seppo.cgi";
99 raw_string
= Sys.getenv_opt
101 r |> Cgi.Request.abs
|> Uri.to_string
|> Assrt.equals_string __LOC__
"https://example.com/sub/seppo.cgi/shaarli?post=uhu";
102 r |> Cgi.Request.path_and_query
|> Uri.to_string
|> Assrt.equals_string __LOC__
"/sub/seppo.cgi/shaarli?post=uhu";
103 "a" |> Assrt.equals_string __LOC__
"a";
107 Uri.make ~scheme
:"https" ~host
:"example.com" ()
108 |> Cgi.Request.base'
"/seppo.cgi"
110 |> Assrt.equals_string __LOC__
"https://example.com/";
111 Uri.make ~scheme
:"https" ~host
:"example.com" ()
112 |> Cgi.Request.base'
"/a/b/seppo.cgi"
114 |> Assrt.equals_string __LOC__
"https://example.com/a/b/";
115 let r : Cgi.Request.t
= {
116 content_type
= "text/plain";
117 content_length
= None
;
118 host
= "example.com";
120 path_info
= "/shaarli";
121 query_string
= "post=uhu";
122 request_method
= "GET";
123 remote_addr
= "127.0.0.1";
125 script_name
= "/cgi-bin/sub/seppo.cgi";
127 raw_string
= Sys.getenv_opt
129 r |> Cgi.Request.base
131 |> Assrt.equals_string __LOC__
"https://example.com/sub/";
132 {r with script_name
= "/sib/seppo.cgi"}
135 |> Assrt.equals_string __LOC__
"https://example.com/sib/";
136 {r with script_name
= "/seppo.cgi"}
139 |> Assrt.equals_string __LOC__
"https://example.com/";
142 let tc_query_string () =
143 match "" |> Uri.query_of_encoded
with
145 | _
-> "no" |> Assrt.equals_string __LOC__
""
148 module Cookie
= struct
150 let s = "Thu, 01 Jan 1970 00:00:00 GMT" in
151 Ptime.epoch
|> Http.to_rfc1123
|> Assrt.equals_string __LOC__
s;
154 let tc_to_string () =
155 let http_only = Some
true
156 and path
= Some
"seppo.cgi"
157 and same_site
= Some `Strict
158 and max_age
= Some
(30. *. 60.)
159 and secure
= Some
true in
160 Cookie.to_string ?path ?secure ?
http_only ?same_site
("auth_until", "2022-04-08T22:30:07Z")
161 |> Assrt.equals_string __LOC__
162 "auth_until=2022-04-08T22:30:07Z; Path=seppo.cgi; Secure; HttpOnly; \
164 Cookie.to_string ?max_age ?path ?secure ?
http_only ?same_site
("auth", "yes")
165 |> Assrt.equals_string __LOC__
166 "auth=yes; Max-Age=1800; Path=seppo.cgi; Secure; HttpOnly; \
170 let tc_of_string () =
171 let c = Cookie.to_string
("#Seppo!", "foo") in
172 c |> Assrt.equals_string __LOC__
"#Seppo!=foo";
173 let v = match c |> Cookie.of_string
with
174 | ("#Seppo!", v) :: [] -> v
177 v |> Assrt.equals_string __LOC__
"foo";
182 module Header
= struct
184 Logr.info
(fun m
-> m
"http_test.test_headers");
185 let h = [ ("A", "a"); ("B", "b") ] @ [ ("C", "c") ]
186 |> Cohttp.Header.of_list
in
187 h |> Cohttp.Header.to_string
188 |> Assrt.equals_string __LOC__
"A: a\r\nB: b\r\nC: c\r\n\r\n";
189 h |> Cohttp.Header.to_frames
190 |> String.concat
"\n"
191 |> Assrt.equals_string __LOC__
"A: a\nB: b\nC: c";
192 Cohttp.Header.get
h "a"
193 |> Option.value ~default
:"-"
194 |> Assrt.equals_string __LOC__
"a";
197 let tc_sig_encode () =
199 "k2","v2"; ] |> Http.Signature.encode
200 |> check
string __LOC__
{|k1
="v1",k2
="v2"|};
202 |> Cohttp.Code.string_of_method
203 |> Astring.String.map
Astring.Char.Ascii.lowercase
204 |> check
string __LOC__
"get"
206 let tc_signature () =
207 Logr.info
(fun m
-> m
"http_test.test_signature");
208 let si = {|keyId
="Test",algorithm
="rsa-sha256",headers
="(request-target) host date",signature
="qdx+H7PHHDZgy4y/Ahn9Tny9V3GP6YgBPyUXMmoxWtLbHpUnXS2mg2+SbrQDMCJypxBLSPQR2aAjn7ndmw2iicw3HMbe8VfEdKFYRqzic+efkb3nndiv/x1xSHDJWeSWkx3ButlYSuBskLu6kd9Fswtemr3lgdDEmn04swr2Os0="|} in
210 |> Http.Signature.decode
212 si'
|> List.length
|> Assrt.equals_int __LOC__
4;
214 |> Tyre.eval
Http.Signature.P.list_auth_param
215 |> check
string __LOC__
si
217 let priv_key_cavage =
218 {|-----BEGIN RSA PRIVATE KEY
-----
219 MIICXgIBAAKBgQDCFENGw33yGihy92pDjZQhl0C36rPJj
+CvfSC8
+q28hxA161QF
220 NUd13wuCTUcq0Qd2qsBe
/2hFyc2DCJJg0h1L78
+6Z4UMR7EOcpfdUE9Hf3m
/hs
+F
221 UR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc
+2xjJwoYi
+1hqp1fIekaxsyQIDAQAB
222 AoGBAJR8ZkCUvx5kzv
+utdl7T5MnordT1TvoXXJGXK7ZZ
+UuvMNUCdN2QPc4sBiA
223 QWvLw1cSKt5DsKZ8UETpYPy8pPYnnDEz2dDYiaew9
+xEpubyeW2oH4Zx71wqBtOK
224 kqwrXa
/pzdpiucRRjk6vE6YY7EBBs
/g7uanVpGibOVAEsqH1AkEA7DkjVH28WDUg
225 f1nqvfn2Kj6CT7nIcE3jGJsZZ7zlZmBmHFDONMLUrXR
/Zm3pR5m0tCmBqa5RK95u
226 412jt1dPIwJBANJT3v8pnkth48bQo
/fKel6uEYyboRtA5
/uHuHkZ6FQF7OUkGogc
227 mSJluOdc5t6hI1VsLn0QZEjQZMEOWr
+wKSMCQQCC4kXJEsHAve77oP6HtG
/IiEn7
228 kpyUXRNvFsDE0czpJJBvL
/aRFUJxuRK91jhjC68sA7NsKMGg5OXb5I5Jj36xAkEA
229 gIT7aFOYBFwGgQAQkWNKLvySgKbAZRTeLBacpHMuQdl1DfdntvAyqpAZ0lY0RKmW
230 G6aFKaqQfOXKCyWoUiVknQJAXrlgySFci
/2ueKlIE
1QqIiLSZ
8V
8OlpFLRnb
1pzI
231 7U1yQXnTAEFYM
560yJlzUpOb
1V
4cScGd
365tiSMvxLOvTA
==
232 -----END RSA PRIVATE KEY
-----
235 |> X509.Private_key.decode_pem
|> Result.get_ok
238 {|-----BEGIN PUBLIC KEY
-----
239 MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCFENGw33yGihy92pDjZQhl0C3
240 6rPJj
+CvfSC8
+q28hxA161QFNUd13wuCTUcq0Qd2qsBe
/2hFyc2DCJJg0h1L78
+6
241 Z4UMR7EOcpfdUE9Hf3m
/hs
+FUR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc
+2xjJw
242 oYi
+1hqp1fIekaxsyQIDAQAB
243 -----END PUBLIC KEY
-----
246 |> X509.Public_key.decode_pem
|> Result.get_ok
249 `GET
|> Cohttp.Code.string_of_method
|> check
string __LOC__
"GET";
250 let n,s = ["date","today";
251 "digest","SHA256=0815"]
252 |> Http.Signature.to_sign_string
254 ("https://example.com/uhu?foo=bar#baz" |> Uri.of_string
) in
255 n |> check
string __LOC__
"(request-target) host date digest";
256 s |> check
string __LOC__
"(request-target): get /uhu?foo=bar\nhost: example.com\ndate: today\ndigest: SHA256=0815";
257 ["host","example.com";
259 "digest","SHA256=0815"]
260 |> Http.Signature.add
263 ("https://example.com/uhu?foo=bar#baz" |> Uri.of_string
)
265 |> Cohttp.Header.of_list
266 |> Cohttp.Header.to_frames
|> Astring.String.concat ~sep
:"\n"
267 |> check
string __LOC__
{|host
: example
.com
270 signature
: algorithm
="rsa-sha256",headers
="(request-target) host date digest",signature
="AFq6XChsi63zuCVVzeVigx7BV/HzHnsg304i9uqJ44t2QufQ4WvYS1jDh2B539B3VyBQiuXoiNrSssMoShVORmZzA1y4dnnFlYncFdQRsRDRA//E2YB39ECSby0Fl6pBK+Ws/090RWcxFxTBFsD0H9JQuVASbBCDxy2lhHTFugg="|}
272 let tc_to_sign_string_basic () =
274 let uri = Uri.of_string
"/foo?param=value&pet=dog" in
276 "host", "example.com" ;
277 "date", "Sun, 05 Jan 2014 21:31:40 GMT" ;
280 |> Http.Signature.to_sign_string0 ~request
:(Some
(`POST
,uri))
281 |> Assrt.equals_string __LOC__
282 {|(request
-target
): post
/foo?param
=value&pet
=dog
284 date
: Sun
, 05 Jan
2014 21:31:40 GMT
|};
288 * https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12#appendix-C.2
290 let tc_sign_basic () =
291 Logr.info
(fun m
-> m
"http_test.test_sign_basic");
292 let pk = priv_key_cavage in
294 let sig_ = "qdx+H7PHHDZgy4y/Ahn9Tny9V3GP6YgBPyUXMmoxWtLbHpUnXS2mg2+SbrQDMCJypxBLSPQR2aAjn7ndmw2iicw3HMbe8VfEdKFYRqzic+efkb3nndiv/x1xSHDJWeSWkx3ButlYSuBskLu6kd9Fswtemr3lgdDEmn04swr2Os0="
295 and uri = Uri.of_string
"/foo?param=value&pet=dog"
297 "host", "example.com" ;
298 "date", "Sun, 05 Jan 2014 21:31:40 GMT" ;
299 ] |> Header.of_list
in
300 let s = h |> Http.Signature.to_sign_string0 ~request
:(Some
(`POST
,uri)) in
301 s |> Assrt.equals_string __LOC__
302 "(request-target): post /foo?param=value&pet=dog\n\
304 date: Sun, 05 Jan 2014 21:31:40 GMT";
305 let al,si = s |> Cstruct.of_string
|> Ap.PubKeyPem.sign
pk in
306 al |> Assrt.equals_string __LOC__
"rsa-sha256";
307 si |> Cstruct.to_string
|> Base64.encode_exn
|> Assrt.equals_string __LOC__
sig_;
309 Logr.info
(fun m
-> m
"http_test.test_sign_basic II");
310 let pub = pub_key_cavage in
311 (match Ap.PubKeyPem.verify ~algo
:"rsa-sha256" ~inbox
:Uri.empty ~key
:pub ~signature
:si (s |> Cstruct.of_string
) with
312 | Error `Msg e
-> e
|> Assrt.equals_string __LOC__
""
313 | Ok _
-> "ha!" |> Assrt.equals_string __LOC__
"ha!");
317 * https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12#appendix-C.3
320 let tc_sign_all_headers () =
321 Logr.info
(fun m
-> m
"http_test.test_sign_all_headers");
324 ("(request-target)", "post /foo?param=value&pet=dog");
325 ("(created)", "1402170695");
326 ("(expires)", "1402170699");
327 ("host", "example.com");
328 ("date", "Sun, 05 Jan 2014 21:31:40 GMT");
329 ("content-type", "application/json");
330 ("digest", "SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=");
331 ("content-length", "18");
332 ] |> Header.of_list
in
335 |> String.concat
"\n"
336 |> Assrt.equals_string __LOC__
337 "(request-target): post /foo?param=value&pet=dog\n\
338 (created): 1402170695\n\
339 (expires): 1402170699\n\
341 date: Sun, 05 Jan 2014 21:31:40 GMT\n\
342 content-type: application/json\n\
343 digest: SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=\n\
346 let pk = priv_key_cavage in
349 |> String.concat
"\n"
351 |> Ap.PubKeyPem.sign
pk
353 (* |> Assrt.equals_string __LOC__
354 "vSdrb+dS3EceC9bcwHSo4MlyKS59iFIrhgYkz8+oVLEEzmYZZvRs8rgOp+63LEM3v+MFHB32NfpB2bEKBIvB1q52LaEUHFv120V01IL+TAD48XaERZFukWgHoBTLMhYS2Gb51gWxpeIq8knRmPnYePbF5MOkR0Zkly4zKH7s1dE="
356 al |> Assrt.equals_string __LOC__
"rsa-sha256";
357 si |> Cstruct.to_string
|> Base64.encode_exn
|> Assrt.equals_string __LOC__
358 "nAkCW0wg9AbbStQRLi8fsS1mPPnA6S5+/0alANcoDFG9hG0bJ8NnMRcB1Sz1eccNMzzLEke7nGXqoiJYZFfT81oaRqh/MNFwQVX4OZvTLZ5xVZQuchRkOSO7b2QX0aFWFOUq6dnwAyliHrp6w3FOxwkGGJPaerw2lOYLdC/Bejk="
360 let tc_signed_headers () =
361 Logr.info
(fun m
-> m
"http_test.test_signed_headers");
364 https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12#appendix-C.3
366 let id = Uri.of_string
"https://example.com/actor/"
367 and dgst
= Some
"SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE="
368 and date
,_
,_
= Ptime.of_rfc3339
"2014-01-05T22:31:40+01:00" |> Result.get_ok
369 and uri = Uri.of_string
"https://example.com/foo?param=value&pet=dog" in
370 let key_id = Uri.with_fragment
id (Some
"main-key")
371 and pk = priv_key_cavage in
372 Http.signed_headers
(key_id,pk,date
) dgst
uri
374 |> String.concat
"\n"
375 |> Assrt.equals_string __LOC__
376 "host: example.com\n\
377 date: Sun, 05 Jan 2014 21:31:40 GMT\n\
378 digest: SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=\n\
380 keyId=\"https://example.com/actor/#main-key\",\
381 algorithm=\"rsa-sha256\",\
382 headers=\"(request-target) host date digest\",\
383 signature=\"WC34OEWXgO0viIZAu5qnBcKj5nOMlgjs0ASxgJPYX9x1VtKrYRRhAosH7ixFnkJneSHGn8yY9lowNvbdBg+ZsINx6P0e1WyB0YJbwsREYKYpG1sjwS3R3iCXmXf3m+txiCNhFcbbvb0Grq3wbAWGB0VW7ymI6AHixDXFLD5IYl4=\""
385 (* https://datatracker.ietf.org/doc/html/rfc7235#appendix-C *)
387 let tc_parse_auth_params () =
388 Logr.info
(fun m
-> m
"http_test.test_parse_auth_param");
389 let module P
= Http.Signature.P
in
390 (match {|uhu
|} |> Tyre.exec
(P.token
|> Tyre.compile
) with
391 | Ok
"uhu" -> "super"
392 | _
-> "was anderes")
393 |> Assrt.equals_string __LOC__
"super";
394 (match {|"uhu"|} |> Tyre.exec
(P.quoted_string
|> Tyre.compile
) with
395 | Ok
"uhu" -> "super"
396 | _
-> "was anderes")
397 |> Assrt.equals_string __LOC__
"super";
398 (match {|uhu
="aha"|} |> Tyre.exec
(P.auth_param
|> Tyre.compile
) with
399 | Ok
("uhu","aha") -> "super"
400 | _
-> "was anderes")
401 |> Assrt.equals_string __LOC__
"super";
402 (match {|uhu
="ah\"a"|} |> Tyre.exec
(P.auth_param
|> Tyre.compile
) with
403 | Ok
("uhu",{|ah
"a|}) -> "super
"
404 | _ -> "was anderes
")
405 |> Assrt.equals_string __LOC__ "super
";
406 (match {|a="A
", b="B
"|} |> Tyre.exec (P.list_auth_param|> Tyre.compile) with
407 | Ok [("a","A
"); ("b
","B
")] -> "super
"
408 | _ -> "was anderes
")
409 |> Assrt.equals_string __LOC__ "super
";
410 (match {|a="A
", nasty="na
,s\"ty
",b="B
"|} |> Tyre.exec (P.list_auth_param|> Tyre.compile) with
412 ("nasty
",{|na,s"ty
|});
413 ("b","B")] -> "super"
414 | _
-> "was anderes")
415 |> Assrt.equals_string __LOC__
"super";
418 let tc_parse_signature () =
419 Logr.info
(fun m
-> m
"http_test.test_parse_signature");
420 (* https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12#section-4.1.1 *)
421 let _sihe = {|keyId
="rsa-key-1",algorithm
="hs2019", created
=1402170695, expires
=1402170995, headers
="(request-target) (created) (expires) host date digest content-length", signature
="Base64(RSA-SHA256(signing string))"|}
422 |> Http.Signature.decode
in
423 let _sihe = {|keyId
="hmac-key-1",algorithm
="hs2019",created
=1402170695,headers
="(request-target) (created) host digest content-length",signature
="Base64(HMAC-SHA512(signing string))"|}
424 |> Http.Signature.decode
in
426 date='Thu, 29 Jun 2023 09:51:37 GMT' digest='SHA-256=rSBxGz18uv2ZvY9PxjkuKv6ZWR78M/5S2m+yOXrq+ik=' signature='keyId="https://alpaka.social/users/traunstein#main-key",algorithm="rsa-sha256",headers="(request-target) host date digest content-type",signature="JIHBg3VahvgFweniUBfH0QSHOuilcYW313i7H6gptKT/uOSfs5QhADm7LKLZ6q7jZWtQLi4Ge8dhxVeYhGpdU5P3iABn665z3TvuUiwVUO0sGI6yAv+z9wVmFfPLFsTYOB09Fy+yht+E4Z9GOF6C/U79eb/y8QOuj1OJB3L+427IQpnJMuPh5e22LBM1E/eXLbvWyshKqX0n8WZj4qPezzsH21Afn+dUnd2jc2XqUbOpzeFkz45ut0okZAF3686/sQ0sBcloSFfvdB+EuLqZLJSYcnMe3Qe8dUpibgm5+v0XfgLZYPL2P7VpuMXkQB9neRbSCdTWojcABBwUGWV0DA=="'
429 ("date",{|Thu
, 29 Jun
2023 09:51:37 GMT
|});
430 ("digest",{|SHA
-256=rSBxGz18uv2ZvY9PxjkuKv6ZWR78M
/5S2m
+yOXrq
+ik
=|});
431 ("signature",{|keyId
="https://alpaka.social/users/traunstein#main-key",algorithm
="rsa-sha256",headers
="(request-target) host date digest content-type",signature
="JIHBg3VahvgFweniUBfH0QSHOuilcYW313i7H6gptKT/uOSfs5QhADm7LKLZ6q7jZWtQLi4Ge8dhxVeYhGpdU5P3iABn665z3TvuUiwVUO0sGI6yAv+z9wVmFfPLFsTYOB09Fy+yht+E4Z9GOF6C/U79eb/y8QOuj1OJB3L+427IQpnJMuPh5e22LBM1E/eXLbvWyshKqX0n8WZj4qPezzsH21Afn+dUnd2jc2XqUbOpzeFkz45ut0okZAF3686/sQ0sBcloSFfvdB+EuLqZLJSYcnMe3Qe8dUpibgm5+v0XfgLZYPL2P7VpuMXkQB9neRbSCdTWojcABBwUGWV0DA=="|});
432 ] |> Cohttp.Header.of_list
in
433 let sh = "signature" |> Cohttp.Header.get
h |> Option.value ~default
:"-" in
435 |> Assrt.equals_string __LOC__
{|keyId
="https://alpaka.social/users/traunstein#main-key",algorithm
="rsa-sha256",headers
="(request-target) host date digest content-type",signature
="JIHBg3VahvgFweniUBfH0QSHOuilcYW313i7H6gptKT/uOSfs5QhADm7LKLZ6q7jZWtQLi4Ge8dhxVeYhGpdU5P3iABn665z3TvuUiwVUO0sGI6yAv+z9wVmFfPLFsTYOB09Fy+yht+E4Z9GOF6C/U79eb/y8QOuj1OJB3L+427IQpnJMuPh5e22LBM1E/eXLbvWyshKqX0n8WZj4qPezzsH21Afn+dUnd2jc2XqUbOpzeFkz45ut0okZAF3686/sQ0sBcloSFfvdB+EuLqZLJSYcnMe3Qe8dUpibgm5+v0XfgLZYPL2P7VpuMXkQB9neRbSCdTWojcABBwUGWV0DA=="|};
436 (match sh |> Http.Signature.decode
437 (* Http.Signature.decode *) with
439 sh |> List.length
|> Assrt.equals_int __LOC__
4;
440 List.assoc_opt
"keyId" sh |> Option.value ~default
:"-"
441 |> Assrt.equals_string __LOC__
"https://alpaka.social/users/traunstein#main-key";
442 List.assoc_opt
"algorithm" sh |> Option.value ~default
:"-"
443 |> Assrt.equals_string __LOC__
"rsa-sha256";
444 List.assoc_opt
"headers" sh |> Option.value ~default
:"-"
445 |> Assrt.equals_string __LOC__
"(request-target) host date digest content-type";
446 List.assoc_opt
"signature" sh |> Option.value ~default
:"-"
447 |> Assrt.equals_string __LOC__
"JIHBg3VahvgFweniUBfH0QSHOuilcYW313i7H6gptKT/uOSfs5QhADm7LKLZ6q7jZWtQLi4Ge8dhxVeYhGpdU5P3iABn665z3TvuUiwVUO0sGI6yAv+z9wVmFfPLFsTYOB09Fy+yht+E4Z9GOF6C/U79eb/y8QOuj1OJB3L+427IQpnJMuPh5e22LBM1E/eXLbvWyshKqX0n8WZj4qPezzsH21Afn+dUnd2jc2XqUbOpzeFkz45ut0okZAF3686/sQ0sBcloSFfvdB+EuLqZLJSYcnMe3Qe8dUpibgm5+v0XfgLZYPL2P7VpuMXkQB9neRbSCdTWojcABBwUGWV0DA=="
448 | _
-> "fail" |> Assrt.equals_string __LOC__
"");
451 let tc_verify_basic () =
452 Logr.info
(fun m
-> m
"http_test.test_verify_basic");
453 let pub = pub_key_cavage in
456 ("date", {|Sun
, 05 Jan
2014 21:31:40 GMT
|});
457 ("signature", {|keyId
="Test",algorithm
="rsa-sha256",headers
="(request-target) host date",signature
="qdx+H7PHHDZgy4y/Ahn9Tny9V3GP6YgBPyUXMmoxWtLbHpUnXS2mg2+SbrQDMCJypxBLSPQR2aAjn7ndmw2iicw3HMbe8VfEdKFYRqzic+efkb3nndiv/x1xSHDJWeSWkx3ButlYSuBskLu6kd9Fswtemr3lgdDEmn04swr2Os0="|});
459 ("host", {|example
.com
|});
460 ] |> Cohttp.Header.of_list
in
461 (* fetch http header values and map from lowercase plus the special name (request-target) *)
462 let hdr = Cohttp.Header.get
h in
463 (* take a list of header names and fetch them incl. values. *)
469 | Some
v -> Cohttp.Header.add init k
v)
471 (Cohttp.Header.init
()) in
473 Logr.debug
(fun m
-> m
"%s.%s get & parse the signature header" "Ap.Inbox" "post");
474 let ( let* ) = Result.bind
in
475 let* si_v
= "signature" |> hdr |> Option.to_result ~none
:Http.s502'
in
477 |> Http.Signature.decode
481 | `ConverterFailure _
->
482 Logr.debug
(fun m
-> m
"%s.%s Signature parsing failure" "Ap.Inbox" "post");
484 let* algo
= si_v
|> List.assoc_opt
"algorithm" |> Option.to_result ~none
:Http.s502'
in
485 let* heads
= si_v
|> List.assoc_opt
"headers" |> Option.to_result ~none
:Http.s502'
in
486 let heads = heads |> String.split_on_char ' '
in
487 let* keyid
= si_v
|> List.assoc_opt
"keyId" |> Option.to_result ~none
:Http.s502'
in
488 let _keyid = keyid
|> Uri.of_string
in
489 let* sign
= si_v
|> List.assoc_opt
"signature" |> Option.to_result ~none
:Http.s502'
in
490 let sign = sign |> Base64.decode_exn
|> Cstruct.of_string
in
492 Logr.debug
(fun m
-> m
"%s.%s fetch the remote actor profile & key" "Ap.Inbox" "post");
494 Logr.debug
(fun m
-> m
"%s.%s get the verified header values, signature algorithm %s" "Ap.Inbox" "post" algo
);
495 let heads = heads |> hdrs in
497 |> Http.Signature.to_sign_string0 ~request
:(Some
(`POST
,Uri.of_string
"/foo?param=value&pet=dog"))
499 |> Ap.PubKeyPem.verify ~algo ~inbox
:Uri.empty ~key
:pub ~signature
:sign
500 |> Result.map_error
(fun (`Msg e
) ->
501 Logr.warn
(fun m
-> m
"%s.%s %s" "Ap.Inbox" "post" e
);
505 let v l
n = Cohttp.Header.get l
n |> Option.value ~default
:"?" in
507 | Error _
-> "aua" |> Assrt.equals_string __LOC__
"-"
509 h |> Cohttp.Header.to_list
|> List.length
|> Assrt.equals_int __LOC__
2;
510 "date" |> v h |> Assrt.equals_string __LOC__
"Sun, 05 Jan 2014 21:31:40 GMT";
511 "host" |> v h |> Assrt.equals_string __LOC__
"example.com");
514 let tc_verify_hs2019_raw () =
516 2024-09-16T14:26:45.455+02:00 DEBUG Is2s.Inbox.post ba111224-c9b7-4c56-ae48-82f04795f23e Signature: keyId="https://gotosocial.dev.seppo.social/users/demo/main-key",algorithm="hs2019",headers="(request-target) host date digest",signature="MG9tIV9rWJHDFKEFGsjakYoBtPjZbyk/ddTn6Xr2xHkmTVZDmkJmGcD4yDfWfQ4m8BYS+jd4lnb8O5fdm/pFwpFDGU70IDLsg6INGxZJQKuWbQB7dFEBJt22h8GcjOIlXvw4cKsgc3KvplIjTrFlnYiQQVvcSy+uQRXJTJTm2Y6vxOQzFvSJa0S8lXz5+x/CqpqXJtj1cSztEHZEFdBla2M30smV1uJvQcfa+lIRPwXdwtL0COsg8J00hAYBoFXPo+4N/jytArkYOFz6MasUrRODURuAE2fR6JI2aAerBy0WFE17TyWuXjWlnYt6t9aO5Wzo/qc/3DgMWHQr8NZGVg=="
517 2024-09-16T14:26:45.608+02:00 DEBUG Is2s.Inbox.post signature check '(request-target): post /2024-03-19/seppo.cgi/activitypub/inbox.jsa
518 host: dev.seppo.social
519 date: Mon, 16 Sep 2024 12:26:45 GMT
520 digest: SHA-256=bcLAcfJg/048pSOMeA29j/PqW2iQJw96mT+egmjF+Zk='
523 ("host", {|dev
.seppo
.social
|});
524 ("date", {|Mon
, 16 Sep
2024 12:26:45 GMT
|});
525 ("signature", {|keyId
="https://gotosocial.dev.seppo.social/users/demo/main-key",algorithm
="hs2019",headers
="(request-target) host date digest",signature
="MG9tIV9rWJHDFKEFGsjakYoBtPjZbyk/ddTn6Xr2xHkmTVZDmkJmGcD4yDfWfQ4m8BYS+jd4lnb8O5fdm/pFwpFDGU70IDLsg6INGxZJQKuWbQB7dFEBJt22h8GcjOIlXvw4cKsgc3KvplIjTrFlnYiQQVvcSy+uQRXJTJTm2Y6vxOQzFvSJa0S8lXz5+x/CqpqXJtj1cSztEHZEFdBla2M30smV1uJvQcfa+lIRPwXdwtL0COsg8J00hAYBoFXPo+4N/jytArkYOFz6MasUrRODURuAE2fR6JI2aAerBy0WFE17TyWuXjWlnYt6t9aO5Wzo/qc/3DgMWHQr8NZGVg=="|});
526 ("digest", {|SHA
-256=bcLAcfJg
/048pSOMeA29j
/PqW2iQJw96mT
+egmjF
+Zk
=|});
527 ] |> Cohttp.Header.of_list
in
528 let pay = h |> Http.Signature.to_sign_string0
529 ~request
:(Some
(`POST
,
530 "https://dev.seppo.social/2024-03-19/seppo.cgi/activitypub/inbox.jsa" |> Uri.of_string
)) in
531 pay |> Assrt.equals_string __LOC__
{|(request
-target
): post
/2024-03-19/seppo
.cgi
/activitypub
/inbox
.jsa
532 host
: dev
.seppo
.social
533 date
: Mon
, 16 Sep
2024 12:26:45 GMT
534 signature
: keyId
="https://gotosocial.dev.seppo.social/users/demo/main-key",algorithm
="hs2019",headers
="(request-target) host date digest",signature
="MG9tIV9rWJHDFKEFGsjakYoBtPjZbyk/ddTn6Xr2xHkmTVZDmkJmGcD4yDfWfQ4m8BYS+jd4lnb8O5fdm/pFwpFDGU70IDLsg6INGxZJQKuWbQB7dFEBJt22h8GcjOIlXvw4cKsgc3KvplIjTrFlnYiQQVvcSy+uQRXJTJTm2Y6vxOQzFvSJa0S8lXz5+x/CqpqXJtj1cSztEHZEFdBla2M30smV1uJvQcfa+lIRPwXdwtL0COsg8J00hAYBoFXPo+4N/jytArkYOFz6MasUrRODURuAE2fR6JI2aAerBy0WFE17TyWuXjWlnYt6t9aO5Wzo/qc/3DgMWHQr8NZGVg=="
535 digest
: SHA
-256=bcLAcfJg
/048pSOMeA29j
/PqW2iQJw96mT
+egmjF
+Zk
=|};
536 let signature = "signature"
537 |> Cohttp.Header.get
h
538 |> Option.value ~default
:"ouch"
539 |> Http.Signature.decode
541 |> List.assoc
"signature" in
542 signature |> check
string __LOC__
{|MG9tIV9rWJHDFKEFGsjakYoBtPjZbyk
/ddTn6Xr2xHkmTVZDmkJmGcD4yDfWfQ4m8BYS
+jd4lnb8O5fdm
/pFwpFDGU70IDLsg6INGxZJQKuWbQB7dFEBJt22h8GcjOIlXvw4cKsgc3KvplIjTrFlnYiQQVvcSy
+uQRXJTJTm2Y6vxOQzFvSJa0S8lXz5
+x
/CqpqXJtj1cSztEHZEFdBla2M30smV1uJvQcfa
+lIRPwXdwtL0COsg8J00hAYBoFXPo
+4N
/jytArkYOFz6MasUrRODURuAE2fR6JI2aAerBy0WFE17TyWuXjWlnYt6t9aO5Wzo
/qc
/3DgMWHQr8NZGVg
==|};
543 let signature = signature
545 |> Cstruct.of_string
in
546 let pe = {|{"@context":["https://www.w3.org/ns/activitystreams","http://schema.org","http://joinmastodon.org/ns","https://w3id.org/security/v1"],"attachment":[{"name":"reason","type":"PropertyValue","value":"\u003ca href=\"https://seppo.social/issues/13\" rel=\"nofollow noreferrer noopener\" target=\"_blank\"\u003ehttps://seppo.social/issues/13\u003c/a\u003e"},{"name":"2","type":"PropertyValue","value":"b"},{"name":"3","type":"PropertyValue","value":"c"},{"name":"4","type":"PropertyValue","value":"d"}],"discoverable":false,"featured":"https://gotosocial.dev.seppo.social/users/demo/collections/featured","followers":"https://gotosocial.dev.seppo.social/users/demo/followers","following":"https://gotosocial.dev.seppo.social/users/demo/following","id":"https://gotosocial.dev.seppo.social/users/demo","inbox":"https://gotosocial.dev.seppo.social/users/demo/inbox","manuallyApprovesFollowers":true,"name":"slöth","outbox":"https://gotosocial.dev.seppo.social/users/demo/outbox","preferredUsername":"demo","publicKey":{"id":"https://gotosocial.dev.seppo.social/users/demo/main-key","owner":"https://gotosocial.dev.seppo.social/users/demo","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxaVct4ctlG5ssph8+Qiu\ncV3/mtoTGE3nyl+9eTUaIvqdgcedIvDrAqJ4Koj6JEFMTjHpJYBvWXWhR7QEuYZJ\n3HUjcZozxh40zCOosu841uvoIGEDq75xHRKhFRC38RBo7PRhAjqqCYgfkt7AERmw\nJBA1BF2EAPI5i5mjo2IPZzAJdByweVZoo5Wo3Ki3utF3oPXZRnuHmtLbphdJxZTD\n9MXfaZLG6sfuzv+Og7O6AninawBmYFz60d8GwfaBOKi8ftbnheh04BGir+jgE02m\nWNgdMWOBM9D0D3+kmZ2+m+rACQCT9D500EtMWZSn4ZBDQxUkLLbXrZJt5/fDRur7\nQQIDAQAB\n-----END PUBLIC KEY-----\n"},"summary":"\u003cp\u003e\u003ca href=\"https://seppo.social/issues/13\" rel=\"nofollow noreferrer noopener\" target=\"_blank\"\u003ehttps://seppo.social/issues/13\u003c/a\u003e\u003c/p\u003e","tag":[],"type":"Person","url":"https://gotosocial.dev.seppo.social/@demo"}|}
547 |> Ezjsonm.from_string
548 |> As2_vocab.Decode.person
550 let pub = pe.public_key
.pem
|> Ap.PubKeyPem.of_pem
|> Result.get_ok
in
551 let `Msg e
= X509.Public_key.verify
556 (`Message
(pay |> Cstruct.of_string
))
557 |> Result.get_error
in
558 e
|> check
string __LOC__
"bad signature"
560 let tc_verify_hs2019 () =
562 2024-09-16T14:26:45.455+02:00 DEBUG Is2s.Inbox.post ba111224-c9b7-4c56-ae48-82f04795f23e Signature: keyId="https://gotosocial.dev.seppo.social/users/demo/main-key",algorithm="hs2019",headers="(request-target) host date digest",signature="MG9tIV9rWJHDFKEFGsjakYoBtPjZbyk/ddTn6Xr2xHkmTVZDmkJmGcD4yDfWfQ4m8BYS+jd4lnb8O5fdm/pFwpFDGU70IDLsg6INGxZJQKuWbQB7dFEBJt22h8GcjOIlXvw4cKsgc3KvplIjTrFlnYiQQVvcSy+uQRXJTJTm2Y6vxOQzFvSJa0S8lXz5+x/CqpqXJtj1cSztEHZEFdBla2M30smV1uJvQcfa+lIRPwXdwtL0COsg8J00hAYBoFXPo+4N/jytArkYOFz6MasUrRODURuAE2fR6JI2aAerBy0WFE17TyWuXjWlnYt6t9aO5Wzo/qc/3DgMWHQr8NZGVg=="
563 2024-09-16T14:26:45.608+02:00 DEBUG Is2s.Inbox.post signature check '(request-target): post /2024-03-19/seppo.cgi/activitypub/inbox.jsa
564 host: dev.seppo.social
565 date: Mon, 16 Sep 2024 12:26:45 GMT
566 digest: SHA-256=bcLAcfJg/048pSOMeA29j/PqW2iQJw96mT+egmjF+Zk='
568 let act = {|{"@context":["https://www.w3.org/ns/activitystreams","http://schema.org","http://joinmastodon.org/ns","https://w3id.org/security/v1"],"attachment":[{"name":"reason","type":"PropertyValue","value":"\u003ca href=\"https://seppo.social/issues/13\" rel=\"nofollow noreferrer noopener\" target=\"_blank\"\u003ehttps://seppo.social/issues/13\u003c/a\u003e"},{"name":"2","type":"PropertyValue","value":"b"},{"name":"3","type":"PropertyValue","value":"c"},{"name":"4","type":"PropertyValue","value":"d"}],"discoverable":false,"featured":"https://gotosocial.dev.seppo.social/users/demo/collections/featured","followers":"https://gotosocial.dev.seppo.social/users/demo/followers","following":"https://gotosocial.dev.seppo.social/users/demo/following","id":"https://gotosocial.dev.seppo.social/users/demo","inbox":"https://gotosocial.dev.seppo.social/users/demo/inbox","manuallyApprovesFollowers":true,"name":"slöth","outbox":"https://gotosocial.dev.seppo.social/users/demo/outbox","preferredUsername":"demo","publicKey":{"id":"https://gotosocial.dev.seppo.social/users/demo/main-key","owner":"https://gotosocial.dev.seppo.social/users/demo","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxaVct4ctlG5ssph8+Qiu\ncV3/mtoTGE3nyl+9eTUaIvqdgcedIvDrAqJ4Koj6JEFMTjHpJYBvWXWhR7QEuYZJ\n3HUjcZozxh40zCOosu841uvoIGEDq75xHRKhFRC38RBo7PRhAjqqCYgfkt7AERmw\nJBA1BF2EAPI5i5mjo2IPZzAJdByweVZoo5Wo3Ki3utF3oPXZRnuHmtLbphdJxZTD\n9MXfaZLG6sfuzv+Og7O6AninawBmYFz60d8GwfaBOKi8ftbnheh04BGir+jgE02m\nWNgdMWOBM9D0D3+kmZ2+m+rACQCT9D500EtMWZSn4ZBDQxUkLLbXrZJt5/fDRur7\nQQIDAQAB\n-----END PUBLIC KEY-----\n"},"summary":"\u003cp\u003e\u003ca href=\"https://seppo.social/issues/13\" rel=\"nofollow noreferrer noopener\" target=\"_blank\"\u003ehttps://seppo.social/issues/13\u003c/a\u003e\u003c/p\u003e","tag":[],"type":"Person","url":"https://gotosocial.dev.seppo.social/@demo"}|} in
569 let pe = act |> Ezjsonm.from_string
|> As2_vocab.Decode.person
|> Result.get_ok
in
570 let pub = pe.public_key
.pem
|> Ap.PubKeyPem.of_pem
|> Result.get_ok
in
572 ("host", {|dev
.seppo
.social
|});
573 ("date", {|Mon
, 16 Sep
2024 12:26:45 GMT
|});
574 ("signature", {|keyId
="https://gotosocial.dev.seppo.social/users/demo/main-key",algorithm
="hs2019",headers
="(request-target) host date digest",signature="MG9tIV9rWJHDFKEFGsjakYoBtPjZbyk/ddTn6Xr2xHkmTVZDmkJmGcD4yDfWfQ4m8BYS+jd4lnb8O5fdm/pFwpFDGU70IDLsg6INGxZJQKuWbQB7dFEBJt22h8GcjOIlXvw4cKsgc3KvplIjTrFlnYiQQVvcSy+uQRXJTJTm2Y6vxOQzFvSJa0S8lXz5+x/CqpqXJtj1cSztEHZEFdBla2M30smV1uJvQcfa+lIRPwXdwtL0COsg8J00hAYBoFXPo+4N/jytArkYOFz6MasUrRODURuAE2fR6JI2aAerBy0WFE17TyWuXjWlnYt6t9aO5Wzo/qc/3DgMWHQr8NZGVg=="|});
575 ("digest", {|SHA
-256=bcLAcfJg
/048pSOMeA29j
/PqW2iQJw96mT
+egmjF
+Zk
=|});
576 ] |> Cohttp.Header.of_list
in
577 h |> Http.Signature.to_sign_string0 ~request
:(Some
(`POST
,"/" |> Uri.of_string
))
578 |> Assrt.equals_string __LOC__
{|(request
-target
): post
/
579 host
: dev
.seppo
.social
580 date
: Mon
, 16 Sep
2024 12:26:45 GMT
581 signature: keyId
="https://gotosocial.dev.seppo.social/users/demo/main-key",algorithm
="hs2019",headers
="(request-target) host date digest",signature="MG9tIV9rWJHDFKEFGsjakYoBtPjZbyk/ddTn6Xr2xHkmTVZDmkJmGcD4yDfWfQ4m8BYS+jd4lnb8O5fdm/pFwpFDGU70IDLsg6INGxZJQKuWbQB7dFEBJt22h8GcjOIlXvw4cKsgc3KvplIjTrFlnYiQQVvcSy+uQRXJTJTm2Y6vxOQzFvSJa0S8lXz5+x/CqpqXJtj1cSztEHZEFdBla2M30smV1uJvQcfa+lIRPwXdwtL0COsg8J00hAYBoFXPo+4N/jytArkYOFz6MasUrRODURuAE2fR6JI2aAerBy0WFE17TyWuXjWlnYt6t9aO5Wzo/qc/3DgMWHQr8NZGVg=="
582 digest
: SHA
-256=bcLAcfJg
/048pSOMeA29j
/PqW2iQJw96mT
+egmjF
+Zk
=|};
584 |> Cohttp.Header.get
h |> Option.value ~default
:"ouch"
585 |> Http.Signature.decode
587 si |> List.length
|> check
int __LOC__
4;
588 si |> List.assoc
"keyId" |> check
string __LOC__
"https://gotosocial.dev.seppo.social/users/demo/main-key";
589 si |> List.assoc
"algorithm" |> check
string __LOC__
"hs2019";
590 let sign = si |> List.assoc
"signature" |> Base64.decode_exn
|> Cstruct.of_string
in
591 let inbox = "/" |> Uri.of_string
in
593 |> Http.Signature.to_sign_string0 ~request
:(Some
(`GET
,Uri.empty
))
595 |> Ap.PubKeyPem.verify ~algo
:"hs2019" ~
inbox ~key
:pub ~
signature:sign
596 |> Result.get_error
in
597 m
|> check
string __LOC__
"bad signature"
599 (* also https://datatracker.ietf.org/doc/id/draft-richanna-http-message-signatures-00.html#section-a.3
601 2024-10-03T00:14:44.022+02:00 DEBUG Is2s.Inbox.post 837a994a-754b-49bd-9154-982603e3dcc7 Signature: keyId="https://gotosocial.dev.seppo.social/users/demo/main-key",algorithm="hs2019",headers="(request-target) host date digest",signature="HCnGYTDX+xfmuUysOGBN//mqInBv//55S/1NRZ+vEqMZxIgu7QsmUB/I7MfeM3PyF1oZDZ5cngsLPmuSjBVnQkAOJIebybNsh9HyLT5Ln4UDyiY30AZVuX+tNz0K5eGmnxS9LFPyfihvrnYZN+2Irny5mCPkB61u8TTmjYKG/WTLKrVhf49fwNt6U11zq7xkVkB8NT6VllH4tftx/GpfqvdCl+FA+UrtKu6GyHBQMmsEz7ybcVXhF04K8z95X2nM/I/pfmQ/b2ySpzX3YwL0UlVrFI44fq7zXIvpkKT3ntg66z3xluuhBL3y2amzty6Ciz/evcJcq6JpaVJ2jTNO5Q=="
602 2024-10-03T00:14:44.205+02:00 DEBUG Is2s.Inbox.post signature check '(request-target): post /2024-03-19/seppo.cgi/activitypub/inbox.jsa
603 host: mx250.darknet.mro.name
604 date: Wed, 02 Oct 2024 22:14:43 GMT
605 digest: SHA-256=DpWuW0JAXqss/tShNxYG7NmD+o18Ok7lNDvHRD0vbcU='
607 let tc_verify_hs2019_b () =
608 let signature = {|HCnGYTDX
+xfmuUysOGBN
//mqInBv
//55S
/1NRZ
+vEqMZxIgu7QsmUB
/I7MfeM3PyF1oZDZ5cngsLPmuSjBVnQkAOJIebybNsh9HyLT5Ln4UDyiY30AZVuX
+tNz0K5eGmnxS9LFPyfihvrnYZN
+2Irny5mCPkB61u8TTmjYKG
/WTLKrVhf49fwNt6U11zq7xkVkB8NT6VllH4tftx
/GpfqvdCl
+FA
+UrtKu6GyHBQMmsEz7ybcVXhF04K8z95X2nM
/I
/pfmQ
/b2ySpzX3YwL0UlVrFI44fq7zXIvpkKT3ntg66z3xluuhBL3y2amzty6Ciz
/evcJcq6JpaVJ2jTNO5Q
==|}
609 |> Base64.decode_exn
|> Cstruct.of_string
610 and key
= {|-----BEGIN PUBLIC KEY
-----
611 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxaVct4ctlG5ssph8
+Qiu
612 cV3
/mtoTGE3nyl
+9eTUaIvqdgcedIvDrAqJ4Koj6JEFMTjHpJYBvWXWhR7QEuYZJ
613 3HUjcZozxh40zCOosu841uvoIGEDq75xHRKhFRC38RBo7PRhAjqqCYgfkt7AERmw
614 JBA1BF2EAPI5i5mjo2IPZzAJdByweVZoo5Wo3Ki3utF3oPXZRnuHmtLbphdJxZTD
615 9MXfaZLG6sfuzv
+Og7O6AninawBmYFz60d8GwfaBOKi8ftbnheh04BGir
+jgE02m
616 WNgdMWOBM9D0D3
+kmZ2
+m
+rACQCT9D500EtMWZSn4ZBDQxUkLLbXrZJt5
/fDRur7
618 -----END PUBLIC KEY
-----
619 |} |> Cstruct.of_string
620 |> X509.Public_key.decode_pem
|> Result.get_ok
621 and data
= [ {|(request
-target
): post
/2024-03-19/seppo
.cgi
/activitypub
/inbox.jsa
|} ;
622 {|host
: mx250
.darknet
.mro
.name
|} ;
623 {|date
: Wed
, 02 Oct
2024 22:14:43 GMT
|} ;
624 {|digest
: SHA
-256=DpWuW0JAXqss
/tShNxYG7NmD
+o18Ok7lNDvHRD0vbcU
=|} ]
626 key
|> X509.Public_key.key_type
|> X509.Key_type.to_string
|> check
string __LOC__
"rsa";
627 X509.Public_key.verify
632 (`Message
(data
|> String.concat
"\n" |> Cstruct.of_string
))
636 https://datatracker.ietf.org/doc/id/draft-richanna-http-message-signatures-00.html#example-key-rsa-test
638 let pub_key_richanna =
639 {|-----BEGIN PUBLIC KEY
-----
640 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhAKYdtoeoy8zcAcR874L
641 8cnZxKzAGwd7v36APp7Pv6Q2jdsPBRrwWEBnez6d0UDKDwGbc6nxfEXAy5mbhgaj
642 zrw3MOEt8uA5txSKobBpKDeBLOsdJKFqMGmXCQvEG7YemcxDTRPxAleIAgYYRjTS
643 d
/QBwVW9OwNFhekro3RtlinV0a75jfZgkne
/YiktSvLG34lw2zqXBDTC5NHROUqG
644 TlML4PlNZS5Ri2U4aCNx2rUPRcKIlE0PuKxI4T
+HIaFpv8
+rdV6eUgOrB2xeI1dS
645 FFn
/nnv5OoZJEIB
+VmuKn3DCUcCZSFlQPSXSfBDiUGhwOw76WuSSsf1D4b
/vLoJ1
647 -----END PUBLIC KEY
-----
649 |> Cstruct.of_string
|> X509.Public_key.decode_pem
|> Result.get_ok
652 https://datatracker.ietf.org/doc/id/draft-richanna-http-message-signatures-00.html#name-hs2019-signature-over-minim
654 let tc_verify_hs2019_richanna_minimal () =
655 let signature = "e3y37nxAoeuXw2KbaIxE2d9jpE7Z9okgizg6QbD2Z7fUVUvog+ZTKKLRBnhNglVIY6fAaYlHwx7ZAXXdBVF8gjWBPL6U9zRrB4PFzjoLSxHaqsvS0ZK9FRxpenptgukaVQ1aeva3PE1aD6zZ93df2lFIFXGDefYCQ+M/SrDGQOFvaVykEkte5mO6zQZ/HpokjMKvilfSMJS+vbvC1GJItQpjs636Db+7zB2W1BurkGxtQdCLDXuIDg4S8pPSDihkch/dUzL2BpML3PXGKVXwHOUkVG6Q2ge07IYdzya6N1fIVA9eKI1Y47HT35QliVAxZgE0EZLo8mxq19ReIVvuFg=="
656 |> Base64.decode_exn
|> Cstruct.of_string
657 and key
= pub_key_richanna
658 and data
= [ {|(created
): 1402170695|} ;
659 {|(request
-target
): post
/foo?param
=value&pet
=dog
|} ]
661 key
|> X509.Public_key.key_type
|> X509.Key_type.to_string
|> check
string __LOC__
"rsa";
662 X509.Public_key.verify
667 (`Message
(data
|> String.concat
"\n" |> Cstruct.of_string
))
671 https://datatracker.ietf.org/doc/id/draft-richanna-http-message-signatures-00.html#name-create-the-signature-input
673 let tc_verify_hs2019_richanna_nonn () =
674 let signature = {|T1l3tWH2cSP31nfuvc3nVaHQ6IAu9YLEXg2pCeEOJETXnlWbgKtBTaXV6LNQWtf4O42V2DZwDZbmVZ8xW3TFW80RrfrY0
+fyjD4OLN7
/zV6L6d2v7uBpuWZ8QzKuHYFaRNVXgFBXN3VJnsIOUjv20pqZMKO3phLCKX2
/zQzJLCBQvF
/5UKtnJiMp
1ACNhG
8LF
0Q
0FPWfe
86YZBBxqrQr
5WfjMu
0LOO
52ZAxi
9KTWSlceJ
2U361gDb
7S
5Deub
8MaDrjUEpluphQeo
8xyvHBoNXsqeax
/WaHyRYOgaW6krxEGVaBQAfA2czYZhEA05Tb38ahq
/gwDQ1bagd9rGnCHtAg
==|}
675 |> Base64.decode_exn
|> Cstruct.of_string
676 and key
= pub_key_richanna
677 and data
= {|(request
-target
): get
/foo
678 (created
): 1402170695
680 date
: Tue
, 07 Jun
2014 20:51:35 GMT
681 cache
-control
: max
-age
=60, must
-revalidate
683 x
-example
: Example header
with some whitespace
.|}
685 key
|> X509.Public_key.key_type
|> X509.Key_type.to_string
|> check
string __LOC__
"rsa";
686 X509.Public_key.verify
691 (`Message
(data
|> Cstruct.of_string
))
695 https://gts.superseriousbusiness.org/@dumpsterqueer/statuses/01J9BKYKR1W5X078ZESSAW10QS
697 let tc_verify_hs2019_gotosocial () =
698 let signature = {|G6X7IV
+qHqOaZIrrwRxunzbRgRhzn84UoUJfsSNLveHdVBAiaY3ayoj2F4ZiDxVV6zG0CN3j
+0pHbngWgHp4aMETkF
/x3KB
/l2ILHgBhUpIB
+ZAb1MkC
+yU
+9BNmp8EmVZldzdjQ
/MalStfeRc7rcMdL770TJbAW8cgPRPA6TB7P6m5tzEPkow56wR
/W0MuYJqWQzE8id7Ri65p63fu8NFha7WgBVM5I
+67hZ3sYZTBKdLQJJyS4K3nOZ20h
+pUSZUGF7WdTNnxtzaryJgFVL4Or2ydBa
/Jp0w8zspWFMlGCtG9A9cayQ7JHlCMiuf92f
/hpLWtWCSftg9IzZVakiw
==|}
699 |> Base64.decode_exn
|> Cstruct.of_string
701 "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy2/j9+2G7xrQvBtygrj4\naYHl8hTeZecDBnS+6IBjjEt+QWJ3z0Cv9lXSVMZw5i6DVTkVOGZrh2vZDu0BCTEV\n07dyASArE63Qe21WwjNObpkQ7YZbMxUkYjWCDYdqLMifAqElYzIK7xnY0pTWylmC\njm39qxmhk22PpzRkw+zofh9ykqyadmkA2/KrpZgGnjn6MiPqb2DeELV1tzmZ7mAz\n+k7pkkhxvBVqPhCZ104pyd1lc66obONSnIqxugRlrrUZbFv1e6xFsUmMUYrAGQTt\nZr4VeZwuaYj/MvFIeOZrmth/lg3QpYbKZYnJKVePyH+530jRSFerr2unbuGmEQAg\nvQIDAQAB\n-----END PUBLIC KEY-----\n"
702 |> Cstruct.of_string
|> X509.Public_key.decode_pem
|> Result.get_ok
703 and data
= {|(request
-target
): get
/@mro
/113244470620401979
704 host
: digitalcourage
.social
705 date
: Fri
, 04 Oct
2024 11:12:53 GMT
|}
707 key
|> X509.Public_key.key_type
|> X509.Key_type.to_string
|> check
string __LOC__
"rsa";
708 let `Msg m
= X509.Public_key.verify
713 (`Message
(data
|> Cstruct.of_string
))
714 |> Result.get_error
in
716 |> check
string __LOC__
"bad signature"
718 let tc_verify_masto430 () =
721 Signature: keyId="https://bewegung.social/use
722 rs/mro#main-key",algorithm="rsa-sha256",headers="(request-target) host date digest content-type",signature="v2h3HUJg0vH6HyNfOHSE7hIg0O
723 i1E+iUEO8ahxDjI99F7jKuh9sVRSpqhsAEagI5WeWZkQyhWjRmDZZBsk4+acALo36CsRj/C/m5CiF4J0hd+x8VSPWDJEQTYclm0jCthfdmeXg1/DOZnlWInBVQdwZJZyoW7nTn
724 EEGZuE0w6LsYCJb2oVUTW32gn+fbHnJ2mkBwPjwBlJ7zckEx3MwnV99GPkjdA0hBX/O4xSD7a0MIn4d0CSOGmbnTGKChTm//AvKXP4L5H9L6ovZFBfaHkDCqYDbdfXGWeheLXd
725 gDJHubi0LFecP3PP5cwOeuFtGgkWsSeLrUEyWgSlEKjCFRhw=="
727 (request-target): post /2024-03-19/seppo.cgi/activitypub/inbox.jsa
728 (request-target): post /2024-03-19/seppo.cgi/activitypub/inbox.jsa
729 host: mx250.darknet.mro.name
730 date: Thu, 10 Oct 2024 10:12:10 GMT
731 digest: SHA-256=N0m+gyYe/GieNBzMEOStVqf7hq/qdmh7bqdWanZSE1o=
732 content-type: application/activity+json
743 constructing the signagee payload
753 let _list_header_get l
(k
: string) : string option = l
|> List.assoc_opt k
in
754 let compute_verify_signature_payload fetcher
: string =
755 (match fetcher
"signature" with
756 | None
-> Some
"header not found: signature"
758 match si |> Http.Signature.decode
with
759 | Error _
-> Some
"error decoding signature"
761 match si |> List.assoc_opt
"headers" with
762 | None
-> Some
"signature field not found: headers"
764 (* assert lowercase *)
766 |> Astring.String.cuts ~sep
:" " |> List.rev
|> List.fold_left
767 (fun init k
-> match k
|> fetcher
with
769 | Some
v -> (k
,v) :: init
) []
770 |> Cohttp.Header.of_list
771 |> Cohttp.Header.to_frames
772 |> Astring.String.concat ~sep
:"\n"
774 |> Option.value ~default
:""
776 let r = {Cgi.Request.empty
with
777 request_method
= "POST";
778 raw_string
= (function
779 | "HTTP_HOST" -> Some
"example.com"
780 | "HTTP_DATE" -> Some
"tomorrow"
781 | "HTTP_SIGNATURE" -> Some
{|keyId
="Test",algorithm
="rsa-sha256",headers
="(request-target) host date",signature="qdx+H7PHHDZgy4y/Ahn9Tny9V3GP6YgBPyUXMmoxWtLbHpUnXS2mg2+SbrQDMCJypxBLSPQR2aAjn7ndmw2iicw3HMbe8VfEdKFYRqzic+efkb3nndiv/x1xSHDJWeSWkx3ButlYSuBskLu6kd9Fswtemr3lgdDEmn04swr2Os0="|}
783 Cgi.Request.header_get
r "SIGNATURE" |> Option.get
784 |> Http.Signature.decode
|> Result.get_ok
785 |> List.assoc
"headers"
786 |> Astring.String.cuts ~sep
:" "
788 |> Assrt.equals_int __LOC__
3;
789 (* typical for verify *)
791 |> Cgi.Request.header_get
792 |> compute_verify_signature_payload
793 |> Assrt.equals_string __LOC__
{|(request
-target
): post
/
804 "setup", `Quick
, set_up;
805 "tc_relpa", `Quick
, tc_relpa;
806 "tc_uri", `Quick
, tc_uri;
807 "tc_rel_cd", `Quick
, tc_rel_cd;
808 "tc_rx_script_name", `Quick
, Request.tc_rx_script_name;
809 "tc_uri", `Quick
, Request.tc_uri;
810 "tc_base", `Quick
, Request.tc_base;
811 "tc_query_string", `Quick
, Request.tc_query_string;
812 "tc_rfc1123", `Quick
, Cookie.tc_rfc1123;
813 "tc_to_string", `Quick
, Cookie.tc_to_string;
814 "tc_of_string", `Quick
, Cookie.tc_of_string;
815 "tc_headers", `Quick
, Header.tc_headers;
816 "tc_sig_encode", `Quick
, Header.tc_sig_encode;
817 "tc_signature", `Quick
, Header.tc_signature;
818 "tc_sign2", `Quick
, Header.tc_sign2;
819 "tc_to_sign_string_basic", `Quick
, Header.tc_to_sign_string_basic;
820 "tc_sign_basic", `Quick
, Header.tc_sign_basic;
821 "tc_sign_all_headers", `Quick
, Header.tc_sign_all_headers;
822 "tc_signed_headers", `Quick
, Header.tc_signed_headers;
823 "tc_parse_auth_params", `Quick
, Header.tc_parse_auth_params;
824 "tc_parse_signature", `Quick
, Header.tc_parse_signature;
825 "tc_verify_basic", `Quick
, Header.tc_verify_basic;
826 "tc_verify_hs2019_raw", `Quick
, Header.tc_verify_hs2019_raw;
827 "tc_verify_hs2019", `Quick
, Header.tc_verify_hs2019;
828 "tc_verify_hs2019_b", `Quick
, Header.tc_verify_hs2019_b;
829 "tc_verify_hs2019_richanna_minimal", `Quick
, Header.tc_verify_hs2019_richanna_minimal;
830 "tc_verify_hs2019_richanna_nonn", `Quick
, Header.tc_verify_hs2019_richanna_nonn;
831 "tc_verify_hs2019_gotosocial", `Quick
, Header.tc_verify_hs2019_gotosocial;
832 "tc_verify_masto430", `Quick
, Header.tc_verify_masto430;
833 "tc_sign_api", `Quick
, Header.tc_sign_api;