packages feed

hOpenPGP-3.7: Codec/Encryption/OpenPGP/Internal/CryptoCipherTypes.hs

-- CryptoCipherTypes.hs: shim for crypto-cipher-types stuff (current nettle)
-- Copyright © 2016-2026  Clint Adams
-- This software is released under the terms of the Expat license.
-- (See the LICENSE file).
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE PackageImports #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE UndecidableInstances #-}

module Codec.Encryption.OpenPGP.Internal.CryptoCipherTypes
    ( HOWrappedOldCCT (..)
    ) where

import Control.Error.Util (note)
import Data.Bifunctor (bimap)
import qualified Data.ByteArray as BA
import qualified Data.ByteString as B
import qualified "crypto-cipher-types" Crypto.Cipher.Types as OldCCT
import qualified "crypton" Crypto.Cipher.Types as CCT

import Codec.Encryption.OpenPGP.Internal.HOBlockCipher
import Codec.Encryption.OpenPGP.Types.Internal.Errors
    ( CipherError (..)
    )

newtype HOWrappedOldCCT a
    = HWOCCT a

instance
    OldCCT.BlockCipher cipher
    => HOBlockCipher (HOWrappedOldCCT cipher)
    where
    cipherInit key =
        let keyBS = BA.convert key :: B.ByteString
         in bimap
                (const (CipherOldInitFailed "nettle invalid key"))
                (HWOCCT . OldCCT.cipherInit)
                (OldCCT.makeKey keyBS)
    cipherName (HWOCCT c) = OldCCT.cipherName c
    cipherKeySize (HWOCCT c) = convertKSS . OldCCT.cipherKeySize $ c
    blockSize (HWOCCT c) = OldCCT.blockSize c
    ecbEncrypt (HWOCCT c) bs = Right (OldCCT.ecbEncrypt c bs)
    ecbDecrypt (HWOCCT c) bs = Right (OldCCT.ecbDecrypt c bs)
    cfbEncrypt (HWOCCT c) iv bs =
        hammerIV iv >>= \i -> return (OldCCT.cfbEncrypt c i bs)
    cfbDecrypt (HWOCCT c) iv bs =
        hammerIV iv >>= \i -> return (OldCCT.cfbDecrypt c i bs)
    paddedCfbEncrypt _ _ _ =
        Left CipherPaddingUnsupported
    paddedCfbDecrypt (HWOCCT cipher) iv ciphertext =
        hammerIV iv >>= \i ->
            return
                (B.take (B.length ciphertext) (OldCCT.cfbDecrypt cipher i padded))
      where
        padded =
            ciphertext
                `B.append` B.pack
                    ( replicate
                        ( OldCCT.blockSize cipher
                            - (B.length ciphertext `mod` OldCCT.blockSize cipher)
                        )
                        0
                    )
    aeadInit mode (HWOCCT c) iv =
        case OldCCT.aeadInit (convertMode mode) c iv of
            Nothing -> Left CipherAEADModeUnsupported
            Just (OldCCT.AEAD _ (OldCCT.AEADState st)) ->
                Right (CCT.AEAD (bridgeImpl c) st)
    aeadSimpleEncrypt aead aad pt plen =
        CCT.aeadSimpleEncrypt aead aad pt plen
    aeadSimpleDecrypt aead aad ct tag =
        CCT.aeadSimpleDecrypt aead aad ct tag

convertKSS :: OldCCT.KeySizeSpecifier -> CCT.KeySizeSpecifier
convertKSS (OldCCT.KeySizeRange a b) = CCT.KeySizeRange a b
convertKSS (OldCCT.KeySizeEnum as) = CCT.KeySizeEnum as
convertKSS (OldCCT.KeySizeFixed a) = CCT.KeySizeFixed a

convertMode :: CCT.AEADMode -> OldCCT.AEADMode
convertMode CCT.AEAD_GCM = OldCCT.AEAD_GCM
convertMode (CCT.AEAD_CCM 0 CCT.CCM_M16 CCT.CCM_L2) = OldCCT.AEAD_CCM
convertMode _ = OldCCT.AEAD_GCM

bridgeImpl
    :: OldCCT.AEADModeImpl cipher st => cipher -> CCT.AEADModeImpl st
bridgeImpl c =
    CCT.AEADModeImpl
        { CCT.aeadImplAppendHeader = \st' ba -> OldCCT.aeadStateAppendHeader c st' (BA.convert ba)
        , CCT.aeadImplEncrypt = \st' ba ->
            let (ct, st'') = OldCCT.aeadStateEncrypt c st' (BA.convert ba)
             in (BA.convert ct, st'')
        , CCT.aeadImplDecrypt = \st' ba ->
            let (pt, st'') = OldCCT.aeadStateDecrypt c st' (BA.convert ba)
             in (BA.convert pt, st'')
        , CCT.aeadImplFinalize = \st' plen ->
            let OldCCT.AuthTag bs = OldCCT.aeadStateFinalize c st' plen
             in CCT.AuthTag (BA.convert bs)
        }

hammerIV
    :: OldCCT.BlockCipher cipher
    => B.ByteString -> Either CipherError (OldCCT.IV cipher)
hammerIV = note (CipherBadIV "nettle") . OldCCT.makeIV