[ChaChaPoly1305] add implementation and simple KAT test
This commit is contained in:
parent
58c6efe4c4
commit
ce849fb0d2
112
Crypto/Cipher/ChaChaPoly1305.hs
Normal file
112
Crypto/Cipher/ChaChaPoly1305.hs
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
-- |
|
||||||
|
-- Module : Crypto.Cipher.ChaChaPoly1305
|
||||||
|
-- License : BSD-style
|
||||||
|
-- Maintainer : Vincent Hanquez <vincent@snarc.org>
|
||||||
|
-- Stability : stable
|
||||||
|
-- Portability : good
|
||||||
|
--
|
||||||
|
-- A simple AEAD scheme using ChaCha20 and Poly1305.
|
||||||
|
--
|
||||||
|
-- See RFC7539.
|
||||||
|
--
|
||||||
|
module Crypto.Cipher.ChaChaPoly1305
|
||||||
|
( State
|
||||||
|
, Nonce
|
||||||
|
, nonce12
|
||||||
|
, nonce8
|
||||||
|
, initialize
|
||||||
|
, appendAAD
|
||||||
|
, finalizeAAD
|
||||||
|
, combine
|
||||||
|
, finalize
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Crypto.Internal.ByteArray (ByteArrayAccess, ByteArray, Bytes, ScrubbedBytes)
|
||||||
|
import qualified Crypto.Internal.ByteArray as B
|
||||||
|
import Crypto.Internal.Imports
|
||||||
|
import Crypto.Error
|
||||||
|
import qualified Crypto.Cipher.ChaCha as ChaCha
|
||||||
|
import qualified Crypto.MAC.Poly1305 as Poly1305
|
||||||
|
import Data.Memory.Endian
|
||||||
|
import qualified Data.ByteArray.Pack as P
|
||||||
|
|
||||||
|
data State = State !ChaCha.State
|
||||||
|
!Poly1305.State
|
||||||
|
!Word64 -- AAD length
|
||||||
|
!Word64 -- ciphertext length
|
||||||
|
|
||||||
|
newtype Nonce = Nonce Bytes
|
||||||
|
|
||||||
|
-- Based on the following pseudo code:
|
||||||
|
--
|
||||||
|
-- chacha20_aead_encrypt(aad, key, iv, constant, plaintext):
|
||||||
|
-- nonce = constant | iv
|
||||||
|
-- otk = poly1305_key_gen(key, nonce)
|
||||||
|
-- ciphertext = chacha20_encrypt(key, 1, nonce, plaintext)
|
||||||
|
-- mac_data = aad | pad16(aad)
|
||||||
|
-- mac_data |= ciphertext | pad16(ciphertext)
|
||||||
|
-- mac_data |= num_to_4_le_bytes(aad.length)
|
||||||
|
-- mac_data |= num_to_4_le_bytes(ciphertext.length)
|
||||||
|
-- tag = poly1305_mac(mac_data, otk)
|
||||||
|
-- return (ciphertext, tag)
|
||||||
|
|
||||||
|
pad16 :: Word64 -> Bytes
|
||||||
|
pad16 n
|
||||||
|
| modLen == 0 = B.empty
|
||||||
|
| otherwise = B.replicate (16 - modLen) 0
|
||||||
|
where
|
||||||
|
modLen = fromIntegral (n `mod` 16)
|
||||||
|
|
||||||
|
-- | Nonce smart constructor 12 bytes IV, nonce constructor
|
||||||
|
nonce12 :: ByteArrayAccess iv => iv -> CryptoFailable Nonce
|
||||||
|
nonce12 iv
|
||||||
|
| B.length iv /= 12 = CryptoFailed $ CryptoError_IvSizeInvalid
|
||||||
|
| otherwise = CryptoPassed $ Nonce (B.convert iv)
|
||||||
|
|
||||||
|
-- | 8 bytes IV, nonce constructor
|
||||||
|
nonce8 :: ByteArrayAccess ba
|
||||||
|
=> ba -- ^ 4 bytes constant
|
||||||
|
-> ba -- ^ 8 bytes IV
|
||||||
|
-> CryptoFailable Nonce
|
||||||
|
nonce8 constant iv
|
||||||
|
| B.length constant /= 4 = CryptoFailed $ CryptoError_IvSizeInvalid
|
||||||
|
| B.length iv /= 8 = CryptoFailed $ CryptoError_IvSizeInvalid
|
||||||
|
| otherwise = CryptoPassed $ Nonce $ B.concat [constant, iv]
|
||||||
|
|
||||||
|
initialize :: ByteArrayAccess key
|
||||||
|
=> key -> Nonce -> CryptoFailable State
|
||||||
|
initialize key (Nonce nonce)
|
||||||
|
| B.length key /= 32 = CryptoFailed $ CryptoError_KeySizeInvalid
|
||||||
|
| otherwise = CryptoPassed $ State encState polyState 0 0
|
||||||
|
where
|
||||||
|
rootState = ChaCha.initialize 20 key nonce
|
||||||
|
(polyKey, encState) = ChaCha.generate rootState 64
|
||||||
|
polyState = Poly1305.initialize (B.take 32 polyKey :: ScrubbedBytes)
|
||||||
|
|
||||||
|
appendAAD :: ByteArrayAccess ba => ba -> State -> State
|
||||||
|
appendAAD ba (State encState macState aadLength plainLength) =
|
||||||
|
State encState newMacState newLength plainLength
|
||||||
|
where
|
||||||
|
newMacState = Poly1305.update macState ba
|
||||||
|
newLength = aadLength + fromIntegral (B.length ba)
|
||||||
|
|
||||||
|
finalizeAAD :: State -> State
|
||||||
|
finalizeAAD (State encState macState aadLength plainLength) =
|
||||||
|
State encState newMacState aadLength plainLength
|
||||||
|
where
|
||||||
|
newMacState = Poly1305.update macState $ pad16 aadLength
|
||||||
|
|
||||||
|
combine :: ByteArray ba => ba -> State -> (ba, State)
|
||||||
|
combine input (State encState macState aadLength plainLength) =
|
||||||
|
(output, State newEncState newMacState aadLength newPlainLength)
|
||||||
|
where
|
||||||
|
(output, newEncState) = ChaCha.combine encState input
|
||||||
|
newMacState = Poly1305.update macState output
|
||||||
|
newPlainLength = plainLength + fromIntegral (B.length input)
|
||||||
|
|
||||||
|
finalize :: State -> Poly1305.Auth
|
||||||
|
finalize (State _ macState aadLength plainLength) =
|
||||||
|
Poly1305.finalize $ Poly1305.updates macState
|
||||||
|
[ pad16 plainLength
|
||||||
|
, either (error "finalize: internal error") id $ P.fill 16 (P.putStorable (LE aadLength) >> P.putStorable (LE plainLength))
|
||||||
|
]
|
||||||
@ -76,6 +76,7 @@ Library
|
|||||||
Crypto.Cipher.Blowfish
|
Crypto.Cipher.Blowfish
|
||||||
Crypto.Cipher.Camellia
|
Crypto.Cipher.Camellia
|
||||||
Crypto.Cipher.ChaCha
|
Crypto.Cipher.ChaCha
|
||||||
|
Crypto.Cipher.ChaChaPoly1305
|
||||||
Crypto.Cipher.DES
|
Crypto.Cipher.DES
|
||||||
Crypto.Cipher.RC4
|
Crypto.Cipher.RC4
|
||||||
Crypto.Cipher.Salsa
|
Crypto.Cipher.Salsa
|
||||||
|
|||||||
31
tests/ChaChaPoly1305.hs
Normal file
31
tests/ChaChaPoly1305.hs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
module ChaChaPoly1305 where
|
||||||
|
|
||||||
|
import qualified Crypto.Cipher.ChaChaPoly1305 as AEAD
|
||||||
|
import Imports
|
||||||
|
import Crypto.Error
|
||||||
|
import Poly1305 ()
|
||||||
|
|
||||||
|
import qualified Data.ByteString as B
|
||||||
|
import qualified Data.ByteArray as B (convert)
|
||||||
|
|
||||||
|
plaintext, aad, key, iv, ciphertext, tag :: B.ByteString
|
||||||
|
plaintext = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."
|
||||||
|
aad = "\x50\x51\x52\x53\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7"
|
||||||
|
key = "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
|
||||||
|
iv = "\x40\x41\x42\x43\x44\x45\x46\x47"
|
||||||
|
constant = "\x07\x00\x00\x00"
|
||||||
|
ciphertext = "\xd3\x1a\x8d\x34\x64\x8e\x60\xdb\x7b\x86\xaf\xbc\x53\xef\x7e\xc2\xa4\xad\xed\x51\x29\x6e\x08\xfe\xa9\xe2\xb5\xa7\x36\xee\x62\xd6\x3d\xbe\xa4\x5e\x8c\xa9\x67\x12\x82\xfa\xfb\x69\xda\x92\x72\x8b\x1a\x71\xde\x0a\x9e\x06\x0b\x29\x05\xd6\xa5\xb6\x7e\xcd\x3b\x36\x92\xdd\xbd\x7f\x2d\x77\x8b\x8c\x98\x03\xae\xe3\x28\x09\x1b\x58\xfa\xb3\x24\xe4\xfa\xd6\x75\x94\x55\x85\x80\x8b\x48\x31\xd7\xbc\x3f\xf4\xde\xf0\x8e\x4b\x7a\x9d\xe5\x76\xd2\x65\x86\xce\xc6\x4b\x61\x16"
|
||||||
|
tag = "\x1a\xe1\x0b\x59\x4f\x09\xe2\x6a\x7e\x90\x2e\xcb\xd0\x60\x06\x91"
|
||||||
|
|
||||||
|
tests = testGroup "ChaChaPoly1305"
|
||||||
|
[ testCase "V1" runEncrypt
|
||||||
|
]
|
||||||
|
where runEncrypt =
|
||||||
|
let ini = throwCryptoError $ AEAD.initialize key (throwCryptoError $ AEAD.nonce8 constant iv)
|
||||||
|
afterAAD = AEAD.finalizeAAD (AEAD.appendAAD aad ini)
|
||||||
|
(out, afterEncrypt) = AEAD.combine plaintext afterAAD
|
||||||
|
outtag = AEAD.finalize afterEncrypt
|
||||||
|
in propertyHoldCase [ eqTest "ciphertext" ciphertext out
|
||||||
|
, eqTest "tag" tag (B.convert outtag)
|
||||||
|
]
|
||||||
@ -8,6 +8,7 @@ import qualified Hash
|
|||||||
import qualified Poly1305
|
import qualified Poly1305
|
||||||
import qualified Salsa
|
import qualified Salsa
|
||||||
import qualified ChaCha
|
import qualified ChaCha
|
||||||
|
import qualified ChaChaPoly1305
|
||||||
import qualified KAT_HMAC
|
import qualified KAT_HMAC
|
||||||
import qualified KAT_PBKDF2
|
import qualified KAT_PBKDF2
|
||||||
import qualified KAT_Curve25519
|
import qualified KAT_Curve25519
|
||||||
@ -48,6 +49,7 @@ tests = testGroup "cryptonite"
|
|||||||
, testGroup "stream-cipher"
|
, testGroup "stream-cipher"
|
||||||
[ KAT_RC4.tests
|
[ KAT_RC4.tests
|
||||||
, ChaCha.tests
|
, ChaCha.tests
|
||||||
|
, ChaChaPoly1305.tests
|
||||||
, Salsa.tests
|
, Salsa.tests
|
||||||
]
|
]
|
||||||
, KAT_AFIS.tests
|
, KAT_AFIS.tests
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user