packages feed

quic-0.3.12: Network/QUIC/Crypto/Utils.hs

{-# LANGUAGE OverloadedStrings #-}

module Network.QUIC.Crypto.Utils (
    tagLength,
    sampleLength,
    integrityLimit,
    confidentialityLimit,
    bsXOR,
    calculateIntegrityTag,
) where

import qualified Data.ByteArray as Byte (xor)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Short as Short
import Network.TLS hiding (Version)
import Network.TLS.Extra.Cipher

import Network.QUIC.Crypto.Nite
import Network.QUIC.Crypto.Types
import Network.QUIC.Imports
import Network.QUIC.Types

----------------------------------------------------------------

bsXOR :: ByteString -> ByteString -> ByteString
bsXOR = Byte.xor

----------------------------------------------------------------

tagLength :: Cipher -> Int
tagLength cipher
    | supportedCipher cipher = 16
    | otherwise = unsupportedCipher "tagLength" cipher

sampleLength :: Cipher -> Int
sampleLength cipher
    | supportedCipher cipher = 16
    | otherwise = unsupportedCipher "sampleLength" cipher

----------------------------------------------------------------

calculateIntegrityTag :: Version -> CID -> ByteString -> ByteString
calculateIntegrityTag ver oCID pseudo0 =
    case aes128gcmEncrypt (key ver) (nonce ver) "" (AssDat pseudo) of
        Nothing -> ""
        Just (hdr, bdy) -> hdr `BS.append` bdy
  where
    (ocid, ocidlen) = unpackCID oCID
    pseudo =
        BS.concat
            [ BS.singleton ocidlen
            , Short.fromShort ocid
            , pseudo0
            ]
    key Draft29 = Key "\xcc\xce\x18\x7e\xd0\x9a\x09\xd0\x57\x28\x15\x5a\x6c\xb9\x6b\xe1"
    key Version1 = Key "\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e"
    key Version2 = Key "\x8f\xb4\xb0\x1b\x56\xac\x48\xe2\x60\xfb\xcb\xce\xad\x7c\xcc\x92"
    key _ = Key "not supported"
    nonce Draft29 = Nonce "\xe5\x49\x30\xf9\x7f\x21\x36\xf0\x53\x0a\x8c\x1c"
    nonce Version1 = Nonce "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb"
    nonce Version2 = Nonce "\xd8\x69\x69\xbc\x2d\x7c\x6d\x99\x90\xef\xb0\x4a"
    nonce _ = Nonce "not supported"

----------------------------------------------------------------

-- | How many packets may fail authentication on a connection before the AEAD
--   is no longer trusted to tell a forgery from the real thing.
--
-- RFC 9001 Sec 6.6: "endpoints MUST count the number of received packets that
-- fail authentication during the lifetime of a connection.  If the total
-- number of received packets that fail authentication within the connection,
-- across all keys, exceeds the integrity limit for the selected AEAD, the
-- endpoint MUST immediately close the connection with a connection error of
-- type AEAD_LIMIT_REACHED and not process any more packets."
--
-- The same section gives the numbers: 2^52 for the AES-GCM ciphers and 2^36
-- for ChaCha20-Poly1305.  An AEAD we do not know gets the smaller of the two,
-- which is the safe way to be wrong.
integrityLimit :: Cipher -> Int
integrityLimit cipher
    | cipher == cipher13_AES_128_GCM_SHA256 = 2 ^ (52 :: Int)
    | cipher == cipher13_AES_256_GCM_SHA384 = 2 ^ (52 :: Int)
    | otherwise = 2 ^ (36 :: Int)

-- | How many packets one key may protect before the AEAD is no longer
--   trusted to keep what it has protected secret.
--
-- RFC 9001 Sec 6.6: "Endpoints MUST count the number of encrypted packets for
-- each set of keys.  If the total number of encrypted packets with the same
-- key exceeds the confidentiality limit for the selected AEAD, the endpoint
-- MUST stop using those keys."
--
-- The same section gives 2^23 for the AES-GCM ciphers, and for
-- ChaCha20-Poly1305 a number "greater than the number of possible packets
-- (2^62) and so can be disregarded".  An AEAD we do not know gets the
-- smaller, which is the safe way to be wrong.
confidentialityLimit :: Cipher -> Int
confidentialityLimit cipher
    | cipher == cipher13_CHACHA20_POLY1305_SHA256 = 2 ^ (62 :: Int)
    | otherwise = 2 ^ (23 :: Int)