content: {-# LANGUAGE BinaryLiterals #-}
  {-# LANGUAGE OverloadedStrings #-}
  
  module Network.QUIC.Crypto.Keys (
      defaultCipher,
      initialSecrets,
      clientInitialSecret,
      serverInitialSecret,
      aeadKey,
      initialVector,
      nextSecret,
      headerProtectionKey,
  ) where
  
  import qualified Control.Exception as E
  import Network.TLS hiding (Version)
  import Network.TLS.Extra.Cipher
  import Network.TLS.QUIC
  
  import Network.QUIC.Crypto.Types
  import Network.QUIC.Imports
  import Network.QUIC.Types
  
  ----------------------------------------------------------------
  
  defaultCipher :: Cipher
  defaultCipher = cipher13_AES_128_GCM_SHA256
  
  ----------------------------------------------------------------
  
  initialSalt :: Version -> Salt
  initialSalt Draft29 =
      "\xaf\xbf\xec\x28\x99\x93\xd2\x4c\x9e\x97\x86\xf1\x9c\x61\x11\xe0\x43\x90\xa8\x99"
  initialSalt Version1 =
      "\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb\x7f\x0a"
  initialSalt Version2 =
      "\x0d\xed\xe3\xde\xf7\x00\xa6\xdb\x81\x93\x81\xbe\x6e\x26\x9d\xcb\xf9\xbd\x2e\xd9"
  initialSalt (Version v) = E.throw $ VersionIsUnknown v
  
  initialSecrets :: Version -> CID -> TrafficSecrets InitialSecret
  initialSecrets v c = (clientInitialSecret v c, serverInitialSecret v c)
  
  clientInitialSecret :: Version -> CID -> ClientTrafficSecret InitialSecret
  clientInitialSecret v c = ClientTrafficSecret $ initialSecret v c $ Label "client in"
  
  serverInitialSecret :: Version -> CID -> ServerTrafficSecret InitialSecret
  serverInitialSecret v c = ServerTrafficSecret $ initialSecret v c $ Label "server in"
  
  initialSecret :: Version -> CID -> Label -> ByteString
  initialSecret Draft29 = initialSecret' $ initialSalt Draft29
  initialSecret Version1 = initialSecret' $ initialSalt Version1
  initialSecret Version2 = initialSecret' $ initialSalt Version2
  initialSecret _ = \_ _ -> "not supported"
  
  initialSecret' :: ByteString -> CID -> Label -> ByteString
  initialSecret' salt cid (Label label) = secret
    where
      cipher = defaultCipher
      hash = cipherHash cipher
      iniSecret = hkdfExtract hash salt $ fromCID cid
      hashSize = hashDigestSize hash
      secret = hkdfExpandLabel hash iniSecret label "" hashSize
  
  aeadKey :: Version -> Cipher -> Secret -> Key
  aeadKey Draft29 = genKey $ Label "quic key"
  aeadKey Version1 = genKey $ Label "quic key"
  aeadKey Version2 = genKey $ Label "quicv2 key"
  aeadKey _ = genKey $ Label "not supported"
  
  headerProtectionKey :: Version -> Cipher -> Secret -> Key
  headerProtectionKey Draft29 = genKey $ Label "quic hp"
  headerProtectionKey Version1 = genKey $ Label "quic hp"
  headerProtectionKey Version2 = genKey $ Label "quicv2 hp"
  headerProtectionKey _ = genKey $ Label "not supported"
  
  genKey :: Label -> Cipher -> Secret -> Key
  genKey (Label label) cipher (Secret secret) = Key key
    where
      hash = cipherHash cipher
      bulk = cipherBulk cipher
      keySize = bulkKeySize bulk
      key = hkdfExpandLabel hash secret label "" keySize
  
  initialVector :: Version -> Cipher -> Secret -> IV
  initialVector ver cipher (Secret secret) = IV iv
    where
      label = ivLabel ver
      hash = cipherHash cipher
      bulk = cipherBulk cipher
      ivSize = max 8 (bulkIVSize bulk + bulkExplicitIV bulk)
      iv = hkdfExpandLabel hash secret label "" ivSize
  
  ivLabel :: Version -> ByteString
  ivLabel Draft29 = "quic iv"
  ivLabel Version1 = "quic iv"
  ivLabel Version2 = "quicv2 iv"
  ivLabel _ = "not supported"
  
  nextSecret :: Version -> Cipher -> Secret -> Secret
  nextSecret ver cipher (Secret secN) = Secret secN1
    where
      label = kuLabel ver
      hash = cipherHash cipher
      hashSize = hashDigestSize hash
      secN1 = hkdfExpandLabel hash secN label "" hashSize
  
  kuLabel :: Version -> ByteString
  kuLabel Draft29 = "quic ku"
  kuLabel Version1 = "quic ku"
  kuLabel Version2 = "quicv2 ku"
  kuLabel _ = "not supported"
  