Interest in a Http_sig library?

Heyo all! I’ve been working on an activitypub server for a while now, and while it’s still not yet complete, recently I’ve reached a point where I realised that I’ve actually been sitting on some libraries that the community might benefit from, as the current ecosystem doesn’t seem to handle these things.

One such component that seemed to be in a state that was suitable to split off from was a small helper module to implement a particular http signature scheme that seems to be rather common in the activitypub scene.

In particular, the scheme I’m referring to is defined here: draft-cavage-http-signatures-12


                         Signing HTTP Messages
                    draft-cavage-http-signatures-12

Abstract
   When communicating over the Internet using the HTTP protocol, it can
   be desirable for a server or client to authenticate the sender of a
   particular message.  It can also be desirable to ensure that the
   message was not tampered with during transit.  This document
   describes a way for servers and clients to simultaneously add
   authentication and message integrity to HTTP messages by using a
   digital signature.

I’ve written a small library that glues together some components in the OCaml ecosystem to somewhat handle the signing (I have been mainly working off an “implement-enough-to-make-the-system-work” process rather than directly transcribing the specification above):

(** [verify ~signed_string ~signature key] returns true iff
   [signature] over [signed_string] is valid according to [key]. *)
val verify: signed_string:string -> signature:string -> X509.Public_key.t -> bool

(** [verify_request ~resolve_public_key req] verifies that a dream
   request has been signed according to the HTTP signature scheme *)
val verify_request:
  resolve_public_key:(string -> (X509.Public_key.t, 'a) Lwt_result.t) ->
  Dream.request -> (bool, 'a) result Lwt.t


(** [build_signed_headers ~priv_key ~key_id ~headers ~body_str
   ~current_time ~method_ ~uri] returns a list of signed headers using
   [priv_key] according to the HTTP signature scheme. [key_id] should
   be a string that can be used to look up the public key associated
   with [priv_key]. *)
val build_signed_headers:
  priv_key:X509.Private_key.t ->
  key_id:string ->
  headers:string StringMap.t ->
  body_str:string ->
  current_time:Ptime.t -> method_:string -> uri:Uri.t -> (string * string) list

The library is currently published at GitHub - Gopiandcode/http_sig_ocaml: HTTP Signatures for OCaml under the LGPL, but I haven’t released it on opam.

Anyway, I was wondering if anyone else had interest in this kind of package, and whether it would be a good candidate for submission to opam - or if there are actually already existing libraries in the OCaml ecosystem that would actually already do this.

1 Like

I had to do this recently but kept things basic and tailored to the usecase at hand relying on some assumptions.

With help I figured the solution at https://codeberg.org/mro/seppo/src/commit/841f6dbcaf737b1343804180d07e0f6ea9efa47d/lib/as2.ml#L484 and personally find it too trivial for a library and am opinionated about design decisions. But tastes differ – some even find a leftpad package a good idea.

Heyo @mro!

While I’m not sure I fully appreciate your allusions to leftpad, let me try and respond to some of your points.

Looking at your library, what you probably meant to link to was: seppo/http.ml at 841f6dbcaf737b1343804180d07e0f6ea9efa47d - seppo - Codeberg.org, which is the file that actually implements the http signatures:

let signed_headers (fkt_sign : string -> string) sndr date dige uri =
  let hdr = [
    ("(request-target)", "post " ^ Uri.path_and_query uri);
    ("host", uri |> Uri.host |> Option.get);
    ("date", date |> to_rfc1123);
    ("digest", dige);
  ] |> Cohttp.Header.of_list in
  let open Cohttp in
  let txt = Printf.sprintf
      "keyId=\"%s#main-key\",\
       algorithm=\"rsa-sha256\",\
       headers=\"(request-target) host date digest\",\
       signature=\"%s\""
      (sndr |> Uri.to_string)
      (hdr |> Header.to_frames |> String.concat "\n" |> fkt_sign)
  and hdr = Header.remove hdr "(request-target)" in
  Header.add hdr "signature" txt

Indeed, I too would agree that this code of only a few lines is far too trivial to be made into a library - it barely implements the specification, hardcodes the number of permitted headers, and in general, isn’t really an extensible or general function that would be suitable to export as a library. Certainly, if one were to expose this as a library, it might conjure up an image of a left-pad like fiasco.

Additionally, skimming through the code in that file, the code there doesn’t seem like it implements signature validation either, which is also another part of the http signatures spec that people seem to frequently use, and I guess is an important thing to implement if you actually want to create a “secure” (for some definition of secure) server.

The reason I considered releasing this module as a library was primarily because it seems like most languages actually have a dedicated library to provide http signatures:

As such, when reading through the code of other servers while implementing my own, it was rather annoying to have to take a tangent to implement signature signing and validation, when so many other languages can simply rely on libraries to provide that functionality.

Anyway, the point is, while the library that I have proposed is not by any means a battle-tested or exhaustive implementation of the http signatures spec, it implements a sufficiently non-trivial subset of the specification in a general enough way to rival that of the libraries provided by other languages.

There are some parts of the interface itself that could use a little work - for example, I don’t necessarily think tie-ing the library to Dream or Cohttp is ideal, and that could be refactored out eventually, but the contribution itself should be self-apparent.

1 Like

@gopiandcode wasn’t @mro talking about his own library ?

In any case if you feel it’s good and can afford the time to maintain it (hint: if you work well and your dependencies are stable, there’s little to maintain with OCaml) just go for it.

Alternatively you can try to find a relevant project (e.g. dream) that would be willing to accept it.

1 Like

Perhaps, yes, that could have been the case - ahh, the ambiguity of language; maybe I did react too hastily, nevertheless, I guess it was still worthwhile to justify the merits of a having a standalone http_sig library.

Yes, this was my plan in the end, my main aim was just to judge the priority I should assign to wrapping it up into an interface. If it’s something that might be immediately useful to others, I could probably do it over the weekend. Otherwise, I guess I’ll just leave it on the backlog and save actually publishing it in full for a rainy day.