Cryptographic signing and verification for HTTP requests

[[ 🗃 ^vM99v http-signature ]] :: [📥 Inbox] [📤 Outbox] [🐤 Followers] [🤝 Collaborators] [🛠 Changes]

Clone

HTTPS: darcs clone https://vervis.peers.community/repos/vM99v

SSH: darcs clone USERNAME@vervis.peers.community:vM99v

Tags

TODO

test /

Test.hs

{- This file is part of http-signature.
 -
 - Written in 2019 by fr33domlover <fr33domlover@riseup.net>.
 -
 - ♡ Copying is an act of love. Please copy, reuse and share.
 -
 - The author(s) have dedicated all copyright and related and neighboring
 - rights to this software to the public domain worldwide. This software is
 - distributed without any warranty.
 -
 - You should have received a copy of the CC0 Public Domain Dedication along
 - with this software. If not, see
 - <http://creativecommons.org/publicdomain/zero/1.0/>.
 -}

{-# LANGUAGE OverloadedStrings #-}

import Control.Exception
import Control.Monad
import Data.List.NonEmpty (NonEmpty (..))
import Data.Maybe
import Data.Time.Clock
import Network.HTTP.Date
import Network.HTTP.Types.Header
import System.Exit

import Network.HTTP.Signature

import qualified Data.ByteArray as BA (convert)
import qualified Crypto.Error as E
import qualified Crypto.PubKey.Ed25519 as E

main :: IO ()
main = do
    secretKey <- E.generateSecretKey
    let publicKey = E.toPublic secretKey

    now <- getCurrentTime
    let hTest = "CustomTestHeader"
        keyId = KeyId "https://dev.angeley.es/test-akey999999999"
        headersToSign = hRequestTarget :| [hDate, hHost, hTest]
        sign b = Signature $ BA.convert $ E.sign secretKey publicKey b
        request = Request
            { requestMethod  = "POST"
            , requestPath    = "/s/fr33"
            , requestHeaders =
                [ (hHost       , "forge.angeley.es")
                , (hDate       , formatHTTPDate $ utcToHTTPDate now)
                , (hContentType, "application/json; charset=utf-8")
                , (hTest       , "Hello world!")
                ]
            }

    sigHeader <-
        case signRequest headersToSign Nothing keyId sign request of
            Left e -> die $ displayException e
            Right b -> return b

    let request' = request
            { requestHeaders = (hSignature, sigHeader) : requestHeaders request
            }
        now' = addUTCTime 20 now
        wants = [hDate, hHost, hTest, hRequestTarget]
        seconds = 30

    verification <-
        case prepareToVerify wants [] seconds now' request' of
            Left e -> die $ displayException e
            Right v -> return v

    unless (isNothing $ verAlgorithm verification) $
        die "Expected algorithm not to be specified"
    unless (verKeyId verification == keyId) $
        die "keyId mismatch"
    sig <-
        let Signature b = verSignature verification
        in  case E.signature b of
                E.CryptoFailed e ->
                    die $
                        "Ed25519 signature decoding failed: " ++
                        displayException e
                E.CryptoPassed s -> return s
    let valid = E.verify publicKey (verInput verification) sig
    unless valid $ die "Ed25519 signature verification says invalid"
[See repo JSON]