diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,10 @@
 # ChangeLog
 
+## 0.3.1
+
+* Using tls v2.4.0.
+* rateLimit is now 32.
+
 ## 0.3.0
 
 * Using "ram" instead of "memory".
diff --git a/Network/QUIC/Config.hs b/Network/QUIC/Config.hs
--- a/Network/QUIC/Config.hs
+++ b/Network/QUIC/Config.hs
@@ -174,7 +174,9 @@
     , scCiphers :: [Cipher]
     -- ^ Cipher candidates defined in TLS 1.3.
     , scGroups :: [Group]
-    -- ^ Key exchange group candidates defined in TLS 1.3.
+    -- ^ Key exchange group candidates defined in TLS 1.3 (tls <2.4).
+    , scGroupsTLS13 :: [[Group]]
+    -- ^ Key exchange group candidates defined in TLS 1.3 (tls >= 2.4).
     , scParameters :: Parameters
     , scKeyLog :: String -> IO ()
     , scQLog :: Maybe FilePath
@@ -205,6 +207,7 @@
         { scVersions = [Version2, Version1]
         , scCiphers = defaultCiphers
         , scGroups = supportedGroups defaultSupported
+        , scGroupsTLS13 = supportedGroupsTLS13 defaultSupported
         , scParameters = defaultParameters
 #if MIN_VERSION_tls(2,1,10)
         , scKeyLog = defaultKeyLogger
diff --git a/Network/QUIC/Crypto/Fusion.hs b/Network/QUIC/Crypto/Fusion.hs
--- a/Network/QUIC/Crypto/Fusion.hs
+++ b/Network/QUIC/Crypto/Fusion.hs
@@ -15,6 +15,7 @@
 
 #ifdef USE_FUSION
 import qualified Data.ByteString as BS
+import Data.ByteArray (convert)
 import Foreign.C.Types
 import Foreign.ForeignPtr
 import Foreign.Ptr
@@ -42,12 +43,12 @@
 
 fusionSetupAES128 :: FusionContext -> Key -> IV -> IO ()
 fusionSetupAES128 (FC fctx) (Key key) (IV iv) = withForeignPtr fctx $ \pctx ->
-    withByteString key $ \keyp ->
+    withByteString (convert key) $ \keyp ->
         withByteString iv $ \ivp -> void $ c_aes128gcm_setup pctx 0 keyp ivp
 
 fusionSetupAES256 :: FusionContext -> Key -> IV -> IO ()
 fusionSetupAES256 (FC fctx) (Key key) (IV iv) = withForeignPtr fctx $ \pctx ->
-    withByteString key $ \keyp ->
+    withByteString (convert key) $ \keyp ->
         withByteString iv $ \ivp -> void $ c_aes256gcm_setup pctx 0 keyp ivp
 
 ----------------------------------------------------------------
@@ -84,7 +85,7 @@
 newtype Supplement = SP (ForeignPtr SupplementOpaque)
 
 fusionSetupSupplement :: Cipher -> Key -> IO Supplement
-fusionSetupSupplement cipher (Key hpkey) = withByteString hpkey $ \hpkeyp ->
+fusionSetupSupplement cipher (Key hpkey) = withByteString (convert hpkey) $ \hpkeyp ->
   SP <$> (c_supplement_new hpkeyp keylen >>= newForeignPtr p_supplement_free)
  where
   keylen
diff --git a/Network/QUIC/Crypto/Keys.hs b/Network/QUIC/Crypto/Keys.hs
--- a/Network/QUIC/Crypto/Keys.hs
+++ b/Network/QUIC/Crypto/Keys.hs
@@ -13,6 +13,7 @@
 ) where
 
 import qualified Control.Exception as E
+import Data.ByteArray (ScrubbedBytes, convert)
 import Network.TLS hiding (Version)
 import Network.TLS.Extra.Cipher
 import Network.TLS.QUIC
@@ -46,18 +47,18 @@
 serverInitialSecret :: Version -> CID -> ServerTrafficSecret InitialSecret
 serverInitialSecret v c = ServerTrafficSecret $ initialSecret v c $ Label "server in"
 
-initialSecret :: Version -> CID -> Label -> ByteString
+initialSecret :: Version -> CID -> Label -> ScrubbedBytes
 initialSecret Draft29 = initialSecret' $ initialSalt Draft29
 initialSecret Version1 = initialSecret' $ initialSalt Version1
 initialSecret Version2 = initialSecret' $ initialSalt Version2
 initialSecret _ = \_ _ -> "not supported"
 
-initialSecret' :: ByteString -> CID -> Label -> ByteString
+initialSecret' :: ByteString -> CID -> Label -> ScrubbedBytes
 initialSecret' salt cid (Label label) = secret
   where
     cipher = defaultCipher
     hash = cipherHash cipher
-    iniSecret = hkdfExtract hash salt $ fromCID cid
+    iniSecret = hkdfExtract hash (convert salt) (convert $ fromCID cid)
     hashSize = hashDigestSize hash
     secret = hkdfExpandLabel hash iniSecret label "" hashSize
 
diff --git a/Network/QUIC/Crypto/Nite.hs b/Network/QUIC/Crypto/Nite.hs
--- a/Network/QUIC/Crypto/Nite.hs
+++ b/Network/QUIC/Crypto/Nite.hs
@@ -41,26 +41,26 @@
 -- it's impossible.
 cipherEncrypt
     :: Cipher -> Key -> Nonce -> PlainText -> AssDat -> Maybe (CipherText, CipherText)
-cipherEncrypt cipher (Key key) (Nonce nonce)
+cipherEncrypt cipher key@(Key key') (Nonce nonce)
     | cipher == cipher13_AES_128_GCM_SHA256 =
         quicAeadEncrypt (aesGCMInit key nonce :: Maybe (AEAD AES128)) 16
     | cipher == cipher13_AES_128_CCM_SHA256 = error "cipher13_AES_128_CCM_SHA256"
     | cipher == cipher13_AES_256_GCM_SHA384 =
         quicAeadEncrypt (aesGCMInit key nonce :: Maybe (AEAD AES256)) 16
     | cipher == cipher13_CHACHA20_POLY1305_SHA256 =
-        quicAeadEncrypt (maybeCryptoError $ aeadChacha20poly1305Init key nonce) 16
+        quicAeadEncrypt (maybeCryptoError $ aeadChacha20poly1305Init key' nonce) 16
     | otherwise = error "cipherEncrypt"
 
 cipherDecrypt
     :: Cipher -> Key -> Nonce -> CipherText -> AssDat -> Maybe PlainText
-cipherDecrypt cipher (Key key) (Nonce nonce)
+cipherDecrypt cipher key@(Key key') (Nonce nonce)
     | cipher == cipher13_AES_128_GCM_SHA256 =
         quicAeadDecrypt (aesGCMInit key nonce :: Maybe (AEAD AES128)) 16
     | cipher == cipher13_AES_128_CCM_SHA256 = error "cipher13_AES_128_CCM_SHA256"
     | cipher == cipher13_AES_256_GCM_SHA384 =
         quicAeadDecrypt (aesGCMInit key nonce :: Maybe (AEAD AES256)) 16
     | cipher == cipher13_CHACHA20_POLY1305_SHA256 =
-        quicAeadDecrypt (maybeCryptoError $ aeadChacha20poly1305Init key nonce) 16
+        quicAeadDecrypt (maybeCryptoError $ aeadChacha20poly1305Init key' nonce) 16
     | otherwise = error "cipherDecrypt"
 
 -- IMPORTANT: Using 'let' so that parameters can be memorized.
@@ -85,15 +85,15 @@
      in aeadSimpleDecrypt aead ad ciphertext authtag
 
 aesGCMInit
-    :: BlockCipher cipher => ByteString -> ByteString -> Maybe (AEAD cipher)
-aesGCMInit key nonce =
+    :: BlockCipher cipher => Key -> ByteString -> Maybe (AEAD cipher)
+aesGCMInit (Key key) nonce =
     case maybeCryptoError $ cipherInit key of
         Nothing -> Nothing
         Just aes -> maybeCryptoError $ aeadInit AEAD_GCM aes nonce
 
 aes128gcmEncrypt
     :: Key -> Nonce -> PlainText -> AssDat -> Maybe (CipherText, CipherText)
-aes128gcmEncrypt (Key key) (Nonce nonce) =
+aes128gcmEncrypt key (Nonce nonce) =
     quicAeadEncrypt (aesGCMInit key nonce :: Maybe (AEAD AES128)) 16
 
 ----------------------------------------------------------------
diff --git a/Network/QUIC/Crypto/Types.hs b/Network/QUIC/Crypto/Types.hs
--- a/Network/QUIC/Crypto/Types.hs
+++ b/Network/QUIC/Crypto/Types.hs
@@ -19,6 +19,7 @@
     ServerTrafficSecret (..),
 ) where
 
+import Data.ByteArray (ScrubbedBytes, convert)
 import qualified Data.ByteString.Char8 as C8
 import Network.TLS hiding (Version)
 import Network.TLS.QUIC
@@ -32,9 +33,9 @@
 type CipherText = ByteString
 type Salt = ByteString
 
-newtype Key = Key ByteString deriving (Eq)
+newtype Key = Key ScrubbedBytes deriving (Eq)
 newtype IV = IV ByteString deriving (Eq)
-newtype Secret = Secret ByteString deriving (Eq)
+newtype Secret = Secret ScrubbedBytes deriving (Eq)
 newtype AssDat = AssDat ByteString deriving (Eq)
 newtype Sample = Sample ByteString deriving (Eq)
 newtype Mask = Mask ByteString deriving (Eq)
@@ -42,11 +43,11 @@
 newtype Nonce = Nonce ByteString deriving (Eq)
 
 instance Show Key where
-    show (Key x) = "Key=" ++ C8.unpack (enc16 x)
+    show (Key x) = "Key=" ++ C8.unpack (enc16 $ convert x)
 instance Show IV where
     show (IV x) = "IV=" ++ C8.unpack (enc16 x)
 instance Show Secret where
-    show (Secret x) = "Secret=" ++ C8.unpack (enc16 x)
+    show (Secret x) = "Secret=" ++ C8.unpack (enc16 $ convert x)
 instance Show AssDat where
     show (AssDat x) = "AssDat=" ++ C8.unpack (enc16 x)
 instance Show Sample where
diff --git a/Network/QUIC/Receiver.hs b/Network/QUIC/Receiver.hs
--- a/Network/QUIC/Receiver.hs
+++ b/Network/QUIC/Receiver.hs
@@ -140,7 +140,7 @@
                 processReceivedPacket conn rpkt
 
 rateLimit :: Int
-rateLimit = 10
+rateLimit = 32
 
 checkRate :: [Frame] -> Int
 checkRate fs0 = go fs0 0
diff --git a/Network/QUIC/TLS.hs b/Network/QUIC/TLS.hs
--- a/Network/QUIC/TLS.hs
+++ b/Network/QUIC/TLS.hs
@@ -122,6 +122,7 @@
             { supportedVersions = [TLS13]
             , supportedCiphers = scCiphers
             , supportedGroups = scGroups
+            , supportedGroupsTLS13 = scGroupsTLS13
             }
     debug =
         defaultDebugParams
diff --git a/quic.cabal b/quic.cabal
--- a/quic.cabal
+++ b/quic.cabal
@@ -1,6 +1,6 @@
 cabal-version:      >=1.10
 name:               quic
-version:            0.3.0
+version:            0.3.1
 license:            BSD3
 license-file:       LICENSE
 maintainer:         kazu@iij.ad.jp
@@ -150,7 +150,7 @@
         random >=1.2 && <1.4,
         serialise,
         stm >=2.5 && <2.6,
-        tls >=2.3.0 && <2.4,
+        tls >=2.4.0 && <2.5,
         unix-time >=0.4.12 && <0.5
 
     if os(linux)
@@ -261,6 +261,7 @@
         hspec,
         network >=3.2.2,
         quic,
+        ram,
         tls,
         unix-time
 
diff --git a/test/HandshakeSpec.hs b/test/HandshakeSpec.hs
--- a/test/HandshakeSpec.hs
+++ b/test/HandshakeSpec.hs
@@ -38,7 +38,7 @@
             testHandshake cc sc waitS FullHandshake
         it "can handshake in the case of TLS hello retry" $ do
             let cc = testClientConfig
-                sc = sc0{scGroups = [P256]}
+                sc = sc0{scGroups = [P256], scGroupsTLS13 = [[P256]]}
             testHandshake cc sc waitS HelloRetryRequest
         it "can handshake in the case of QUIC retry" $ do
             let cc = testClientConfig
@@ -67,7 +67,7 @@
         it "fails with no group in common" $ do
             let cc1 = testClientConfig{ccGroups = [X25519]}
                 cc2 = testClientConfig{ccGroups = [P256]}
-                sc = sc0{scGroups = [P256]}
+                sc = sc0{scGroups = [P256], scGroupsTLS13 = [[P256]]}
                 handshakeFailure e
                     | TransportErrorIsReceived te@(TransportError _) _ <- e =
                         te == cryptoError TLS.HandshakeFailure
diff --git a/test/TLSSpec.hs b/test/TLSSpec.hs
--- a/test/TLSSpec.hs
+++ b/test/TLSSpec.hs
@@ -5,6 +5,7 @@
 module TLSSpec where
 
 import Data.Bits
+import Data.ByteArray (ScrubbedBytes, convert)
 import qualified Data.ByteString as BS
 import Data.Maybe
 import Network.TLS.Extra.Cipher
@@ -36,23 +37,23 @@
             let client_initial_secret@(ClientTrafficSecret cis) = clientInitialSecret ver dcID
             client_initial_secret
                 `shouldBe` ClientTrafficSecret
-                    (dec16 "c00cf151ca5be075ed0ebfb5c80323c42d6b7db67881289af4008f1f6c357aea")
+                    (dec16' "c00cf151ca5be075ed0ebfb5c80323c42d6b7db67881289af4008f1f6c357aea")
             let ckey = aeadKey ver defaultCipher (Secret cis)
-            ckey `shouldBe` Key (dec16 "1f369613dd76d5467730efcbe3b1a22d")
+            ckey `shouldBe` Key (dec16' "1f369613dd76d5467730efcbe3b1a22d")
             let civ = initialVector ver defaultCipher (Secret cis)
             civ `shouldBe` IV (dec16 "fa044b2f42a3fd3b46fb255c")
             let chp = headerProtectionKey ver defaultCipher (Secret cis)
-            chp `shouldBe` Key (dec16 "9f50449e04a0e810283a1e9933adedd2")
+            chp `shouldBe` Key (dec16' "9f50449e04a0e810283a1e9933adedd2")
             let server_initial_secret@(ServerTrafficSecret sis) = serverInitialSecret ver dcID
             server_initial_secret
                 `shouldBe` ServerTrafficSecret
-                    (dec16 "3c199828fd139efd216c155ad844cc81fb82fa8d7446fa7d78be803acdda951b")
+                    (dec16' "3c199828fd139efd216c155ad844cc81fb82fa8d7446fa7d78be803acdda951b")
             let skey = aeadKey ver defaultCipher (Secret sis)
-            skey `shouldBe` Key (dec16 "cf3a5331653c364c88f0f379b6067e37")
+            skey `shouldBe` Key (dec16' "cf3a5331653c364c88f0f379b6067e37")
             let siv = initialVector ver defaultCipher (Secret sis)
             siv `shouldBe` IV (dec16 "0ac1493ca1905853b0bba03e")
             let shp = headerProtectionKey ver defaultCipher (Secret sis)
-            shp `shouldBe` Key (dec16 "c206b8d9b9f0f37644430b490eeaa314")
+            shp `shouldBe` Key (dec16' "c206b8d9b9f0f37644430b490eeaa314")
 
         it "describes the examples of Client Initial (RFC 9001: A.2)" $ do
             let dcID = makeCID (dec16s "8394c8f03e515708")
@@ -159,7 +160,7 @@
                 let cipher = cipher13_CHACHA20_POLY1305_SHA256
                     secret =
                         Secret $
-                            dec16 "9ac312a7f877468ebe69422748ad00a15443f18203a07d6060f688f30f21632b"
+                            dec16' "9ac312a7f877468ebe69422748ad00a15443f18203a07d6060f688f30f21632b"
                     key = aeadKey ver cipher secret
                     iv = initialVector ver cipher secret
                     hp = headerProtectionKey ver cipher secret
@@ -168,14 +169,14 @@
                     sample = Sample $ dec16 "5e5cd55c41f69080575d7999c25a5bfb"
                     mask = protectionMask cipher hp sample
                 key
-                    `shouldBe` Key (dec16 "c6d98ff3441c3fe1b2182094f69caa2ed4b716b65488960a7a984979fb23e1c8")
+                    `shouldBe` Key (dec16' "c6d98ff3441c3fe1b2182094f69caa2ed4b716b65488960a7a984979fb23e1c8")
                 iv
                     `shouldBe` IV (dec16 "e0459b3474bdd0e44a41c144")
                 hp
-                    `shouldBe` Key (dec16 "25a282b9e82f06f21f488917a4fc8f1b73573685608597d0efcb076b0ab7a7a4")
+                    `shouldBe` Key (dec16' "25a282b9e82f06f21f488917a4fc8f1b73573685608597d0efcb076b0ab7a7a4")
                 ku
                     `shouldBe` Secret
-                        (dec16 "1223504755036d556342ee9361d253421a826c9ecdf3c7148684b36b714881f9")
+                        (dec16' "1223504755036d556342ee9361d253421a826c9ecdf3c7148684b36b714881f9")
                 payloadCipherText
                     `shouldBe` Just (dec16 "65", dec16 "5e5cd55c41f69080575d7999c25a5bfb")
                 mask `shouldBe` Mask (dec16 "aefefe7d03")
@@ -194,23 +195,23 @@
             let client_initial_secret@(ClientTrafficSecret cis) = clientInitialSecret ver dcID
             client_initial_secret
                 `shouldBe` ClientTrafficSecret
-                    (dec16 "14ec9d6eb9fd7af83bf5a668bc17a7e283766aade7ecd0891f70f9ff7f4bf47b")
+                    (dec16' "14ec9d6eb9fd7af83bf5a668bc17a7e283766aade7ecd0891f70f9ff7f4bf47b")
             let ckey = aeadKey ver defaultCipher (Secret cis)
-            ckey `shouldBe` Key (dec16 "8b1a0bc121284290a29e0971b5cd045d")
+            ckey `shouldBe` Key (dec16' "8b1a0bc121284290a29e0971b5cd045d")
             let civ = initialVector ver defaultCipher (Secret cis)
             civ `shouldBe` IV (dec16 "91f73e2351d8fa91660e909f")
             let chp = headerProtectionKey ver defaultCipher (Secret cis)
-            chp `shouldBe` Key (dec16 "45b95e15235d6f45a6b19cbcb0294ba9")
+            chp `shouldBe` Key (dec16' "45b95e15235d6f45a6b19cbcb0294ba9")
             let server_initial_secret@(ServerTrafficSecret sis) = serverInitialSecret ver dcID
             server_initial_secret
                 `shouldBe` ServerTrafficSecret
-                    (dec16 "0263db1782731bf4588e7e4d93b7463907cb8cd8200b5da55a8bd488eafc37c1")
+                    (dec16' "0263db1782731bf4588e7e4d93b7463907cb8cd8200b5da55a8bd488eafc37c1")
             let skey = aeadKey ver defaultCipher (Secret sis)
-            skey `shouldBe` Key (dec16 "82db637861d55e1d011f19ea71d5d2a7")
+            skey `shouldBe` Key (dec16' "82db637861d55e1d011f19ea71d5d2a7")
             let siv = initialVector ver defaultCipher (Secret sis)
             siv `shouldBe` IV (dec16 "dd13c276499c0249d3310652")
             let shp = headerProtectionKey ver defaultCipher (Secret sis)
-            shp `shouldBe` Key (dec16 "edf6d05c83121201b436e16877593c3a")
+            shp `shouldBe` Key (dec16' "edf6d05c83121201b436e16877593c3a")
 
         it "describes the examples of Client Initial (RFC 9369: A2)" $ do
             let dcID = makeCID (dec16s "8394c8f03e515708")
@@ -317,7 +318,7 @@
                 let cipher = cipher13_CHACHA20_POLY1305_SHA256
                     secret =
                         Secret $
-                            dec16 "9ac312a7f877468ebe69422748ad00a15443f18203a07d6060f688f30f21632b"
+                            dec16' "9ac312a7f877468ebe69422748ad00a15443f18203a07d6060f688f30f21632b"
                     key = aeadKey ver cipher secret
                     iv = initialVector ver cipher secret
                     hp = headerProtectionKey ver cipher secret
@@ -326,14 +327,14 @@
                     sample = Sample $ dec16 "e7b6b932bc27d786f4bc2bb20f2162ba"
                     mask = protectionMask cipher hp sample
                 key
-                    `shouldBe` Key (dec16 "3bfcddd72bcf02541d7fa0dd1f5f9eeea817e09a6963a0e6c7df0f9a1bab90f2")
+                    `shouldBe` Key (dec16' "3bfcddd72bcf02541d7fa0dd1f5f9eeea817e09a6963a0e6c7df0f9a1bab90f2")
                 iv
                     `shouldBe` IV (dec16 "a6b5bc6ab7dafce30ffff5dd")
                 hp
-                    `shouldBe` Key (dec16 "d659760d2ba434a226fd37b35c69e2da8211d10c4f12538787d65645d5d1b8e2")
+                    `shouldBe` Key (dec16' "d659760d2ba434a226fd37b35c69e2da8211d10c4f12538787d65645d5d1b8e2")
                 ku
                     `shouldBe` Secret
-                        (dec16 "c69374c49e3d2a9466fa689e49d476db5d0dfbc87d32ceeaa6343fd0ae4c7d88")
+                        (dec16' "c69374c49e3d2a9466fa689e49d476db5d0dfbc87d32ceeaa6343fd0ae4c7d88")
                 payloadCipherText
                     `shouldBe` Just (dec16 "0a", dec16 "e7b6b932bc27d786f4bc2bb20f2162ba")
                 mask `shouldBe` Mask (dec16 "97580e32bf")
@@ -363,3 +364,6 @@
             , "3900320408ffffffffffffffff05048000ffff07048000ffff08011001048000"
             , "75300901100f088394c8f03e51570806048000ffff"
             ]
+
+dec16' :: BS.ByteString -> ScrubbedBytes
+dec16' = convert . dec16
