summaryrefslogtreecommitdiff
path: root/src/crypto/reflibs/Crypto/Cipher/ChaChaPoly1305.hs
blob: 9071d9f6881141c72cb28ecd45dd08fcbac619ae (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 :: ByteString -> ByteString -> CryptoFailable Nonce
nonce8 c iv
    | B.length c /= 4 = CryptoFailed CryptoError
    | B.length iv /= 8 = CryptoFailed CryptoError
    | otherwise = CryptoPassed $ Nonce $ B.concat [ c, iv ]

nonce12 :: ByteString -> CryptoFailable Nonce
nonce12 iv
    | B.length iv /= 12 = CryptoFailed CryptoError
    | otherwise = CryptoPassed $ Nonce 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 :: ByteString -> AeadState -> ( ByteString, AeadState )
encrypt ptext st =
    let Nonce nonce = stNonce st
        ( ctext, mac ) = P.encrypt (B.concat $ stAAD st) (stKey st) nonce ptext
     in ( ctext, st { stAuth = Just $ Auth mac } )

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

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