-- This file is part of the Wire Server implementation.
--
-- Copyright (C) 2023 Wire Swiss GmbH <opensource@wire.com>
--
-- This program is free software: you can redistribute it and/or modify it under
-- the terms of the GNU Affero General Public License as published by the Free
-- Software Foundation, either version 3 of the License, or (at your option) any
-- later version.
--
-- This program is distributed in the hope that it will be useful, but WITHOUT
-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
-- details.
--
-- You should have received a copy of the GNU Affero General Public License along
-- with this program. If not, see <https://www.gnu.org/licenses/>.

module Wire.API.MLS.ECDSA where

import Crypto.Error
import Crypto.Hash hiding (hash)
import Crypto.PubKey.ECDSA
import Data.ASN1.BinaryEncoding
import Data.ASN1.Encoding
import Data.ASN1.Prim
import Data.Proxy
import Imports
import Wire.API.MLS.Serialisation

-- | Decode an ECDSA signature.
decodeSignature ::
  forall curve.
  (EllipticCurveECDSA curve) =>
  Proxy curve ->
  ByteString ->
  Maybe (Signature curve)
decodeSignature :: forall curve.
EllipticCurveECDSA curve =>
Proxy curve -> ByteString -> Maybe (Signature curve)
decodeSignature Proxy curve
curve ByteString
bs = do
  (Integer, Integer)
ints <- case DER -> ByteString -> Either ASN1Error [ASN1]
forall a.
ASN1Decoding a =>
a -> ByteString -> Either ASN1Error [ASN1]
decodeASN1' DER
DER ByteString
bs of
    Right ([Start ASN1ConstructionType
Sequence, IntVal Integer
r, IntVal Integer
s, End ASN1ConstructionType
Sequence]) -> (Integer, Integer) -> Maybe (Integer, Integer)
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Integer
r, Integer
s)
    Either ASN1Error [ASN1]
_ -> Maybe (Integer, Integer)
forall a. Maybe a
Nothing
  CryptoFailable (Signature curve) -> Maybe (Signature curve)
forall a. CryptoFailable a -> Maybe a
maybeCryptoError (CryptoFailable (Signature curve) -> Maybe (Signature curve))
-> CryptoFailable (Signature curve) -> Maybe (Signature curve)
forall a b. (a -> b) -> a -> b
$ Proxy curve
-> (Integer, Integer) -> CryptoFailable (Signature curve)
forall curve (proxy :: * -> *).
EllipticCurveECDSA curve =>
proxy curve
-> (Integer, Integer) -> CryptoFailable (Signature curve)
signatureFromIntegers Proxy curve
curve (Integer, Integer)
ints

-- Encode an ECDSA signature.
encodeSignature ::
  forall curve.
  (EllipticCurveECDSA curve) =>
  Proxy curve ->
  Signature curve ->
  ByteString
encodeSignature :: forall curve.
EllipticCurveECDSA curve =>
Proxy curve -> Signature curve -> ByteString
encodeSignature Proxy curve
curve Signature curve
sig = case Proxy curve -> Signature curve -> (Integer, Integer)
forall curve (proxy :: * -> *).
EllipticCurveECDSA curve =>
proxy curve -> Signature curve -> (Integer, Integer)
signatureToIntegers Proxy curve
curve Signature curve
sig of
  (Integer
r, Integer
s) ->
    DER -> [ASN1] -> ByteString
forall a. ASN1Encoding a => a -> [ASN1] -> ByteString
encodeASN1'
      DER
DER
      [ ASN1ConstructionType -> ASN1
Start ASN1ConstructionType
Sequence,
        Integer -> ASN1
IntVal Integer
r,
        Integer -> ASN1
IntVal Integer
s,
        ASN1ConstructionType -> ASN1
End ASN1ConstructionType
Sequence
      ]

verifySignature ::
  forall curve a hash.
  ( EllipticCurveECDSA curve,
    HashAlgorithm hash
  ) =>
  Proxy curve ->
  hash ->
  ByteString ->
  RawMLS a ->
  ByteString ->
  Bool
verifySignature :: forall curve a hash.
(EllipticCurveECDSA curve, HashAlgorithm hash) =>
Proxy curve -> hash -> ByteString -> RawMLS a -> ByteString -> Bool
verifySignature Proxy curve
curve hash
hash ByteString
pub RawMLS a
x ByteString
sig =
  Bool -> Maybe Bool -> Bool
forall a. a -> Maybe a -> a
fromMaybe Bool
False (Maybe Bool -> Bool) -> Maybe Bool -> Bool
forall a b. (a -> b) -> a -> b
$ do
    Signature curve
sig' <- Proxy curve -> ByteString -> Maybe (Signature curve)
forall curve.
EllipticCurveECDSA curve =>
Proxy curve -> ByteString -> Maybe (Signature curve)
decodeSignature Proxy curve
curve ByteString
sig
    Point curve
pub' <- CryptoFailable (Point curve) -> Maybe (Point curve)
forall a. CryptoFailable a -> Maybe a
maybeCryptoError (CryptoFailable (Point curve) -> Maybe (Point curve))
-> CryptoFailable (Point curve) -> Maybe (Point curve)
forall a b. (a -> b) -> a -> b
$ Proxy curve -> ByteString -> CryptoFailable (Point curve)
forall curve bs (proxy :: * -> *).
(EllipticCurve curve, ByteArray bs) =>
proxy curve -> bs -> CryptoFailable (PublicKey curve)
decodePublic Proxy curve
curve ByteString
pub
    let valid :: Bool
valid = Proxy curve
-> hash -> Point curve -> Signature curve -> ByteString -> Bool
forall curve msg hash (proxy :: * -> *).
(EllipticCurveECDSA curve, ByteArrayAccess msg,
 HashAlgorithm hash) =>
proxy curve
-> hash -> PublicKey curve -> Signature curve -> msg -> Bool
verify Proxy curve
curve hash
hash Point curve
pub' Signature curve
sig' RawMLS a
x.raw
    Bool -> Maybe Bool
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
valid