-- 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
    , withAEADCipher
    ) 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 Codec.Encryption.OpenPGP.Internal.CryptoCipherTypes
    ( HOWrappedOldCCT (..)
    )
import Codec.Encryption.OpenPGP.Internal.Crypton
    ( HOWrappedCCT (..)
    )
import Codec.Encryption.OpenPGP.Internal.HOBlockCipher
import Codec.Encryption.OpenPGP.Types

type HOCipher a =
    forall cipher
     . HOBlockCipher cipher
    => cipher -> Either CipherError 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
CipherUnsupportedAlgorithm SymmetricAlgorithm
Plaintext)
withSymmetricCipher SymmetricAlgorithm
IDEA ByteString
_ HOCipher a
_ = CipherError -> Either CipherError a
forall a b. a -> Either a b
Left (SymmetricAlgorithm -> CipherError
CipherUnsupportedAlgorithm SymmetricAlgorithm
IDEA)
withSymmetricCipher SymmetricAlgorithm
ReservedSAFER ByteString
_ HOCipher a
_ = CipherError -> Either CipherError a
forall a b. a -> Either a b
Left (SymmetricAlgorithm -> CipherError
CipherUnsupportedAlgorithm SymmetricAlgorithm
ReservedSAFER)
withSymmetricCipher SymmetricAlgorithm
ReservedDES ByteString
_ HOCipher a
_ = CipherError -> Either CipherError a
forall a b. a -> Either a b
Left (SymmetricAlgorithm -> CipherError
CipherUnsupportedAlgorithm SymmetricAlgorithm
ReservedDES)
withSymmetricCipher (OtherSA Word8
n) ByteString
_ HOCipher a
_ = CipherError -> Either CipherError a
forall a b. a -> Either a b
Left (SymmetricAlgorithm -> CipherError
CipherUnsupportedAlgorithm (Word8 -> SymmetricAlgorithm
OtherSA Word8
n))
withSymmetricCipher SymmetricAlgorithm
CAST5 ByteString
keyBytes HOCipher a
f =
    ( ByteString -> Either CipherError (HOWrappedOldCCT CAST128)
forall key.
ByteArray key =>
key -> Either CipherError (HOWrappedOldCCT CAST128)
forall cipher key.
(HOBlockCipher cipher, ByteArray key) =>
key -> Either CipherError cipher
cipherInit ByteString
keyBytes
        :: Either CipherError (HOWrappedOldCCT CNC.CAST128)
    )
        Either CipherError (HOWrappedOldCCT CAST128)
-> (HOWrappedOldCCT CAST128 -> Either CipherError a)
-> Either CipherError a
forall a b.
Either CipherError a
-> (a -> Either CipherError b) -> Either CipherError b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= HOWrappedOldCCT CAST128 -> Either CipherError a
HOCipher a
f
withSymmetricCipher SymmetricAlgorithm
Twofish ByteString
keyBytes HOCipher a
f =
    ( ByteString -> Either CipherError (HOWrappedOldCCT TWOFISH)
forall key.
ByteArray key =>
key -> Either CipherError (HOWrappedOldCCT TWOFISH)
forall cipher key.
(HOBlockCipher cipher, ByteArray key) =>
key -> Either CipherError cipher
cipherInit ByteString
keyBytes
        :: Either CipherError (HOWrappedOldCCT CNC.TWOFISH)
    )
        Either CipherError (HOWrappedOldCCT TWOFISH)
-> (HOWrappedOldCCT TWOFISH -> Either CipherError a)
-> Either CipherError a
forall a b.
Either CipherError a
-> (a -> Either CipherError b) -> Either CipherError b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= HOWrappedOldCCT TWOFISH -> Either CipherError a
HOCipher a
f
withSymmetricCipher SymmetricAlgorithm
TripleDES ByteString
keyBytes HOCipher a
f =
    ( ByteString -> Either CipherError (HOWrappedCCT DES_EDE3)
forall key.
ByteArray key =>
key -> Either CipherError (HOWrappedCCT DES_EDE3)
forall cipher key.
(HOBlockCipher cipher, ByteArray key) =>
key -> Either CipherError cipher
cipherInit ByteString
keyBytes
        :: Either CipherError (HOWrappedCCT TripleDES.DES_EDE3)
    )
        Either CipherError (HOWrappedCCT DES_EDE3)
-> (HOWrappedCCT DES_EDE3 -> Either CipherError a)
-> Either CipherError a
forall a b.
Either CipherError a
-> (a -> Either CipherError b) -> Either CipherError b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= HOWrappedCCT DES_EDE3 -> Either CipherError a
HOCipher a
f
withSymmetricCipher SymmetricAlgorithm
Blowfish ByteString
keyBytes HOCipher a
f =
    ( ByteString -> Either CipherError (HOWrappedCCT Blowfish128)
forall key.
ByteArray key =>
key -> Either CipherError (HOWrappedCCT Blowfish128)
forall cipher key.
(HOBlockCipher cipher, ByteArray key) =>
key -> Either CipherError cipher
cipherInit ByteString
keyBytes
        :: Either CipherError (HOWrappedCCT Blowfish.Blowfish128)
    )
        Either CipherError (HOWrappedCCT Blowfish128)
-> (HOWrappedCCT Blowfish128 -> Either CipherError a)
-> Either CipherError a
forall a b.
Either CipherError a
-> (a -> Either CipherError b) -> Either CipherError b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= HOWrappedCCT Blowfish128 -> Either CipherError a
HOCipher a
f
withSymmetricCipher SymmetricAlgorithm
AES128 ByteString
keyBytes HOCipher a
f =
    ( ByteString -> Either CipherError (HOWrappedCCT AES128)
forall key.
ByteArray key =>
key -> Either CipherError (HOWrappedCCT AES128)
forall cipher key.
(HOBlockCipher cipher, ByteArray key) =>
key -> Either CipherError cipher
cipherInit ByteString
keyBytes
        :: Either CipherError (HOWrappedCCT AES.AES128)
    )
        Either CipherError (HOWrappedCCT AES128)
-> (HOWrappedCCT AES128 -> Either CipherError a)
-> Either CipherError a
forall a b.
Either CipherError a
-> (a -> Either CipherError b) -> Either CipherError b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= HOWrappedCCT AES128 -> Either CipherError a
HOCipher a
f
withSymmetricCipher SymmetricAlgorithm
AES192 ByteString
keyBytes HOCipher a
f =
    ( ByteString -> Either CipherError (HOWrappedCCT AES192)
forall key.
ByteArray key =>
key -> Either CipherError (HOWrappedCCT AES192)
forall cipher key.
(HOBlockCipher cipher, ByteArray key) =>
key -> Either CipherError cipher
cipherInit ByteString
keyBytes
        :: Either CipherError (HOWrappedCCT AES.AES192)
    )
        Either CipherError (HOWrappedCCT AES192)
-> (HOWrappedCCT AES192 -> Either CipherError a)
-> Either CipherError a
forall a b.
Either CipherError a
-> (a -> Either CipherError b) -> Either CipherError b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= HOWrappedCCT AES192 -> Either CipherError a
HOCipher a
f
withSymmetricCipher SymmetricAlgorithm
AES256 ByteString
keyBytes HOCipher a
f =
    ( ByteString -> Either CipherError (HOWrappedCCT AES256)
forall key.
ByteArray key =>
key -> Either CipherError (HOWrappedCCT AES256)
forall cipher key.
(HOBlockCipher cipher, ByteArray key) =>
key -> Either CipherError cipher
cipherInit ByteString
keyBytes
        :: Either CipherError (HOWrappedCCT AES.AES256)
    )
        Either CipherError (HOWrappedCCT AES256)
-> (HOWrappedCCT AES256 -> Either CipherError a)
-> Either CipherError a
forall a b.
Either CipherError a
-> (a -> Either CipherError b) -> Either CipherError b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= HOWrappedCCT AES256 -> Either CipherError a
HOCipher a
f
withSymmetricCipher SymmetricAlgorithm
Camellia128 ByteString
keyBytes HOCipher a
f =
    ( ByteString -> Either CipherError (HOWrappedCCT Camellia128)
forall key.
ByteArray key =>
key -> Either CipherError (HOWrappedCCT Camellia128)
forall cipher key.
(HOBlockCipher cipher, ByteArray key) =>
key -> Either CipherError cipher
cipherInit ByteString
keyBytes
        :: Either CipherError (HOWrappedCCT Camellia.Camellia128)
    )
        Either CipherError (HOWrappedCCT Camellia128)
-> (HOWrappedCCT Camellia128 -> Either CipherError a)
-> Either CipherError a
forall a b.
Either CipherError a
-> (a -> Either CipherError b) -> Either CipherError b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= HOWrappedCCT Camellia128 -> Either CipherError a
HOCipher a
f
withSymmetricCipher SymmetricAlgorithm
Camellia192 ByteString
keyBytes HOCipher a
f =
    ( ByteString -> Either CipherError (HOWrappedOldCCT Camellia192)
forall key.
ByteArray key =>
key -> Either CipherError (HOWrappedOldCCT Camellia192)
forall cipher key.
(HOBlockCipher cipher, ByteArray key) =>
key -> Either CipherError cipher
cipherInit ByteString
keyBytes
        :: Either CipherError (HOWrappedOldCCT CNC.Camellia192)
    )
        Either CipherError (HOWrappedOldCCT Camellia192)
-> (HOWrappedOldCCT Camellia192 -> Either CipherError a)
-> Either CipherError a
forall a b.
Either CipherError a
-> (a -> Either CipherError b) -> Either CipherError b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= HOWrappedOldCCT Camellia192 -> Either CipherError a
HOCipher a
f
withSymmetricCipher SymmetricAlgorithm
Camellia256 ByteString
keyBytes HOCipher a
f =
    ( ByteString -> Either CipherError (HOWrappedOldCCT Camellia256)
forall key.
ByteArray key =>
key -> Either CipherError (HOWrappedOldCCT Camellia256)
forall cipher key.
(HOBlockCipher cipher, ByteArray key) =>
key -> Either CipherError cipher
cipherInit ByteString
keyBytes
        :: Either CipherError (HOWrappedOldCCT CNC.Camellia256)
    )
        Either CipherError (HOWrappedOldCCT Camellia256)
-> (HOWrappedOldCCT Camellia256 -> Either CipherError a)
-> Either CipherError a
forall a b.
Either CipherError a
-> (a -> Either CipherError b) -> Either CipherError b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= HOWrappedOldCCT Camellia256 -> Either CipherError a
HOCipher a
f

withAEADCipher
    :: SymmetricAlgorithm
    -> B.ByteString
    -> HOCipher a
    -> Either CipherError a
withAEADCipher :: forall a.
SymmetricAlgorithm
-> ByteString -> HOCipher a -> Either CipherError a
withAEADCipher SymmetricAlgorithm
symalgo ByteString
keyBytes HOCipher a
f =
    case SymmetricAlgorithm
symalgo of
        SymmetricAlgorithm
AES128 -> SymmetricAlgorithm
-> ByteString -> HOCipher a -> Either CipherError a
forall a.
SymmetricAlgorithm
-> ByteString -> HOCipher a -> Either CipherError a
withSymmetricCipher SymmetricAlgorithm
AES128 ByteString
keyBytes cipher -> Either CipherError a
HOCipher a
f
        SymmetricAlgorithm
AES192 -> SymmetricAlgorithm
-> ByteString -> HOCipher a -> Either CipherError a
forall a.
SymmetricAlgorithm
-> ByteString -> HOCipher a -> Either CipherError a
withSymmetricCipher SymmetricAlgorithm
AES192 ByteString
keyBytes cipher -> Either CipherError a
HOCipher a
f
        SymmetricAlgorithm
AES256 -> SymmetricAlgorithm
-> ByteString -> HOCipher a -> Either CipherError a
forall a.
SymmetricAlgorithm
-> ByteString -> HOCipher a -> Either CipherError a
withSymmetricCipher SymmetricAlgorithm
AES256 ByteString
keyBytes cipher -> Either CipherError a
HOCipher a
f
        SymmetricAlgorithm
Camellia128 -> SymmetricAlgorithm
-> ByteString -> HOCipher a -> Either CipherError a
forall a.
SymmetricAlgorithm
-> ByteString -> HOCipher a -> Either CipherError a
withSymmetricCipher SymmetricAlgorithm
Camellia128 ByteString
keyBytes cipher -> Either CipherError a
HOCipher a
f
        SymmetricAlgorithm
Twofish -> SymmetricAlgorithm
-> ByteString -> HOCipher a -> Either CipherError a
forall a.
SymmetricAlgorithm
-> ByteString -> HOCipher a -> Either CipherError a
withSymmetricCipher SymmetricAlgorithm
Twofish ByteString
keyBytes cipher -> Either CipherError a
HOCipher a
f
        SymmetricAlgorithm
Camellia192 -> SymmetricAlgorithm
-> ByteString -> HOCipher a -> Either CipherError a
forall a.
SymmetricAlgorithm
-> ByteString -> HOCipher a -> Either CipherError a
withSymmetricCipher SymmetricAlgorithm
Camellia192 ByteString
keyBytes cipher -> Either CipherError a
HOCipher a
f
        SymmetricAlgorithm
Camellia256 -> SymmetricAlgorithm
-> ByteString -> HOCipher a -> Either CipherError a
forall a.
SymmetricAlgorithm
-> ByteString -> HOCipher a -> Either CipherError a
withSymmetricCipher SymmetricAlgorithm
Camellia256 ByteString
keyBytes cipher -> Either CipherError a
HOCipher a
f
        SymmetricAlgorithm
_ -> CipherError -> Either CipherError a
forall a b. a -> Either a b
Left (SymmetricAlgorithm -> CipherError
CipherUnsupportedAlgorithm SymmetricAlgorithm
symalgo)

{- | 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 :: [SymmetricAlgorithm]
supportedSymmetricAlgorithmsForCFB :: [SymmetricAlgorithm]
supportedSymmetricAlgorithmsForCFB =
    [ SymmetricAlgorithm
AES256
    , SymmetricAlgorithm
AES192
    , SymmetricAlgorithm
AES128
    , SymmetricAlgorithm
Camellia256
    , SymmetricAlgorithm
Camellia192
    , SymmetricAlgorithm
Camellia128
    , SymmetricAlgorithm
Twofish
    , SymmetricAlgorithm
Blowfish
    ]

-- 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
CipherUnsupportedAlgorithm SymmetricAlgorithm
ReservedSAFER)
keySize SymmetricAlgorithm
ReservedDES = CipherError -> Either CipherError Int
forall a b. a -> Either a b
Left (SymmetricAlgorithm -> CipherError
CipherUnsupportedAlgorithm 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
CipherUnsupportedAlgorithm (Word8 -> SymmetricAlgorithm
OtherSA Word8
n))