packages feed

amazonka-s3-encryption-2.0: src/Amazonka/S3/Encryption/Types.hs

{-# LANGUAGE TemplateHaskell #-}

-- |
-- Module      : Amazonka.S3.Encryption.Types
-- Copyright   : (c) 2013-2023 Brendan Hay
-- License     : Mozilla Public License, v. 2.0.
-- Maintainer  : Brendan Hay <brendan.g.hay+amazonka.com>
-- Stability   : provisional
-- Portability : non-portable (GHC extensions)
module Amazonka.S3.Encryption.Types where

import Amazonka.Data
import Amazonka.Prelude
import qualified Amazonka.S3 as S3
import qualified Control.Exception.Lens as Exception.Lens
import qualified Control.Lens as Lens
import qualified Crypto.Cipher.AES as AES
import qualified Crypto.Error
import qualified Crypto.PubKey.RSA.Types as RSA
import qualified Data.Aeson as Aeson
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text

-- | An error thrown when performing encryption or decryption.
data EncryptionError
  = -- | Error initialising an AES cipher from a secret key.
    CipherFailure Crypto.Error.CryptoError
  | -- | Failure performing asymmetric encryption/decryption.
    PubKeyFailure RSA.Error
  | -- | Failure creating an IV from some bytes.
    IVInvalid ByteString
  | -- | Required envelope field missing.
    EnvelopeMissing (CI Text)
  | -- | Error parsing envelope.
    EnvelopeInvalid (CI Text) String
  | -- | KMS error when retrieving decrypted plaintext.
    PlaintextUnavailable
  deriving stock (Eq, Show)

instance Exception EncryptionError

$(Lens.makeClassyPrisms ''EncryptionError)

instance AsEncryptionError SomeException where
  _EncryptionError = Exception.Lens.exception

data ContentAlgorithm
  = -- | AES/CBC/PKCS5Padding
    AES_CBC_PKCS5Padding

instance FromText ContentAlgorithm where
  fromText = \case
    "AES/CBC/PKCS5Padding" -> pure AES_CBC_PKCS5Padding
    other -> Left ("Unrecognised content encryption algorithm: " ++ show other)

instance ToByteString ContentAlgorithm where
  toBS AES_CBC_PKCS5Padding = "AES/CBC/PKCS5Padding"

data WrappingAlgorithm
  = -- | Key Management Service.
    KMSWrap

instance FromText WrappingAlgorithm where
  fromText = \case
    "kms" -> pure KMSWrap
    other -> Left ("Unrecognised key wrapping algorithm: " ++ show other)

instance ToByteString WrappingAlgorithm where
  toBS KMSWrap = "kms"

data Location = Metadata | Discard
  deriving stock (Eq)

-- | An instructions file extension.
newtype Ext = Ext Text
  deriving stock (Eq, Show)
  deriving newtype (IsString)

-- | Defaults to @.instruction@
defaultExtension :: Ext
defaultExtension = ".instruction"

appendExtension :: Ext -> S3.ObjectKey -> S3.ObjectKey
appendExtension (Ext s) o@(S3.ObjectKey k)
  | s `Text.isSuffixOf` k = o
  | otherwise = S3.ObjectKey (k <> s)

-- | A key material description. This is attached in plaintext to the metadata,
-- and will be logged using CloudTrail. For KMS decryption any supplemental
-- material description is merged with the description stored on the object during
-- decryption.
newtype Description = Description {fromDescription :: HashMap Text Text}
  deriving stock (Eq, Show)
  deriving newtype (Semigroup, Monoid, FromJSON, ToJSON)

instance ToByteString Description where
  toBS = toBS . Aeson.encode

instance FromText Description where
  fromText = Aeson.eitherDecodeStrict' . Text.encodeUtf8

-- | The key used for encryption and decryption.
data Key
  = Symmetric AES.AES256 Description
  | Asymmetric RSA.KeyPair Description
  | KMS Text Description

-- | Modify the material description of a key.
--
-- /See:/ 'Description'.
description :: Lens' Key Description
description = Lens.lens f (flip g)
  where
    f = \case
      Symmetric _ a -> a
      Asymmetric _ a -> a
      KMS _ a -> a

    g a = \case
      Symmetric c _ -> Symmetric c a
      Asymmetric k _ -> Asymmetric k a
      KMS k _ -> KMS k a