-- BlockCipher.hs: OpenPGP (RFC9580) block cipher stuff
-- Copyright © 2013-2026  Clint Adams
-- This software is released under the terms of the Expat license.
-- (See the LICENSE file).
{-# LANGUAGE RankNTypes #-}

module Codec.Encryption.OpenPGP.BlockCipher
    ( keySize
    , supportedSymmetricAlgorithmsForCFB
    , withSymmetricCipher
    ) where

import qualified Crypto.Cipher.AES as AES
import qualified Crypto.Cipher.Blowfish as Blowfish
import qualified Crypto.Cipher.Camellia as Camellia
import qualified Crypto.Cipher.TripleDES as TripleDES
import qualified Crypto.Nettle.Ciphers as CNC
import qualified Data.ByteString as B
import qualified Data.Set as Set

import Codec.Encryption.OpenPGP.Internal.CryptoCipherTypes
    ( HOWrappedOldCCT (..)
    )
import Codec.Encryption.OpenPGP.Internal.Crypton
    ( HOWrappedCCT (..)
    )
import Codec.Encryption.OpenPGP.Internal.HOBlockCipher
import Codec.Encryption.OpenPGP.Types
import Codec.Encryption.OpenPGP.Types.Internal.Errors
    ( CipherError (..)
    , renderCipherError
    )

type HOCipher a =
    forall cipher
     . HOBlockCipher cipher
    => cipher -> Either String a

withSymmetricCipher
    :: SymmetricAlgorithm
    -> B.ByteString
    -> HOCipher a
    -> Either CipherError a
withSymmetricCipher :: forall a.
SymmetricAlgorithm
-> ByteString -> HOCipher a -> Either CipherError a
withSymmetricCipher SymmetricAlgorithm
Plaintext ByteString
_ HOCipher a
_ = CipherError -> Either CipherError a
forall a b. a -> Either a b
Left (SymmetricAlgorithm -> CipherError
UnsupportedAlgorithm SymmetricAlgorithm
Plaintext)
withSymmetricCipher SymmetricAlgorithm
IDEA ByteString
_ HOCipher a
_ = CipherError -> Either CipherError a
forall a b. a -> Either a b
Left (SymmetricAlgorithm -> CipherError
UnsupportedAlgorithm SymmetricAlgorithm
IDEA)
withSymmetricCipher SymmetricAlgorithm
ReservedSAFER ByteString
_ HOCipher a
_ = CipherError -> Either CipherError a
forall a b. a -> Either a b
Left (SymmetricAlgorithm -> CipherError
UnsupportedAlgorithm SymmetricAlgorithm
ReservedSAFER)
withSymmetricCipher SymmetricAlgorithm
ReservedDES ByteString
_ HOCipher a
_ = CipherError -> Either CipherError a
forall a b. a -> Either a b
Left (SymmetricAlgorithm -> CipherError
UnsupportedAlgorithm SymmetricAlgorithm
ReservedDES)
withSymmetricCipher (OtherSA Word8
n) ByteString
_ HOCipher a
_ = CipherError -> Either CipherError a
forall a b. a -> Either a b
Left (SymmetricAlgorithm -> CipherError
UnsupportedAlgorithm (Word8 -> SymmetricAlgorithm
OtherSA Word8
n))
withSymmetricCipher SymmetricAlgorithm
CAST5 ByteString
keyBytes HOCipher a
f =
    SymmetricAlgorithm
-> Either String (HOWrappedOldCCT CAST128)
-> (HOWrappedOldCCT CAST128 -> Either String a)
-> Either CipherError a
forall cipher a.
HOBlockCipher cipher =>
SymmetricAlgorithm
-> Either String cipher
-> (cipher -> Either String a)
-> Either CipherError a
initAndRun
        SymmetricAlgorithm
CAST5
        ( ByteString -> Either String (HOWrappedOldCCT CAST128)
forall cipher.
HOBlockCipher cipher =>
ByteString -> Either String cipher
cipherInit ByteString
keyBytes
            :: Either String (HOWrappedOldCCT CNC.CAST128)
        )
        HOWrappedOldCCT CAST128 -> Either String a
HOCipher a
f
withSymmetricCipher SymmetricAlgorithm
Twofish ByteString
keyBytes HOCipher a
f =
    SymmetricAlgorithm
-> Either String (HOWrappedOldCCT TWOFISH)
-> (HOWrappedOldCCT TWOFISH -> Either String a)
-> Either CipherError a
forall cipher a.
HOBlockCipher cipher =>
SymmetricAlgorithm
-> Either String cipher
-> (cipher -> Either String a)
-> Either CipherError a
initAndRun
        SymmetricAlgorithm
Twofish
        ( ByteString -> Either String (HOWrappedOldCCT TWOFISH)
forall cipher.
HOBlockCipher cipher =>
ByteString -> Either String cipher
cipherInit ByteString
keyBytes
            :: Either String (HOWrappedOldCCT CNC.TWOFISH)
        )
        HOWrappedOldCCT TWOFISH -> Either String a
HOCipher a
f
withSymmetricCipher SymmetricAlgorithm
TripleDES ByteString
keyBytes HOCipher a
f =
    SymmetricAlgorithm
-> Either String (HOWrappedCCT DES_EDE3)
-> (HOWrappedCCT DES_EDE3 -> Either String a)
-> Either CipherError a
forall cipher a.
HOBlockCipher cipher =>
SymmetricAlgorithm
-> Either String cipher
-> (cipher -> Either String a)
-> Either CipherError a
initAndRun
        SymmetricAlgorithm
TripleDES
        ( ByteString -> Either String (HOWrappedCCT DES_EDE3)
forall cipher.
HOBlockCipher cipher =>
ByteString -> Either String cipher
cipherInit ByteString
keyBytes
            :: Either String (HOWrappedCCT TripleDES.DES_EDE3)
        )
        HOWrappedCCT DES_EDE3 -> Either String a
HOCipher a
f
withSymmetricCipher SymmetricAlgorithm
Blowfish ByteString
keyBytes HOCipher a
f =
    SymmetricAlgorithm
-> Either String (HOWrappedCCT Blowfish128)
-> (HOWrappedCCT Blowfish128 -> Either String a)
-> Either CipherError a
forall cipher a.
HOBlockCipher cipher =>
SymmetricAlgorithm
-> Either String cipher
-> (cipher -> Either String a)
-> Either CipherError a
initAndRun
        SymmetricAlgorithm
Blowfish
        ( ByteString -> Either String (HOWrappedCCT Blowfish128)
forall cipher.
HOBlockCipher cipher =>
ByteString -> Either String cipher
cipherInit ByteString
keyBytes
            :: Either String (HOWrappedCCT Blowfish.Blowfish128)
        )
        HOWrappedCCT Blowfish128 -> Either String a
HOCipher a
f
withSymmetricCipher SymmetricAlgorithm
AES128 ByteString
keyBytes HOCipher a
f =
    SymmetricAlgorithm
-> Either String (HOWrappedCCT AES128)
-> (HOWrappedCCT AES128 -> Either String a)
-> Either CipherError a
forall cipher a.
HOBlockCipher cipher =>
SymmetricAlgorithm
-> Either String cipher
-> (cipher -> Either String a)
-> Either CipherError a
initAndRun
        SymmetricAlgorithm
AES128
        (ByteString -> Either String (HOWrappedCCT AES128)
forall cipher.
HOBlockCipher cipher =>
ByteString -> Either String cipher
cipherInit ByteString
keyBytes :: Either String (HOWrappedCCT AES.AES128))
        HOWrappedCCT AES128 -> Either String a
HOCipher a
f
withSymmetricCipher SymmetricAlgorithm
AES192 ByteString
keyBytes HOCipher a
f =
    SymmetricAlgorithm
-> Either String (HOWrappedCCT AES192)
-> (HOWrappedCCT AES192 -> Either String a)
-> Either CipherError a
forall cipher a.
HOBlockCipher cipher =>
SymmetricAlgorithm
-> Either String cipher
-> (cipher -> Either String a)
-> Either CipherError a
initAndRun
        SymmetricAlgorithm
AES192
        (ByteString -> Either String (HOWrappedCCT AES192)
forall cipher.
HOBlockCipher cipher =>
ByteString -> Either String cipher
cipherInit ByteString
keyBytes :: Either String (HOWrappedCCT AES.AES192))
        HOWrappedCCT AES192 -> Either String a
HOCipher a
f
withSymmetricCipher SymmetricAlgorithm
AES256 ByteString
keyBytes HOCipher a
f =
    SymmetricAlgorithm
-> Either String (HOWrappedCCT AES256)
-> (HOWrappedCCT AES256 -> Either String a)
-> Either CipherError a
forall cipher a.
HOBlockCipher cipher =>
SymmetricAlgorithm
-> Either String cipher
-> (cipher -> Either String a)
-> Either CipherError a
initAndRun
        SymmetricAlgorithm
AES256
        (ByteString -> Either String (HOWrappedCCT AES256)
forall cipher.
HOBlockCipher cipher =>
ByteString -> Either String cipher
cipherInit ByteString
keyBytes :: Either String (HOWrappedCCT AES.AES256))
        HOWrappedCCT AES256 -> Either String a
HOCipher a
f
withSymmetricCipher SymmetricAlgorithm
Camellia128 ByteString
keyBytes HOCipher a
f =
    SymmetricAlgorithm
-> Either String (HOWrappedCCT Camellia128)
-> (HOWrappedCCT Camellia128 -> Either String a)
-> Either CipherError a
forall cipher a.
HOBlockCipher cipher =>
SymmetricAlgorithm
-> Either String cipher
-> (cipher -> Either String a)
-> Either CipherError a
initAndRun
        SymmetricAlgorithm
Camellia128
        ( ByteString -> Either String (HOWrappedCCT Camellia128)
forall cipher.
HOBlockCipher cipher =>
ByteString -> Either String cipher
cipherInit ByteString
keyBytes
            :: Either String (HOWrappedCCT Camellia.Camellia128)
        )
        HOWrappedCCT Camellia128 -> Either String a
HOCipher a
f
withSymmetricCipher SymmetricAlgorithm
Camellia192 ByteString
keyBytes HOCipher a
f =
    SymmetricAlgorithm
-> Either String (HOWrappedOldCCT Camellia192)
-> (HOWrappedOldCCT Camellia192 -> Either String a)
-> Either CipherError a
forall cipher a.
HOBlockCipher cipher =>
SymmetricAlgorithm
-> Either String cipher
-> (cipher -> Either String a)
-> Either CipherError a
initAndRun
        SymmetricAlgorithm
Camellia192
        ( ByteString -> Either String (HOWrappedOldCCT Camellia192)
forall cipher.
HOBlockCipher cipher =>
ByteString -> Either String cipher
cipherInit ByteString
keyBytes
            :: Either String (HOWrappedOldCCT CNC.Camellia192)
        )
        HOWrappedOldCCT Camellia192 -> Either String a
HOCipher a
f
withSymmetricCipher SymmetricAlgorithm
Camellia256 ByteString
keyBytes HOCipher a
f =
    SymmetricAlgorithm
-> Either String (HOWrappedOldCCT Camellia256)
-> (HOWrappedOldCCT Camellia256 -> Either String a)
-> Either CipherError a
forall cipher a.
HOBlockCipher cipher =>
SymmetricAlgorithm
-> Either String cipher
-> (cipher -> Either String a)
-> Either CipherError a
initAndRun
        SymmetricAlgorithm
Camellia256
        ( ByteString -> Either String (HOWrappedOldCCT Camellia256)
forall cipher.
HOBlockCipher cipher =>
ByteString -> Either String cipher
cipherInit ByteString
keyBytes
            :: Either String (HOWrappedOldCCT CNC.Camellia256)
        )
        HOWrappedOldCCT Camellia256 -> Either String a
HOCipher a
f

{- | Symmetric algorithms that the CFB (SEIPDv1) encryption backend can use for
new *encryption*, restricted to the RFC 9580 §9.3-permitted set.

This is the intersection of:
  * algorithms 'withSymmetricCipher' can actually encrypt
    (`CAST5`, `Twofish`, `TripleDES`, `Blowfish`, `AES128/192/256`,
    `Camellia128/192/256`), minus
  * algorithms RFC 9580 §9.3 forbids for new encryption (`IDEA`, `TripleDES`,
    `CAST5`).

Decryption backward-compatibility is unaffected: 'withSymmetricCipher' still
handles all ten algorithms, including the three forbidden above.
-}
supportedSymmetricAlgorithmsForCFB :: Set.Set SymmetricAlgorithm
supportedSymmetricAlgorithmsForCFB :: Set SymmetricAlgorithm
supportedSymmetricAlgorithmsForCFB =
    [SymmetricAlgorithm] -> Set SymmetricAlgorithm
forall a. Ord a => [a] -> Set a
Set.fromList
        [ SymmetricAlgorithm
Twofish
        , SymmetricAlgorithm
Blowfish
        , SymmetricAlgorithm
AES128
        , SymmetricAlgorithm
AES192
        , SymmetricAlgorithm
AES256
        , SymmetricAlgorithm
Camellia128
        , SymmetricAlgorithm
Camellia192
        , SymmetricAlgorithm
Camellia256
        ]

initAndRun
    :: HOBlockCipher cipher
    => SymmetricAlgorithm
    -> Either String cipher
    -> (cipher -> Either String a)
    -> Either CipherError a
initAndRun :: forall cipher a.
HOBlockCipher cipher =>
SymmetricAlgorithm
-> Either String cipher
-> (cipher -> Either String a)
-> Either CipherError a
initAndRun SymmetricAlgorithm
algo Either String cipher
initResult cipher -> Either String a
f =
    case Either String cipher
initResult of
        Left String
err -> CipherError -> Either CipherError a
forall a b. a -> Either a b
Left (SymmetricAlgorithm -> String -> CipherError
CipherInitFailed SymmetricAlgorithm
algo String
err)
        Right cipher
c ->
            case cipher -> Either String a
f cipher
c of
                Left String
err -> CipherError -> Either CipherError a
forall a b. a -> Either a b
Left (String -> CipherError
CipherOperationFailed String
err)
                Right a
x -> a -> Either CipherError a
forall a b. b -> Either a b
Right a
x

-- In octets. Keep this as an explicit OpenPGP algorithm mapping so behavior
-- stays stable across mixed backends (crypton/nettle) and includes unsupported
-- algorithms that never reach backend cipher types.
keySize :: SymmetricAlgorithm -> Either CipherError Int
keySize :: SymmetricAlgorithm -> Either CipherError Int
keySize SymmetricAlgorithm
Plaintext = Int -> Either CipherError Int
forall a b. b -> Either a b
Right Int
0
keySize SymmetricAlgorithm
IDEA = Int -> Either CipherError Int
forall a b. b -> Either a b
Right Int
16
keySize SymmetricAlgorithm
TripleDES = Int -> Either CipherError Int
forall a b. b -> Either a b
Right Int
24
keySize SymmetricAlgorithm
CAST5 = Int -> Either CipherError Int
forall a b. b -> Either a b
Right Int
16
keySize SymmetricAlgorithm
Blowfish = Int -> Either CipherError Int
forall a b. b -> Either a b
Right Int
16
keySize SymmetricAlgorithm
ReservedSAFER = CipherError -> Either CipherError Int
forall a b. a -> Either a b
Left (SymmetricAlgorithm -> CipherError
UnsupportedAlgorithm SymmetricAlgorithm
ReservedSAFER)
keySize SymmetricAlgorithm
ReservedDES = CipherError -> Either CipherError Int
forall a b. a -> Either a b
Left (SymmetricAlgorithm -> CipherError
UnsupportedAlgorithm SymmetricAlgorithm
ReservedDES)
keySize SymmetricAlgorithm
AES128 = Int -> Either CipherError Int
forall a b. b -> Either a b
Right Int
16
keySize SymmetricAlgorithm
AES192 = Int -> Either CipherError Int
forall a b. b -> Either a b
Right Int
24
keySize SymmetricAlgorithm
AES256 = Int -> Either CipherError Int
forall a b. b -> Either a b
Right Int
32
keySize SymmetricAlgorithm
Twofish = Int -> Either CipherError Int
forall a b. b -> Either a b
Right Int
32
keySize SymmetricAlgorithm
Camellia128 = Int -> Either CipherError Int
forall a b. b -> Either a b
Right Int
16
keySize SymmetricAlgorithm
Camellia192 = Int -> Either CipherError Int
forall a b. b -> Either a b
Right Int
24
keySize SymmetricAlgorithm
Camellia256 = Int -> Either CipherError Int
forall a b. b -> Either a b
Right Int
32
keySize (OtherSA Word8
n) = CipherError -> Either CipherError Int
forall a b. a -> Either a b
Left (SymmetricAlgorithm -> CipherError
UnsupportedAlgorithm (Word8 -> SymmetricAlgorithm
OtherSA Word8
n))