TLS signature with opam:tls

how would I sign string, namely do the equivalent of

Base64.strict_encode64(keypair.sign(OpenSSL::Digest::SHA256.new, signed_string))

from How to implement a basic ActivityPub server - Official Mastodon Blog with the pure OCaml tls?

I can’t find sign or the like in index (tls.index)

1 Like

meanwhile found PSS (mirage-crypto-pk.Mirage_crypto_pk.Rsa.PSS) but frankly it sounds a bit greek to me.

Will try along mirage-crypto/test_rsa.ml at main · mirage/mirage-crypto · GitHub

It is not entirely clear to me which kind of signatures AcitvityPub uses – I found reference to draft-ietf-httpbis-message-signatures-08 (in SocialCG/ActivityPub/Authentication Authorization - W3C Wiki):

RSA-PSS with SHA-512:

module PSS_SHA512 = Mirage_crypto_pk.Rsa.PSS(Mirage_crypto.Hash.SHA512)
let priv = Mirage_crypto_pk.Rsa.generate ~bits:2048 () in
let data = "to-be-signed-data" in
let signature = PSS_SHA512.sign ~key:priv (`Data (Cstruct.of_string data)) in
Cstruct.to_string signature

(if you need PSS_SHA256, use module PSS_SHA256 = Mirage_crypto_pk.Rsa.PSS(Mirage_crypto.Hash.SHA256) instead).

For PKCS1 (using SHA256):

let priv = Mirage_crypto_pk.Rsa.generate ~bits:2048 () in
let data = "to-be-signed-data" in
let signature = Mirage_crypto_pk.Rsa.PKCS1.sign ~hash:`SHA256 ~key:priv (`Data (Cstruct.of_string data)) in
Cstruct.to_string signature
3 Likes

the latter looks like a hit - will try asap and report back here. Allow 48h.

Thanks so much for implementing TLS in OCaml :camel:!

please bear with my baby steps, loading the keys as pem(s) seems to be incompabible with the sign call. As a whole:

let test_sign_sha256 () =
  let open Lwt in
  X509_lwt.private_of_pems ~cert:"test/public.pem" ~priv_key:"test/private.pem"
  >>= fun (_, priv) ->
  let data = "to-be-signed-data" in
  let signature =
    Mirage_crypto_pk.Rsa.PKCS1.sign ~hash:`SHA256 ~key:priv
      (`Message (Cstruct.of_string data))
  in
  signature |> Cstruct.to_string |> Assert2.equals_string "sig" "sig"

yields

dune runtest
File "test/tls_test.ml", line 20, characters 55-59:
20 |     Mirage_crypto_pk.Rsa.PKCS1.sign ~hash:`SHA256 ~key:priv
                                                            ^^^^
Error: This expression has type X509.Private_key.t
       but an expression was expected of type Mirage_crypto_pk.Rsa.priv
make: *** [test] Error 1

and I am puzzling how to either turn the loaded key priv into a Mirage_crypto_pk.Rsa.priv or load the latter from pems directly.

@mro Are you implementing an activitypub server? I was actually playing around with doing the same, and had run into the exact same question - In fact, I think I was also exactly following the join-mastadon blog post as a starting point.
(If so, would love to contribute btw).

I managed to get the types to match up at least by using X509.Private.sign or something to the same, and from looking at the implementation of some other activitypub servers (microblog.pub in particular), I believe this is equivalent to how they implement the signing as well? I think.

1 Like
let test_sign_sha256 () =
  let open Lwt in
  X509_lwt.private_of_pems ~cert:"test/public.pem" ~priv_key:"test/private.pem"
  >>= fun (_, priv) ->

indeed priv is a X509.Private_key.t. I can see two options:

(a)

let test_sign_sha256 () =
  let open Lwt in
  X509_lwt.private_of_pems ~cert:"test/public.pem" ~priv_key:"test/private.pem"
  >>= fun (_, priv) ->
  let data = "to-be-signed-data" in
  let signature =
    X509.Private_key.sign `SHA256 ~scheme:`RSA_PKCS1 priv (`Message (Cstruct.of_string data))
  in
  signature |> Cstruct.to_string |> Assert2.equals_string "sig" "sig"

(b)

let test_sign_sha256 () =
  let open Lwt in
  X509_lwt.private_of_pems ~cert:"test/public.pem" ~priv_key:"test/private.pem"
  >>= function
  | (_, `RSA priv) ->
    let data = "to-be-signed-data" in
    let signature =
      Mirage_crypto_pk.Rsa.PKCS1.sign ~hash:`SHA256 ~key:priv
        (`Message (Cstruct.of_string data))
    in
    signature |> Cstruct.to_string |> Assert2.equals_string "sig" "sig"
  | _ -> assert false
1 Like

indeed I do and I am raising funds (e.g. apply at prototypefund.de) to be able to do it professionally.
I am aiming for a single-user, zero-admin, self-hosted OCaml successor of https://mro.name/shaarligo – but now integrated in the Fediverse. I looked round and found nothing so far.

I shall open the now closed repo around June at Marcus Rohrmoser 🌍 - Codeberg.org and mirrors at sourcehut, github etc.

2 Likes

thank you @hannes - that got me over the hill to a final

let test_sign_sha256 () =
  let open Lwt in
  let p =
    (*
$ openssl genrsa -out private.pem 2048
$ openssl rsa -in private.pem -outform PEM -pubout -out public.pem
    *)
    (* https://mirleft.github.io/ocaml-tls/doc/tls/X509_lwt/#val-private_of_pems *)
    X509_lwt.private_of_pems ~cert:"public.pem" ~priv_key:"private.pem"
    >>= fun (_, priv) ->
    let data : string = "to-be-signed-data" in
    let signature : string =
      (*
    https://mirleft.github.io/ocaml-x509/doc/x509/X509/Private_key/#cryptographic-sign-operation
    *)
      X509.Private_key.sign `SHA256 ~scheme:`RSA_PKCS1 priv
        (`Message (Cstruct.of_string data))
      |> Result.get_ok |> Cstruct.to_string |> Base64.encode_exn
    in
    signature
    |> Assert2.equals_string "sig 256"
         "TVMQvS8OZ94BFvMn8ToL0jG01L1T3Dww4o7R6NwcJd7KsOmZtUKzzCezbnY5gjSECj/cfXxs2mrZlk9xGntTKqhJ6YIZmM3BBdXuPl8IyWms/ qtqZ4d+NVfMVDhYeGm43+j2HTegpcH2px9auXSThd2WcJmc7J98g9hx5+pEr6hA2UjawzOPYxIyyhNHzX9L1hTu6Xyjq6OkPWgqK9aHnAnGG1f3LgH+     YTR0T/l5ODPCyKboFMfvmnQ2PDNRPgsz82j9YuMVF2sE/TCdpTg+T6dX99Hmp35lomXnf1GSTrVAWBcx6mFEOABMrFSRRcMzGo9zCWPb/               y8V3xWaSpjroQ==";
    return ()
  in
  Lwt_main.run p

Feels good.

2 Likes