packages feed

hOpenPGP-3.7: Codec/Encryption/OpenPGP/BlockCipher.hs

-- 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 Plaintext _ _ = Left (CipherUnsupportedAlgorithm Plaintext)
withSymmetricCipher IDEA _ _ = Left (CipherUnsupportedAlgorithm IDEA)
withSymmetricCipher ReservedSAFER _ _ = Left (CipherUnsupportedAlgorithm ReservedSAFER)
withSymmetricCipher ReservedDES _ _ = Left (CipherUnsupportedAlgorithm ReservedDES)
withSymmetricCipher (OtherSA n) _ _ = Left (CipherUnsupportedAlgorithm (OtherSA n))
withSymmetricCipher CAST5 keyBytes f =
    ( cipherInit keyBytes
        :: Either CipherError (HOWrappedOldCCT CNC.CAST128)
    )
        >>= f
withSymmetricCipher Twofish keyBytes f =
    ( cipherInit keyBytes
        :: Either CipherError (HOWrappedOldCCT CNC.TWOFISH)
    )
        >>= f
withSymmetricCipher TripleDES keyBytes f =
    ( cipherInit keyBytes
        :: Either CipherError (HOWrappedCCT TripleDES.DES_EDE3)
    )
        >>= f
withSymmetricCipher Blowfish keyBytes f =
    ( cipherInit keyBytes
        :: Either CipherError (HOWrappedCCT Blowfish.Blowfish128)
    )
        >>= f
withSymmetricCipher AES128 keyBytes f =
    ( cipherInit keyBytes
        :: Either CipherError (HOWrappedCCT AES.AES128)
    )
        >>= f
withSymmetricCipher AES192 keyBytes f =
    ( cipherInit keyBytes
        :: Either CipherError (HOWrappedCCT AES.AES192)
    )
        >>= f
withSymmetricCipher AES256 keyBytes f =
    ( cipherInit keyBytes
        :: Either CipherError (HOWrappedCCT AES.AES256)
    )
        >>= f
withSymmetricCipher Camellia128 keyBytes f =
    ( cipherInit keyBytes
        :: Either CipherError (HOWrappedCCT Camellia.Camellia128)
    )
        >>= f
withSymmetricCipher Camellia192 keyBytes f =
    ( cipherInit keyBytes
        :: Either CipherError (HOWrappedOldCCT CNC.Camellia192)
    )
        >>= f
withSymmetricCipher Camellia256 keyBytes f =
    ( cipherInit keyBytes
        :: Either CipherError (HOWrappedOldCCT CNC.Camellia256)
    )
        >>= f

withAEADCipher
    :: SymmetricAlgorithm
    -> B.ByteString
    -> HOCipher a
    -> Either CipherError a
withAEADCipher symalgo keyBytes f =
    case symalgo of
        AES128 -> withSymmetricCipher AES128 keyBytes f
        AES192 -> withSymmetricCipher AES192 keyBytes f
        AES256 -> withSymmetricCipher AES256 keyBytes f
        Camellia128 -> withSymmetricCipher Camellia128 keyBytes f
        Twofish -> withSymmetricCipher Twofish keyBytes f
        Camellia192 -> withSymmetricCipher Camellia192 keyBytes f
        Camellia256 -> withSymmetricCipher Camellia256 keyBytes f
        _ -> Left (CipherUnsupportedAlgorithm 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 =
    [ AES256
    , AES192
    , AES128
    , Camellia256
    , Camellia192
    , Camellia128
    , Twofish
    , 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 Plaintext = Right 0
keySize IDEA = Right 16
keySize TripleDES = Right 24
keySize CAST5 = Right 16
keySize Blowfish = Right 16
keySize ReservedSAFER = Left (CipherUnsupportedAlgorithm ReservedSAFER)
keySize ReservedDES = Left (CipherUnsupportedAlgorithm ReservedDES)
keySize AES128 = Right 16
keySize AES192 = Right 24
keySize AES256 = Right 32
keySize Twofish = Right 32
keySize Camellia128 = Right 16
keySize Camellia192 = Right 24
keySize Camellia256 = Right 32
keySize (OtherSA n) = Left (CipherUnsupportedAlgorithm (OtherSA n))