summaryrefslogtreecommitdiff
path: root/src/crypto/reflibs/Crypto/Cipher/ChaChaPoly1305.hs
blob: c849c0bc61a45e6c234d5b69bbbcf66822dfbdef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
module Crypto.Cipher.ChaChaPoly1305 (
    AeadState, Nonce, Auth(..),
    authTag,
    nonce8, nonce12,
    aeadInitialize,
    aeadAppendAAD, aeadFinalizeAAD,
    encrypt, decrypt,
    aeadFinalize,
) where

import Crypto.AEAD.ChaCha20Poly1305 qualified as P
import Crypto.Error
import Crypto.MAC.Poly1305

import Data.ByteArray as BA
import Data.ByteString (ByteString)
import Data.ByteString qualified as B


data AeadState = AeadState
    { stKey :: ByteString
    , stNonce :: Nonce
    , stAAD :: [ ByteString ]
    , stAuth :: Maybe Auth
    }

newtype Nonce = Nonce ByteString
    deriving (Eq, ByteArrayAccess)

nonce8 :: Bytes -> Bytes -> CryptoFailable Nonce
nonce8 c iv
    | BA.length c /= 4 = CryptoFailed CryptoError
    | BA.length iv /= 8 = CryptoFailed CryptoError
    | otherwise = CryptoPassed $ Nonce $ B.concat $ [ BA.convert c, BA.convert iv ]

nonce12 :: Bytes -> CryptoFailable Nonce
nonce12 iv
    | BA.length iv /= 12 = CryptoFailed CryptoError
    | otherwise = CryptoPassed $ Nonce $ BA.convert iv

aeadInitialize :: ScrubbedBytes -> Nonce -> CryptoFailable AeadState
aeadInitialize key nonce
    | BA.length key /= 32 = CryptoFailed CryptoError
    | otherwise = CryptoPassed AeadState
        { stKey = BA.convert key
        , stNonce = nonce
        , stAAD = []
        , stAuth = Nothing
        }

aeadAppendAAD :: ByteString -> AeadState -> AeadState
aeadAppendAAD aad st = st { stAAD = stAAD st ++ [ aad ] }

aeadFinalizeAAD :: AeadState -> AeadState
aeadFinalizeAAD = id

encrypt :: ByteArray ba => ba -> AeadState -> ( ba, AeadState )
encrypt ptext st =
    let Nonce nonce = stNonce st
        ( ctext, mac ) = P.encrypt (B.concat $ stAAD st) (stKey st) nonce (convert ptext)
     in ( convert ctext, st { stAuth = Just $ Auth mac } )

decrypt :: ByteArray ba => ( ba, Auth ) -> AeadState -> Maybe ba
decrypt ( ctext, auth ) st = do
    let Nonce nonce = stNonce st
        Auth mac = auth
    fmap convert $ P.decrypt (B.concat $ stAAD st) (stKey st) nonce ( convert ctext, mac )

aeadFinalize :: AeadState -> Auth
aeadFinalize st = case stAuth st of
    Just auth -> auth
    Nothing -> aeadFinalize $ snd $ encrypt B.empty st