packages feed

libjwt-typed-0.2: src/Libjwt/Algorithms.hs

--   This Source Code Form is subject to the terms of the Mozilla Public
--   License, v. 2.0. If a copy of the MPL was not distributed with this
--   file, You can obtain one at http://mozilla.org/MPL/2.0/.

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}

-- | Algorithms used to sign and validate JWT signatures
module Libjwt.Algorithms
  ( Algorithm(..)
  , RsaKey
  , EcKey
  , toHeaderAlg
  , jwtAlgWithKey
  )
where

import           Libjwt.Header                  ( Alg(..) )
import           Libjwt.FFI.Libjwt
import           Libjwt.Keys

import           Data.Kind                      ( Constraint )

import           GHC.TypeLits

-- | Cryptographic algorithm used to secure the JWT
data Algorithm k where
  -- | HMAC SHA-256 (secret key must be __at least 256 bits in size__)
  HMAC256  ::Secret  -> Algorithm Secret
  -- | HMAC SHA-384 (secret key must be __at least 384 bits in size__)
  HMAC384  ::Secret  -> Algorithm Secret
  -- | HMAC SHA-512 (secret key must be __at least 512 bits in size__)
  HMAC512  ::Secret  -> Algorithm Secret
  -- | RSASSA-PKCS1-v1_5 SHA-256 (a key of size __2048 bits or larger__ must be used with this algorithm)
  RSA256   ::RsaKey r => r -> Algorithm r
  -- | RSASSA-PKCS1-v1_5 SHA-384 (a key of size __2048 bits or larger__ must be used with this algorithm) 
  RSA384   ::RsaKey r => r -> Algorithm r
  -- | RSASSA-PKCS1-v1_5 SHA-512 (a key of size __2048 bits or larger__ must be used with this algorithm)
  RSA512   ::RsaKey r => r -> Algorithm r
  -- | ECDSA with P-256 curve and SHA-256
  ECDSA256 ::EcKey e  => e -> Algorithm e
  -- | ECDSA with P-384 curve and SHA-384
  ECDSA384 ::EcKey e  => e -> Algorithm e
  -- | ECDSA with P-521 curve and SHA-512
  ECDSA512 ::EcKey e  => e -> Algorithm e
  -- | None
  AlgNone  ::Algorithm ()

deriving stock instance Show k => Show (Algorithm k)

type family RsaKey t :: Constraint where
  RsaKey RsaKeyPair = ()
  RsaKey RsaPubKey  = ()
  RsaKey a          = TypeError ('Text "RSASSA-PKCS-v1_5 cannot be used with " ':<>: 'ShowType a)

type family EcKey t :: Constraint where
  EcKey EcKeyPair = ()
  EcKey EcPubKey  = ()
  EcKey a         = TypeError ('Text "ECDSA cannot be used with " ':<>: 'ShowType a)

jwtAlgWithKey :: Algorithm k -> (JwtAlgT, k)
jwtAlgWithKey (HMAC256  secret) = (jwtAlgHs256, secret)
jwtAlgWithKey (HMAC384  secret) = (jwtAlgHs384, secret)
jwtAlgWithKey (HMAC512  secret) = (jwtAlgHs512, secret)
jwtAlgWithKey (RSA256   key   ) = (jwtAlgRs256, key)
jwtAlgWithKey (RSA384   key   ) = (jwtAlgRs384, key)
jwtAlgWithKey (RSA512   key   ) = (jwtAlgRs512, key)
jwtAlgWithKey (ECDSA256 key   ) = (jwtAlgEs256, key)
jwtAlgWithKey (ECDSA384 key   ) = (jwtAlgEs384, key)
jwtAlgWithKey (ECDSA512 key   ) = (jwtAlgEs512, key)
jwtAlgWithKey AlgNone           = (jwtAlgNone, ())

-- | Get the header parameter "alg" from the algorithm
--
-- +------------+-----------+
-- |Algorithm   |  'alg'    |   
-- +============+===========+
-- |'HMAC256'   |  'HS256'  |
-- +------------+-----------+
-- |'HMAC384'   |  'HS384'  |
-- +------------+-----------+
-- |'HMAC512'   |  'HS512'  |
-- +------------+-----------+
-- |'RSA256'    |  'RS256'  | 
-- +------------+-----------+
-- |'RSA384'    |  'RS384'  |
-- +------------+-----------+
-- |'RSA512'    |  'RS512'  |
-- +------------+-----------+
-- |'ECDSA256'  |  'ES256'  |
-- +------------+-----------+
-- |'ECDSA384'  |  'ES384'  |
-- +------------+-----------+
-- |'ECDSA512'  |  'ES512'  |
-- +------------+-----------+
--
toHeaderAlg :: Algorithm k -> Alg
toHeaderAlg (HMAC256  _) = HS256
toHeaderAlg (HMAC384  _) = HS384
toHeaderAlg (HMAC512  _) = HS512
toHeaderAlg (RSA256   _) = RS256
toHeaderAlg (RSA384   _) = RS384
toHeaderAlg (RSA512   _) = RS512
toHeaderAlg (ECDSA256 _) = ES256
toHeaderAlg (ECDSA384 _) = ES384
toHeaderAlg (ECDSA512 _) = ES512
toHeaderAlg AlgNone      = None