From e4c130fca98f022f5f5e35d4411da38ea86e38ea Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Wed, 30 Nov 2016 03:03:09 -0500 Subject: [PATCH 01/10] Rename sign/verify RSA -> Private/Public These functions work with general public keys and are not RSA-specific --- core/Network/TLS/Handshake/Key.hs | 12 ++++++------ core/Network/TLS/Handshake/Signature.hs | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/Network/TLS/Handshake/Key.hs b/core/Network/TLS/Handshake/Key.hs index 5b916bb6d..9afc1bf9f 100644 --- a/core/Network/TLS/Handshake/Key.hs +++ b/core/Network/TLS/Handshake/Key.hs @@ -9,9 +9,9 @@ -- module Network.TLS.Handshake.Key ( encryptRSA - , signRSA + , signPrivate , decryptRSA - , verifyRSA + , verifyPublic , generateDHE , generateECDHE ) where @@ -37,8 +37,8 @@ encryptRSA ctx content = do Left err -> fail ("rsa encrypt failed: " ++ show err) Right econtent -> return econtent -signRSA :: Context -> Role -> Hash -> ByteString -> IO ByteString -signRSA ctx _ hsh content = do +signPrivate :: Context -> Role -> Hash -> ByteString -> IO ByteString +signPrivate ctx _ hsh content = do privateKey <- usingHState ctx getLocalPrivateKey usingState_ ctx $ do r <- withRNG $ kxSign privateKey hsh content @@ -54,8 +54,8 @@ decryptRSA ctx econtent = do let cipher = if ver < TLS10 then econtent else B.drop 2 econtent withRNG $ kxDecrypt privateKey cipher -verifyRSA :: Context -> Role -> Hash -> ByteString -> ByteString -> IO Bool -verifyRSA ctx _ hsh econtent sign = do +verifyPublic :: Context -> Role -> Hash -> ByteString -> ByteString -> IO Bool +verifyPublic ctx _ hsh econtent sign = do publicKey <- usingHState ctx getRemotePublicKey return $ kxVerify publicKey hsh econtent sign diff --git a/core/Network/TLS/Handshake/Signature.hs b/core/Network/TLS/Handshake/Signature.hs index dddc93985..fdd881bd2 100644 --- a/core/Network/TLS/Handshake/Signature.hs +++ b/core/Network/TLS/Handshake/Signature.hs @@ -107,7 +107,7 @@ signatureCreate ctx malg (hashAlg, toSign) = do case (malg, hashAlg) of (Nothing, SHA1_MD5) -> hashFinal $ hashUpdate (hashInit SHA1_MD5) toSign _ -> toSign - DigitallySigned malg <$> signRSA ctx cc hashAlg signData + DigitallySigned malg <$> signPrivate ctx cc hashAlg signData signatureVerify :: Context -> DigitallySigned -> SignatureAlgorithm -> Bytes -> IO Bool signatureVerify ctx digSig@(DigitallySigned hashSigAlg _) sigAlgExpected toVerifyData = do @@ -133,9 +133,9 @@ signatureVerifyWithHashDescr :: Context signatureVerifyWithHashDescr ctx sigAlgExpected (DigitallySigned _ bs) (hashDescr, toVerify) = do cc <- usingState_ ctx $ isClientContext case sigAlgExpected of - SignatureRSA -> verifyRSA ctx cc hashDescr toVerify bs - SignatureDSS -> verifyRSA ctx cc hashDescr toVerify bs - SignatureECDSA -> verifyRSA ctx cc hashDescr toVerify bs + SignatureRSA -> verifyPublic ctx cc hashDescr toVerify bs + SignatureDSS -> verifyPublic ctx cc hashDescr toVerify bs + SignatureECDSA -> verifyPublic ctx cc hashDescr toVerify bs _ -> error "signature verification not implemented yet" digitallySignParams :: Context -> Bytes -> SignatureAlgorithm -> IO DigitallySigned From e5aa1f5703a631e8f626fa391d2013ee9c6d374c Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Wed, 30 Nov 2016 03:04:52 -0500 Subject: [PATCH 02/10] Say fixed DH/ECDH ciphers should not be implemented This just changes a comment. These are neither used nor needed in TLS, and have e.g. been withdrawn from OpenSSL. --- core/Network/TLS/Context.hs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core/Network/TLS/Context.hs b/core/Network/TLS/Context.hs index abf22041e..3a56f83fb 100644 --- a/core/Network/TLS/Context.hs +++ b/core/Network/TLS/Context.hs @@ -122,13 +122,16 @@ instance TLSParams ServerParams where CipherKeyExchange_DHE_RSA -> canSignRSA && canDHE CipherKeyExchange_DHE_DSS -> canSignDSS && canDHE CipherKeyExchange_ECDHE_RSA -> canSignRSA - -- unimplemented: non ephemeral DH + -- unimplemented: EC + CipherKeyExchange_ECDHE_ECDSA -> False + -- unimplemented: non ephemeral DH & ECDH. + -- Note, these *should not* be implemented, and have + -- (for example) been removed in OpenSSL 1.1.0 + -- CipherKeyExchange_DH_DSS -> False CipherKeyExchange_DH_RSA -> False - -- unimplemented: EC CipherKeyExchange_ECDH_ECDSA -> False CipherKeyExchange_ECDH_RSA -> False - CipherKeyExchange_ECDHE_ECDSA -> False canDHE = isJust $ serverDHEParams sparams canSignDSS = SignatureDSS `elem` signingAlgs From d69b9fd98aab018ca5d943d0297d7fe676e87350 Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Wed, 30 Nov 2016 03:07:41 -0500 Subject: [PATCH 03/10] Define prf_TLS parametrized by version and hash With version >= TLS12 the PRF is cipher dependent. We throw in the version as an ignored parameter as future-proofing. The caller will pass in the protocol version and the PRF from the appropriate cipher. --- core/Network/TLS/MAC.hs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/Network/TLS/MAC.hs b/core/Network/TLS/MAC.hs index 4a8465312..70c14e3bd 100644 --- a/core/Network/TLS/MAC.hs +++ b/core/Network/TLS/MAC.hs @@ -11,10 +11,12 @@ module Network.TLS.MAC , prf_MD5 , prf_SHA1 , prf_SHA256 + , prf_TLS , prf_MD5SHA1 ) where import Network.TLS.Crypto +import Network.TLS.Types import qualified Data.ByteString as B import Data.ByteString (ByteString) import Data.Bits (xor) @@ -71,3 +73,9 @@ prf_MD5SHA1 secret seed len = prf_SHA256 :: ByteString -> ByteString -> Int -> ByteString prf_SHA256 secret seed len = B.concat $ hmacIter (hmac SHA256) secret seed seed len + +-- | For now we ignore the version, but perhaps some day the PRF will depend +-- not only on the cipher PRF algorithm, but also on the protocol version. +prf_TLS :: Version -> Hash -> ByteString -> ByteString -> Int -> ByteString +prf_TLS _ halg secret seed len = + B.concat $ hmacIter (hmac halg) secret seed seed len From 063b8fcd4821bb7fa27640d1dbc242499ca51c25 Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Wed, 30 Nov 2016 03:18:20 -0500 Subject: [PATCH 04/10] Add cipherPRFHash attribute This is needed for SHA384 ciphers in TLS12 and later. Also fix typos: cipher_ECDHE_RSA_AES256CBC_SHA384: cipherID cipher_ECDHE_RSA_AES256GCM_SHA384: cipherName --- core/Network/TLS/Cipher.hs | 1 + core/Network/TLS/Extra/Cipher.hs | 24 ++++++++++++++++++++++-- core/Tests/Connection.hs | 1 + 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/core/Network/TLS/Cipher.hs b/core/Network/TLS/Cipher.hs index 9ef7b05e2..15e4d6ea2 100644 --- a/core/Network/TLS/Cipher.hs +++ b/core/Network/TLS/Cipher.hs @@ -125,6 +125,7 @@ data Cipher = Cipher , cipherBulk :: Bulk , cipherKeyExchange :: CipherKeyExchangeType , cipherMinVer :: Maybe Version + , cipherPRFHash :: Maybe Hash } cipherKeyBlockSize :: Cipher -> Int diff --git a/core/Network/TLS/Extra/Cipher.hs b/core/Network/TLS/Extra/Cipher.hs index 3691e907f..8b7cf46af 100644 --- a/core/Network/TLS/Extra/Cipher.hs +++ b/core/Network/TLS/Extra/Cipher.hs @@ -260,6 +260,7 @@ cipher_null_MD5 = Cipher , cipherName = "RSA-null-MD5" , cipherBulk = bulk_null , cipherHash = MD5 + , cipherPRFHash = Nothing , cipherKeyExchange = CipherKeyExchange_RSA , cipherMinVer = Nothing } @@ -271,6 +272,7 @@ cipher_null_SHA1 = Cipher , cipherName = "RSA-null-SHA1" , cipherBulk = bulk_null , cipherHash = SHA1 + , cipherPRFHash = Nothing , cipherKeyExchange = CipherKeyExchange_RSA , cipherMinVer = Nothing } @@ -282,6 +284,7 @@ cipher_RC4_128_MD5 = Cipher , cipherName = "RSA-rc4-128-md5" , cipherBulk = bulk_rc4 , cipherHash = MD5 + , cipherPRFHash = Nothing , cipherKeyExchange = CipherKeyExchange_RSA , cipherMinVer = Nothing } @@ -293,6 +296,7 @@ cipher_RC4_128_SHA1 = Cipher , cipherName = "RSA-rc4-128-sha1" , cipherBulk = bulk_rc4 , cipherHash = SHA1 + , cipherPRFHash = Nothing , cipherKeyExchange = CipherKeyExchange_RSA , cipherMinVer = Nothing } @@ -304,6 +308,7 @@ cipher_AES128_SHA1 = Cipher , cipherName = "RSA-AES128-SHA1" , cipherBulk = bulk_aes128 , cipherHash = SHA1 + , cipherPRFHash = Nothing , cipherKeyExchange = CipherKeyExchange_RSA , cipherMinVer = Just SSL3 } @@ -315,6 +320,7 @@ cipher_DHE_DSS_AES128_SHA1 = Cipher , cipherName = "DHE-DSA-AES128-SHA1" , cipherBulk = bulk_aes128 , cipherHash = SHA1 + , cipherPRFHash = Nothing , cipherKeyExchange = CipherKeyExchange_DHE_DSS , cipherMinVer = Nothing } @@ -326,6 +332,7 @@ cipher_DHE_RSA_AES128_SHA1 = Cipher , cipherName = "DHE-RSA-AES128-SHA1" , cipherBulk = bulk_aes128 , cipherHash = SHA1 + , cipherPRFHash = Nothing , cipherKeyExchange = CipherKeyExchange_DHE_RSA , cipherMinVer = Nothing } @@ -337,6 +344,7 @@ cipher_AES256_SHA1 = Cipher , cipherName = "RSA-AES256-SHA1" , cipherBulk = bulk_aes256 , cipherHash = SHA1 + , cipherPRFHash = Nothing , cipherKeyExchange = CipherKeyExchange_RSA , cipherMinVer = Just SSL3 } @@ -364,6 +372,7 @@ cipher_AES128_SHA256 = Cipher , cipherName = "RSA-AES128-SHA256" , cipherBulk = bulk_aes128 , cipherHash = SHA256 + , cipherPRFHash = Just SHA256 , cipherKeyExchange = CipherKeyExchange_RSA , cipherMinVer = Just TLS12 } @@ -375,6 +384,7 @@ cipher_AES256_SHA256 = Cipher , cipherName = "RSA-AES256-SHA256" , cipherBulk = bulk_aes256 , cipherHash = SHA256 + , cipherPRFHash = Just SHA256 , cipherKeyExchange = CipherKeyExchange_RSA , cipherMinVer = Just TLS12 } @@ -392,6 +402,7 @@ cipher_DHE_RSA_AES128_SHA256 = cipher_DHE_RSA_AES128_SHA1 { cipherID = 0x67 , cipherName = "DHE-RSA-AES128-SHA256" , cipherHash = SHA256 + , cipherPRFHash = Just SHA256 , cipherMinVer = Just TLS12 } @@ -409,6 +420,7 @@ cipher_RSA_3DES_EDE_CBC_SHA1 = Cipher , cipherName = "RSA-3DES-EDE-CBC-SHA1" , cipherBulk = bulk_tripledes_ede , cipherHash = SHA1 + , cipherPRFHash = Nothing , cipherKeyExchange = CipherKeyExchange_RSA , cipherMinVer = Nothing } @@ -419,6 +431,7 @@ cipher_DHE_RSA_AES128GCM_SHA256 = Cipher , cipherName = "DHE-RSA-AES128GCM-SHA256" , cipherBulk = bulk_aes128gcm , cipherHash = SHA256 + , cipherPRFHash = Just SHA256 , cipherKeyExchange = CipherKeyExchange_DHE_RSA , cipherMinVer = Just TLS12 -- RFC 5288 Sec 4 } @@ -429,6 +442,7 @@ cipher_ECDHE_RSA_AES128CBC_SHA = Cipher , cipherName = "ECDHE-RSA-AES128CBC-SHA" , cipherBulk = bulk_aes128 , cipherHash = SHA1 + , cipherPRFHash = Nothing , cipherKeyExchange = CipherKeyExchange_ECDHE_RSA , cipherMinVer = Just TLS10 } @@ -439,6 +453,7 @@ cipher_ECDHE_RSA_AES256CBC_SHA = Cipher , cipherName = "ECDHE-RSA-AES256CBC-SHA" , cipherBulk = bulk_aes256 , cipherHash = SHA1 + , cipherPRFHash = Nothing , cipherKeyExchange = CipherKeyExchange_ECDHE_RSA , cipherMinVer = Just TLS10 } @@ -449,16 +464,18 @@ cipher_ECDHE_RSA_AES128CBC_SHA256 = Cipher , cipherName = "ECDHE-RSA-AES128CBC-SHA256" , cipherBulk = bulk_aes128 , cipherHash = SHA256 + , cipherPRFHash = Just SHA256 , cipherKeyExchange = CipherKeyExchange_ECDHE_RSA , cipherMinVer = Just TLS12 -- RFC 5288 Sec 4 } cipher_ECDHE_RSA_AES256CBC_SHA384 :: Cipher cipher_ECDHE_RSA_AES256CBC_SHA384 = Cipher - { cipherID = 0xc027 + { cipherID = 0xc028 , cipherName = "ECDHE-RSA-AES256CBC-SHA384" , cipherBulk = bulk_aes256 , cipherHash = SHA384 + , cipherPRFHash = Just SHA384 , cipherKeyExchange = CipherKeyExchange_ECDHE_RSA , cipherMinVer = Just TLS12 -- RFC 5288 Sec 4 } @@ -469,6 +486,7 @@ cipher_ECDHE_ECDSA_AES128GCM_SHA256 = Cipher , cipherName = "ECDHE-ECDSA-AES128GCM-SHA256" , cipherBulk = bulk_aes128gcm , cipherHash = SHA256 + , cipherPRFHash = Just SHA256 , cipherKeyExchange = CipherKeyExchange_ECDHE_ECDSA , cipherMinVer = Just TLS12 -- RFC 5289 } @@ -479,6 +497,7 @@ cipher_ECDHE_RSA_AES128GCM_SHA256 = Cipher , cipherName = "ECDHE-RSA-AES128GCM-SHA256" , cipherBulk = bulk_aes128gcm , cipherHash = SHA256 + , cipherPRFHash = Just SHA256 , cipherKeyExchange = CipherKeyExchange_ECDHE_RSA , cipherMinVer = Just TLS12 -- RFC 5288 Sec 4 } @@ -486,9 +505,10 @@ cipher_ECDHE_RSA_AES128GCM_SHA256 = Cipher cipher_ECDHE_RSA_AES256GCM_SHA384 :: Cipher cipher_ECDHE_RSA_AES256GCM_SHA384 = Cipher { cipherID = 0xc030 - , cipherName = "ECDHE-RSA-AES256GCM-SHA256" + , cipherName = "ECDHE-RSA-AES256GCM-SHA384" , cipherBulk = bulk_aes256gcm , cipherHash = SHA384 + , cipherPRFHash = Just SHA384 , cipherKeyExchange = CipherKeyExchange_ECDHE_RSA , cipherMinVer = Just TLS12 -- RFC 5289 } diff --git a/core/Tests/Connection.hs b/core/Tests/Connection.hs index 5847932d4..4b82b26a5 100644 --- a/core/Tests/Connection.hs +++ b/core/Tests/Connection.hs @@ -38,6 +38,7 @@ blockCipher = Cipher , bulkF = BulkBlockF $ \_ _ _ -> (\m -> (m, B.empty)) } , cipherHash = MD5 + , cipherPRFHash = Nothing , cipherKeyExchange = CipherKeyExchange_RSA , cipherMinVer = Nothing } From 38ffd2234b209cd03e7a76821d59eafe5733af40 Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Wed, 30 Nov 2016 03:26:22 -0500 Subject: [PATCH 05/10] SHA384 PRF/Hash support for TLS12 ciphers The optional cipherPRFHash field determines the Hash in the PRF HMAC (which otherwise defaults to SHA256), while the required cipherHash field determines the Hash used in the Finished message (and as MAC for non-AEAD ciphers). --- core/Network/TLS/Handshake/State.hs | 18 ++++++-- core/Network/TLS/Packet.hs | 70 +++++++++++++++++++---------- 2 files changed, 60 insertions(+), 28 deletions(-) diff --git a/core/Network/TLS/Handshake/State.hs b/core/Network/TLS/Handshake/State.hs index 833e48c09..8c5581ce1 100644 --- a/core/Network/TLS/Handshake/State.hs +++ b/core/Network/TLS/Handshake/State.hs @@ -188,7 +188,8 @@ getHandshakeDigest ver role = gets gen where gen hst = case hstHandshakeDigest hst of Right hashCtx -> let msecret = fromJust "master secret" $ hstMasterSecret hst - in generateFinish ver msecret hashCtx + cipher = fromJust "cipher" $ hstPendingCipher hst + in generateFinish ver cipher msecret hashCtx Left _ -> error "un-initialized handshake digest" generateFinish | role == ClientRole = generateClientFinished @@ -203,7 +204,8 @@ setMasterSecretFromPre :: ByteArrayAccess preMaster setMasterSecretFromPre ver role premasterSecret = do secret <- genSecret <$> get setMasterSecret ver role secret - where genSecret hst = generateMasterSecret ver + where genSecret hst = + generateMasterSecret ver (fromJust "cipher" $ hstPendingCipher hst) premasterSecret (hstClientRandom hst) (fromJust "server random" $ hstServerRandom hst) @@ -227,7 +229,7 @@ computeKeyBlock hst masterSecret ver cc = (pendingTx, pendingRx) else 0 keySize = bulkKeySize bulk ivSize = bulkIVSize bulk - kb = generateKeyBlock ver (hstClientRandom hst) + kb = generateKeyBlock ver cipher (hstClientRandom hst) (fromJust "server random" $ hstServerRandom hst) masterSecret keyblockSize @@ -271,6 +273,14 @@ setServerHelloParameters ver sran cipher compression = do , hstPendingCompression = compression , hstHandshakeDigest = updateDigest $ hstHandshakeDigest hst } - where hashAlg = if ver < TLS12 then SHA1_MD5 else SHA256 + where hashAlg = getHash ver cipher updateDigest (Left bytes) = Right $ foldl hashUpdate (hashInit hashAlg) $ reverse bytes updateDigest (Right _) = error "cannot initialize digest with another digest" + +-- The TLS12 Hash is cipher specific, and some TLS12 algorithms use SHA384 +-- instead of the default SHA256. +getHash :: Version -> Cipher -> Hash +getHash ver ciph + | ver < TLS12 = SHA1_MD5 + | maybe True (< TLS12) (cipherMinVer ciph) = SHA256 + | otherwise = cipherHash ciph diff --git a/core/Network/TLS/Packet.hs b/core/Network/TLS/Packet.hs index 3172a1aa0..1685a0fba 100644 --- a/core/Network/TLS/Packet.hs +++ b/core/Network/TLS/Packet.hs @@ -71,7 +71,7 @@ import Data.ASN1.BinaryEncoding (DER(..)) import Data.X509 (CertificateChainRaw(..), encodeCertificateChain, decodeCertificateChain) import Network.TLS.Crypto import Network.TLS.MAC -import Network.TLS.Cipher (CipherKeyExchangeType(..)) +import Network.TLS.Cipher (CipherKeyExchangeType(..), Cipher(..)) import Network.TLS.Util.Serialization (os2ip,i2ospOf_) import Data.ByteString (ByteString) import qualified Data.ByteString as B @@ -380,9 +380,9 @@ encodeHandshakeContent (ClientHello version random session cipherIDs compression putExtensions exts return () -encodeHandshakeContent (ServerHello version random session cipherID compressionID exts) = +encodeHandshakeContent (ServerHello version random session cipherid compressionID exts) = putVersion version >> putServerRandom32 random >> putSession session - >> putWord16 cipherID >> putWord8 compressionID + >> putWord16 cipherid >> putWord8 compressionID >> putExtensions exts >> return () encodeHandshakeContent (Certificates cc) = putOpaque24 (runPut $ mapM_ putOpaque24 certs) @@ -581,6 +581,14 @@ decodeReallyServerKeyXchgAlgorithmData ver cke = -} type PRF = Bytes -> Bytes -> Int -> Bytes +-- | The TLS12 PRF is cipher specific, and some TLS12 algorithms use SHA384 +-- instead of the default SHA256. +getPRF :: Version -> Cipher -> PRF +getPRF ver ciph + | ver < TLS12 = prf_MD5SHA1 + | maybe True (< TLS12) (cipherMinVer ciph) = prf_SHA256 + | otherwise = prf_TLS ver $ maybe SHA256 id $ cipherPRFHash ciph + generateMasterSecret_SSL :: ByteArrayAccess preMaster => preMaster -> ClientRandom -> ServerRandom -> Bytes generateMasterSecret_SSL premasterSecret (ClientRandom c) (ServerRandom s) = B.concat $ map (computeMD5) ["A","BB","CCC"] @@ -592,12 +600,16 @@ generateMasterSecret_TLS prf premasterSecret (ClientRandom c) (ServerRandom s) = prf (B.convert premasterSecret) seed 48 where seed = B.concat [ "master secret", c, s ] -generateMasterSecret :: ByteArrayAccess preMaster => Version -> preMaster -> ClientRandom -> ServerRandom -> Bytes -generateMasterSecret SSL2 = generateMasterSecret_SSL -generateMasterSecret SSL3 = generateMasterSecret_SSL -generateMasterSecret TLS10 = generateMasterSecret_TLS prf_MD5SHA1 -generateMasterSecret TLS11 = generateMasterSecret_TLS prf_MD5SHA1 -generateMasterSecret TLS12 = generateMasterSecret_TLS prf_SHA256 +generateMasterSecret :: ByteArrayAccess preMaster + => Version + -> Cipher + -> preMaster + -> ClientRandom + -> ServerRandom + -> Bytes +generateMasterSecret SSL2 _ = generateMasterSecret_SSL +generateMasterSecret SSL3 _ = generateMasterSecret_SSL +generateMasterSecret v c = generateMasterSecret_TLS $ getPRF v c generateKeyBlock_TLS :: PRF -> ClientRandom -> ServerRandom -> Bytes -> Int -> Bytes generateKeyBlock_TLS prf (ClientRandom c) (ServerRandom s) mastersecret kbsize = @@ -610,12 +622,16 @@ generateKeyBlock_SSL (ClientRandom c) (ServerRandom s) mastersecret kbsize = computeMD5 label = hash MD5 $ B.concat [ mastersecret, computeSHA1 label ] computeSHA1 label = hash SHA1 $ B.concat [ label, mastersecret, s, c ] -generateKeyBlock :: Version -> ClientRandom -> ServerRandom -> Bytes -> Int -> Bytes -generateKeyBlock SSL2 = generateKeyBlock_SSL -generateKeyBlock SSL3 = generateKeyBlock_SSL -generateKeyBlock TLS10 = generateKeyBlock_TLS prf_MD5SHA1 -generateKeyBlock TLS11 = generateKeyBlock_TLS prf_MD5SHA1 -generateKeyBlock TLS12 = generateKeyBlock_TLS prf_SHA256 +generateKeyBlock :: Version + -> Cipher + -> ClientRandom + -> ServerRandom + -> Bytes + -> Int + -> Bytes +generateKeyBlock SSL2 _ = generateKeyBlock_SSL +generateKeyBlock SSL3 _ = generateKeyBlock_SSL +generateKeyBlock v c = generateKeyBlock_TLS $ getPRF v c generateFinished_TLS :: PRF -> Bytes -> Bytes -> HashCtx -> Bytes generateFinished_TLS prf label mastersecret hashctx = prf mastersecret seed 12 @@ -632,17 +648,23 @@ generateFinished_SSL sender mastersecret hashctx = B.concat [md5hash, sha1hash] pad2 = B.replicate 48 0x5c pad1 = B.replicate 48 0x36 -generateClientFinished :: Version -> Bytes -> HashCtx -> Bytes -generateClientFinished ver +generateClientFinished :: Version + -> Cipher + -> Bytes + -> HashCtx + -> Bytes +generateClientFinished ver ciph | ver < TLS10 = generateFinished_SSL "CLNT" - | ver < TLS12 = generateFinished_TLS prf_MD5SHA1 "client finished" - | otherwise = generateFinished_TLS prf_SHA256 "client finished" - -generateServerFinished :: Version -> Bytes -> HashCtx -> Bytes -generateServerFinished ver + | otherwise = generateFinished_TLS (getPRF ver ciph) "client finished" + +generateServerFinished :: Version + -> Cipher + -> Bytes + -> HashCtx + -> Bytes +generateServerFinished ver ciph | ver < TLS10 = generateFinished_SSL "SRVR" - | ver < TLS12 = generateFinished_TLS prf_MD5SHA1 "server finished" - | otherwise = generateFinished_TLS prf_SHA256 "server finished" + | otherwise = generateFinished_TLS (getPRF ver ciph) "server finished" generateCertificateVerify_SSL :: Bytes -> HashCtx -> Bytes generateCertificateVerify_SSL = generateFinished_SSL "" From 2a404786ed27d5fd521e0577cc1e227256939e45 Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Wed, 30 Nov 2016 03:32:44 -0500 Subject: [PATCH 06/10] Add eight new ciphers Two are AES256GCM-SHA384 variants of existing AES128GCM-SHA256 ciphers: DHE-RSA-AES256GCM-SHA384 ECDHE-ECDSA-AES256GCM-SHA384 Two more are CBC alternatives to GCM for ECDHE-ECDSA with SHA2: ECDHE-ECDSA-AES128CBC-SHA256 ECDHE-ECDSA-AES256CBC-SHA384 Two more are 128/256-bit GCM alternatives to CBC for non-DHE RSA key-exchange: RSA-AES128GCM-SHA256 RSA-AES256GCM-SHA384 The last two flesh out ECDHE-ECDSA support with CBC+SHA1 ECDHE-ECDSA-AES128CBC-SHA ECDHE-ECDSA-AES256CBC-SHA We also note that: cipher_RSA_3DES_EDE_CBC_SHA1 cipher_RC4_128_MD5 cipher_RC4_128_SHA1 cipher_null_MD5 cipher_DHE_DSS_RC4_SHA1 are obsolete and possibly non-standard (hintint that they should not be used). --- core/Network/TLS/Extra/Cipher.hs | 120 +++++++++++++++++++++++++++++-- 1 file changed, 115 insertions(+), 5 deletions(-) diff --git a/core/Network/TLS/Extra/Cipher.hs b/core/Network/TLS/Extra/Cipher.hs index 8b7cf46af..5179e9348 100644 --- a/core/Network/TLS/Extra/Cipher.hs +++ b/core/Network/TLS/Extra/Cipher.hs @@ -18,29 +18,38 @@ module Network.TLS.Extra.Cipher , ciphersuite_dhe_dss -- * individual ciphers , cipher_null_SHA1 - , cipher_null_MD5 - , cipher_RC4_128_MD5 - , cipher_RC4_128_SHA1 , cipher_AES128_SHA1 , cipher_AES256_SHA1 , cipher_AES128_SHA256 , cipher_AES256_SHA256 - , cipher_RSA_3DES_EDE_CBC_SHA1 + , cipher_AES128GCM_SHA256 + , cipher_AES256GCM_SHA384 , cipher_DHE_RSA_AES128_SHA1 , cipher_DHE_RSA_AES256_SHA1 , cipher_DHE_RSA_AES128_SHA256 , cipher_DHE_RSA_AES256_SHA256 , cipher_DHE_DSS_AES128_SHA1 , cipher_DHE_DSS_AES256_SHA1 - , cipher_DHE_DSS_RC4_SHA1 , cipher_DHE_RSA_AES128GCM_SHA256 + , cipher_DHE_RSA_AES256GCM_SHA384 , cipher_ECDHE_RSA_AES128GCM_SHA256 , cipher_ECDHE_RSA_AES256GCM_SHA384 , cipher_ECDHE_RSA_AES128CBC_SHA256 , cipher_ECDHE_RSA_AES128CBC_SHA , cipher_ECDHE_RSA_AES256CBC_SHA , cipher_ECDHE_RSA_AES256CBC_SHA384 + , cipher_ECDHE_ECDSA_AES128CBC_SHA + , cipher_ECDHE_ECDSA_AES256CBC_SHA + , cipher_ECDHE_ECDSA_AES128CBC_SHA256 + , cipher_ECDHE_ECDSA_AES256CBC_SHA384 , cipher_ECDHE_ECDSA_AES128GCM_SHA256 + , cipher_ECDHE_ECDSA_AES256GCM_SHA384 + -- * obsolete and non-standard ciphers + , cipher_RSA_3DES_EDE_CBC_SHA1 + , cipher_RC4_128_MD5 + , cipher_RC4_128_SHA1 + , cipher_null_MD5 + , cipher_DHE_DSS_RC4_SHA1 ) where import qualified Data.ByteString as B @@ -389,6 +398,31 @@ cipher_AES256_SHA256 = Cipher , cipherMinVer = Just TLS12 } +-- | AESGCM cipher (128 bit key), RSA key exchange. +-- The SHA256 digest is used as a PRF, not as a MAC. +cipher_AES128GCM_SHA256 :: Cipher +cipher_AES128GCM_SHA256 = Cipher + { cipherID = 0x9c + , cipherName = "RSA-AES128GCM-SHA256" + , cipherBulk = bulk_aes128gcm + , cipherHash = SHA256 + , cipherPRFHash = Just SHA256 + , cipherKeyExchange = CipherKeyExchange_RSA + , cipherMinVer = Just TLS12 + } + +-- | AESGCM cipher (256 bit key), RSA key exchange. +-- The SHA384 digest is used as a PRF, not as a MAC. +cipher_AES256GCM_SHA384 :: Cipher +cipher_AES256GCM_SHA384 = Cipher + { cipherID = 0x9d + , cipherName = "RSA-AES256GCM-SHA384" + , cipherBulk = bulk_aes256gcm + , cipherHash = SHA384 + , cipherPRFHash = Just SHA384 + , cipherKeyExchange = CipherKeyExchange_RSA + , cipherMinVer = Just TLS12 + } cipher_DHE_DSS_RC4_SHA1 :: Cipher cipher_DHE_DSS_RC4_SHA1 = cipher_DHE_DSS_AES128_SHA1 @@ -436,6 +470,39 @@ cipher_DHE_RSA_AES128GCM_SHA256 = Cipher , cipherMinVer = Just TLS12 -- RFC 5288 Sec 4 } +cipher_DHE_RSA_AES256GCM_SHA384 :: Cipher +cipher_DHE_RSA_AES256GCM_SHA384 = Cipher + { cipherID = 0x9f + , cipherName = "DHE-RSA-AES256GCM-SHA384" + , cipherBulk = bulk_aes256gcm + , cipherHash = SHA384 + , cipherPRFHash = Just SHA384 + , cipherKeyExchange = CipherKeyExchange_DHE_RSA + , cipherMinVer = Just TLS12 + } + +cipher_ECDHE_ECDSA_AES128CBC_SHA :: Cipher +cipher_ECDHE_ECDSA_AES128CBC_SHA = Cipher + { cipherID = 0xc009 + , cipherName = "ECDHE-ECDSA-AES128CBC-SHA" + , cipherBulk = bulk_aes128 + , cipherHash = SHA1 + , cipherPRFHash = Nothing + , cipherKeyExchange = CipherKeyExchange_ECDHE_RSA + , cipherMinVer = Just TLS10 + } + +cipher_ECDHE_ECDSA_AES256CBC_SHA :: Cipher +cipher_ECDHE_ECDSA_AES256CBC_SHA = Cipher + { cipherID = 0xc00A + , cipherName = "ECDHE-ECDSA-AES256CBC-SHA" + , cipherBulk = bulk_aes256 + , cipherHash = SHA1 + , cipherPRFHash = Nothing + , cipherKeyExchange = CipherKeyExchange_ECDHE_RSA + , cipherMinVer = Just TLS10 + } + cipher_ECDHE_RSA_AES128CBC_SHA :: Cipher cipher_ECDHE_RSA_AES128CBC_SHA = Cipher { cipherID = 0xc013 @@ -480,6 +547,28 @@ cipher_ECDHE_RSA_AES256CBC_SHA384 = Cipher , cipherMinVer = Just TLS12 -- RFC 5288 Sec 4 } +cipher_ECDHE_ECDSA_AES128CBC_SHA256 :: Cipher +cipher_ECDHE_ECDSA_AES128CBC_SHA256 = Cipher + { cipherID = 0xc023 + , cipherName = "ECDHE-ECDSA-AES128CBC-SHA256" + , cipherBulk = bulk_aes128 + , cipherHash = SHA256 + , cipherPRFHash = Just SHA256 + , cipherKeyExchange = CipherKeyExchange_ECDHE_ECDSA + , cipherMinVer = Just TLS12 -- RFC 5289 + } + +cipher_ECDHE_ECDSA_AES256CBC_SHA384 :: Cipher +cipher_ECDHE_ECDSA_AES256CBC_SHA384 = Cipher + { cipherID = 0xc024 + , cipherName = "ECDHE-ECDSA-AES256CBC-SHA384" + , cipherBulk = bulk_aes256 + , cipherHash = SHA384 + , cipherPRFHash = Just SHA384 + , cipherKeyExchange = CipherKeyExchange_ECDHE_ECDSA + , cipherMinVer = Just TLS12 -- RFC 5289 + } + cipher_ECDHE_ECDSA_AES128GCM_SHA256 :: Cipher cipher_ECDHE_ECDSA_AES128GCM_SHA256 = Cipher { cipherID = 0xc02b @@ -491,6 +580,17 @@ cipher_ECDHE_ECDSA_AES128GCM_SHA256 = Cipher , cipherMinVer = Just TLS12 -- RFC 5289 } +cipher_ECDHE_ECDSA_AES256GCM_SHA384 :: Cipher +cipher_ECDHE_ECDSA_AES256GCM_SHA384 = Cipher + { cipherID = 0xc02c + , cipherName = "ECDHE-ECDSA-AES256GCM-SHA384" + , cipherBulk = bulk_aes256gcm + , cipherHash = SHA384 + , cipherPRFHash = Just SHA384 + , cipherKeyExchange = CipherKeyExchange_ECDHE_ECDSA + , cipherMinVer = Just TLS12 -- RFC 5289 + } + cipher_ECDHE_RSA_AES128GCM_SHA256 :: Cipher cipher_ECDHE_RSA_AES128GCM_SHA256 = Cipher { cipherID = 0xc02f @@ -545,6 +645,9 @@ CipherSuite TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x19 }; CipherSuite TLS_DH_anon_WITH_DES_CBC_SHA = { 0x00,0x1A }; CipherSuite TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = { 0x00,0x1B }; +TLS-RSA-WITH-AES-128-GCM-SHA256 {0x00,0x9C} +TLS-RSA-WITH-AES-256-GCM-SHA384 {0x00,0x9D} + TLS-DHE-RSA-WITH-AES-128-CBC-SHA {0x00,0x33} TLS-DHE-RSA-WITH-AES-256-CBC-SHA {0x00,0x39} TLS-DHE-RSA-WITH-AES-128-CBC-SHA256 {0x00,0x67} @@ -574,6 +677,13 @@ TLS-ECDHE-RSA-WITH-3DES-EDE-CBC-SHA {0xC0,0x12} TLS-ECDHE-RSA-WITH-RC4-128-SHA {0xC0,0x11} TLS-ECDHE-RSA-WITH-NULL-SHA {0xC0,0x10} +TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA {0xC0,0x09} +TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA {0xC0,0x0A} +TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256 {0xC0,0x23} +TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384 {0xC0,0x24} +TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256 {0xC0,0x2B} +TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384 {0xC0,0x2C} + TLS-PSK-WITH-RC4-128-SHA {0x00,0x8A} TLS-PSK-WITH-3DES-EDE-CBC-SHA {0x00,0x8B} TLS-PSK-WITH-AES-128-CBC-SHA {0x00,0x8C} From 009adc89be67302ade4f29c23ec79c1dfbf0bc4c Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Wed, 30 Nov 2016 07:00:24 -0500 Subject: [PATCH 07/10] Create a "ciphersuite_default" and improve "all" and "strong" "ciphersuite_default" is "ciphersuite_all", minus the obsolete stuff. Suggest using this as the "Supported" set of ciphers. --- core/Network/TLS/Extra/Cipher.hs | 81 +++++++++++++++++++++++--------- core/Network/TLS/Parameters.hs | 6 ++- 2 files changed, 65 insertions(+), 22 deletions(-) diff --git a/core/Network/TLS/Extra/Cipher.hs b/core/Network/TLS/Extra/Cipher.hs index 5179e9348..8308c27d7 100644 --- a/core/Network/TLS/Extra/Cipher.hs +++ b/core/Network/TLS/Extra/Cipher.hs @@ -10,7 +10,8 @@ module Network.TLS.Extra.Cipher ( -- * cipher suite - ciphersuite_all + ciphersuite_default + , ciphersuite_all , ciphersuite_medium , ciphersuite_strong , ciphersuite_unencrypted @@ -143,44 +144,82 @@ rc4 _ bulkKey = BulkStream (combineRC4 $ RC4.initialize bulkKey) let (ctx', output) = RC4.combine ctx input in (output, BulkStream (combineRC4 ctx')) +-- | All AES ciphers supported ordered from strong to weak. This choice +-- of ciphersuites should satisfy most normal needs. For otherwise strong +-- ciphers we make little distinction between AES128 and AES256, and list +-- each but the weakest of the AES128 ciphers ahead of the corresponding AES256 +-- ciphers. +ciphersuite_default :: [Cipher] +ciphersuite_default = + [ -- First the PFS + GCM + SHA2 ciphers + cipher_ECDHE_ECDSA_AES128GCM_SHA256, cipher_ECDHE_ECDSA_AES256GCM_SHA384 + , cipher_ECDHE_RSA_AES128GCM_SHA256, cipher_ECDHE_RSA_AES256GCM_SHA384 + , cipher_DHE_RSA_AES128GCM_SHA256, cipher_DHE_RSA_AES256GCM_SHA384 + -- Next the PFS + CBC + SHA2 ciphers + , cipher_ECDHE_ECDSA_AES128CBC_SHA256, cipher_ECDHE_ECDSA_AES256CBC_SHA384 + , cipher_ECDHE_RSA_AES128CBC_SHA256, cipher_ECDHE_RSA_AES256CBC_SHA384 + , cipher_DHE_RSA_AES128_SHA256, cipher_DHE_RSA_AES256_SHA256 + -- Next the PFS + CBC + SHA1 ciphers + , cipher_ECDHE_ECDSA_AES128CBC_SHA, cipher_ECDHE_ECDSA_AES256CBC_SHA + , cipher_ECDHE_RSA_AES128CBC_SHA, cipher_ECDHE_RSA_AES256CBC_SHA + , cipher_DHE_RSA_AES128_SHA1, cipher_DHE_RSA_AES256_SHA1 + -- Next the non-PFS + GCM + SHA2 ciphers + , cipher_AES128GCM_SHA256, cipher_AES256GCM_SHA384 + -- Next the non-PFS + CBC + SHA2 ciphers + , cipher_AES256_SHA256, cipher_AES128_SHA256 + -- Next the non-PFS + CBC + SHA1 ciphers + , cipher_AES256_SHA1, cipher_AES128_SHA1 + -- Nobody uses or should use DSS, RC4, 3DES or MD5 + -- , cipher_DHE_DSS_AES256_SHA1, cipher_DHE_DSS_AES128_SHA1 + -- , cipher_DHE_DSS_RC4_SHA1, cipher_RC4_128_SHA1, cipher_RC4_128_MD5 + -- , cipher_RSA_3DES_EDE_CBC_SHA1 + ] --- | all encrypted ciphers supported ordered from strong to weak. --- this choice of ciphersuite should satisfy most normal need +-- | The default ciphersuites + some not recommended last resort ciphers. ciphersuite_all :: [Cipher] -ciphersuite_all = - [ cipher_ECDHE_RSA_AES128GCM_SHA256 - , cipher_ECDHE_RSA_AES256CBC_SHA - , cipher_ECDHE_ECDSA_AES128GCM_SHA256 - , cipher_DHE_RSA_AES256_SHA256, cipher_DHE_RSA_AES128_SHA256 - , cipher_DHE_RSA_AES256_SHA1, cipher_DHE_RSA_AES128_SHA1 - , cipher_DHE_DSS_AES256_SHA1, cipher_DHE_DSS_AES128_SHA1 - , cipher_AES128_SHA256, cipher_AES256_SHA256 - , cipher_AES128_SHA1, cipher_AES256_SHA1 - , cipher_DHE_DSS_RC4_SHA1, cipher_RC4_128_SHA1, cipher_RC4_128_MD5 +ciphersuite_all = ciphersuite_default ++ + [ cipher_DHE_DSS_AES256_SHA1, cipher_DHE_DSS_AES128_SHA1 , cipher_RSA_3DES_EDE_CBC_SHA1 - , cipher_DHE_RSA_AES128GCM_SHA256 + , cipher_RC4_128_SHA1 ] -- | list of medium ciphers. ciphersuite_medium :: [Cipher] -ciphersuite_medium = [cipher_RC4_128_MD5, cipher_RC4_128_SHA1, cipher_AES128_SHA1, cipher_AES256_SHA1] +ciphersuite_medium = [ cipher_RC4_128_SHA1 + , cipher_AES128_SHA1 + ] --- | the strongest ciphers supported. +-- | The strongest ciphers supported ciphers supported. For ciphers with PFS, +-- AEAD and SHA2, we list each AES128 variant right after the corresponding +-- AES256 variant. For weaker constructs, we use just the AES256 form. ciphersuite_strong :: [Cipher] ciphersuite_strong = - [ cipher_ECDHE_RSA_AES128GCM_SHA256 - , cipher_ECDHE_RSA_AES256CBC_SHA - , cipher_ECDHE_ECDSA_AES128GCM_SHA256 + [ -- If we have PFS + AEAD + SHA2, then allow AES128, else just 256 + cipher_ECDHE_ECDSA_AES256GCM_SHA384, cipher_ECDHE_ECDSA_AES128GCM_SHA256 + , cipher_ECDHE_RSA_AES256GCM_SHA384, cipher_ECDHE_RSA_AES128GCM_SHA256 + , cipher_DHE_RSA_AES256GCM_SHA384, cipher_DHE_RSA_AES128GCM_SHA256 + -- No AEAD + , cipher_ECDHE_ECDSA_AES256CBC_SHA384 + , cipher_ECDHE_RSA_AES256CBC_SHA384 , cipher_DHE_RSA_AES256_SHA256 + -- No SHA2 + , cipher_ECDHE_ECDSA_AES256CBC_SHA + , cipher_ECDHE_RSA_AES256CBC_SHA + , cipher_DHE_RSA_AES256_SHA1 + -- No PFS + , cipher_AES256GCM_SHA384 + -- Neither PFS nor AEAD, just SHA2 , cipher_AES256_SHA256 + -- Last resort no PFS, AEAD or SHA2 , cipher_AES256_SHA1 ] -- | DHE-RSA cipher suite ciphersuite_dhe_rsa :: [Cipher] -ciphersuite_dhe_rsa = [cipher_DHE_RSA_AES256_SHA256, cipher_DHE_RSA_AES128_SHA256 +ciphersuite_dhe_rsa = [ cipher_DHE_RSA_AES256GCM_SHA384, cipher_DHE_RSA_AES128GCM_SHA256 + , cipher_DHE_RSA_AES256_SHA256, cipher_DHE_RSA_AES128_SHA256 , cipher_DHE_RSA_AES256_SHA1, cipher_DHE_RSA_AES128_SHA1 - , cipher_DHE_RSA_AES128GCM_SHA256] + ] ciphersuite_dhe_dss :: [Cipher] ciphersuite_dhe_dss = [cipher_DHE_DSS_AES256_SHA1, cipher_DHE_DSS_AES128_SHA1, cipher_DHE_DSS_RC4_SHA1] diff --git a/core/Network/TLS/Parameters.hs b/core/Network/TLS/Parameters.hs index f1f0a11cb..afa23ed14 100644 --- a/core/Network/TLS/Parameters.hs +++ b/core/Network/TLS/Parameters.hs @@ -88,6 +88,8 @@ data ClientParams = ClientParams , clientWantSessionResume :: Maybe (SessionID, SessionData) , clientShared :: Shared , clientHooks :: ClientHooks + -- | In this element, you'll need to override the default empty value of + -- of 'supportedCiphers' with a suitable cipherlist. , clientSupported :: Supported , clientDebug :: DebugParams } deriving (Show) @@ -144,7 +146,9 @@ data Supported = Supported -- On the client side, the highest version will be used to establish the connection. -- On the server side, the highest version that is less or equal than the client version will be chosed. supportedVersions :: [Version] - -- | Supported cipher methods + -- | Supported cipher methods. The default is empty, specify a suitable + -- cipher list. 'Network.TLS.Extra.Cipher.ciphersuite_default' is often + -- a good choice. , supportedCiphers :: [Cipher] -- | supported compressions methods , supportedCompressions :: [Compression] From 828b0d5416171f640b16fedd735d147ad87f96c3 Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Wed, 30 Nov 2016 07:39:30 -0500 Subject: [PATCH 08/10] Enable ECDSA signature algorithms and secp384r1 Signal ECDSA signatures as part of the TLS12 signature algorithms extension. It is not uncommon to find secp384r1 as a server's sole ECDHE curve. Add it to the supported curve list. --- core/Network/TLS/Extension.hs | 2 +- core/Network/TLS/Parameters.hs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/Network/TLS/Extension.hs b/core/Network/TLS/Extension.hs index 7ec389c6b..e05ad4c86 100644 --- a/core/Network/TLS/Extension.hs +++ b/core/Network/TLS/Extension.hs @@ -270,7 +270,7 @@ data BrainPoolCurve = deriving (Show,Eq) availableEllipticCurves :: [NamedCurve] -availableEllipticCurves = [SEC SEC_p256r1, SEC SEC_p521r1] +availableEllipticCurves = [SEC SEC_p256r1, SEC SEC_p384r1, SEC SEC_p521r1] instance EnumSafe16 NamedCurve where fromEnumSafe16 NamedCurve_arbitrary_explicit_prime_curves = 0xFF01 diff --git a/core/Network/TLS/Parameters.hs b/core/Network/TLS/Parameters.hs index afa23ed14..844962a71 100644 --- a/core/Network/TLS/Parameters.hs +++ b/core/Network/TLS/Parameters.hs @@ -186,9 +186,11 @@ defaultSupported = Supported , supportedCiphers = [] , supportedCompressions = [nullCompression] , supportedHashSignatures = [ (Struct.HashSHA512, SignatureRSA) + , (Struct.HashSHA512, SignatureECDSA) , (Struct.HashSHA384, SignatureRSA) + , (Struct.HashSHA384, SignatureECDSA) , (Struct.HashSHA256, SignatureRSA) - , (Struct.HashSHA224, SignatureRSA) + , (Struct.HashSHA256, SignatureECDSA) , (Struct.HashSHA1, SignatureRSA) , (Struct.HashSHA1, SignatureDSS) ] From 0016374712b998a721a5659c7cb2fd9797ce440c Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Wed, 30 Nov 2016 17:07:52 -0500 Subject: [PATCH 09/10] Add ECDHE_RSA_SHA384 test --- core/Tests/Connection.hs | 50 +++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/core/Tests/Connection.hs b/core/Tests/Connection.hs index 4b82b26a5..ed3faab22 100644 --- a/core/Tests/Connection.hs +++ b/core/Tests/Connection.hs @@ -57,6 +57,23 @@ blockCipherDHE_DSS = blockCipher , cipherKeyExchange = CipherKeyExchange_DHE_DSS } +blockCipherECDHE_RSA :: Cipher +blockCipherECDHE_RSA = blockCipher + { cipherID = 0xff16 + , cipherName = "ecdhe-rsa-id-const" + , cipherKeyExchange = CipherKeyExchange_ECDHE_RSA + } + +blockCipherECDHE_RSA_SHA384 :: Cipher +blockCipherECDHE_RSA_SHA384 = blockCipher + { cipherID = 0xff17 + , cipherName = "ecdhe-rsa-id-const-sha384" + , cipherKeyExchange = CipherKeyExchange_ECDHE_RSA + , cipherHash = SHA384 + , cipherPRFHash = Just SHA384 + , cipherMinVer = Just TLS12 + } + streamCipher :: Cipher streamCipher = blockCipher { cipherID = 0xff13 @@ -74,7 +91,13 @@ streamCipher = blockCipher passThrough _ _ = BulkStream go where go inp = (inp, BulkStream go) knownCiphers :: [Cipher] -knownCiphers = [blockCipher,blockCipherDHE_RSA,blockCipherDHE_DSS,streamCipher] +knownCiphers = [ blockCipher + , blockCipherDHE_RSA + , blockCipherDHE_DSS + , blockCipherECDHE_RSA + , blockCipherECDHE_RSA_SHA384 + , streamCipher + ] knownVersions :: [Version] knownVersions = [SSL3,TLS10,TLS11,TLS12] @@ -87,10 +110,16 @@ arbitraryPairParams = do return (CertificateChain [cert], priv) ) [ (pubKey, privKey), (dsaPub, dsaPriv) ] connectVersion <- elements knownVersions - let allowedVersions = [ v | v <- knownVersions, v <= connectVersion ] + serverCiphers <- arbitraryCiphers `suchThat` + (\cs -> or [maybe True (<= connectVersion) (cipherMinVer x) | x <- cs]) + clientCiphers <- oneof [arbitraryCiphers] `suchThat` + (\cs -> or [x `elem` serverCiphers && + maybe True (<= connectVersion) (cipherMinVer x) | x <- cs]) + -- The shared ciphers may set a floor on the compatible protocol versions + let allowedVersions = [ v | v <- knownVersions, + or [ x `elem` serverCiphers && + maybe True (<= v) (cipherMinVer x) | x <- clientCiphers ]] serAllowedVersions <- (:[]) `fmap` elements allowedVersions - serverCiphers <- arbitraryCiphers - clientCiphers <- oneof [arbitraryCiphers] `suchThat` (\cs -> or [x `elem` serverCiphers | x <- cs]) secNeg <- arbitrary @@ -156,10 +185,15 @@ establishDataPipe params tlsServer tlsClient = do (cCtx, sCtx) <- newPairContext pipe params - _ <- forkIO $ E.catch (tlsServer sCtx resultQueue) (printAndRaise "server") - _ <- forkIO $ E.catch (tlsClient startQueue cCtx) (printAndRaise "client") + _ <- forkIO $ E.catch (tlsServer sCtx resultQueue) + (printAndRaise "server" (serverSupported $ snd params)) + _ <- forkIO $ E.catch (tlsClient startQueue cCtx) + (printAndRaise "client" (clientSupported $ fst params)) return (startQueue, resultQueue) where - printAndRaise :: String -> E.SomeException -> IO () - printAndRaise s e = putStrLn (s ++ " exception: " ++ show e) >> E.throw e + printAndRaise :: String -> Supported -> E.SomeException -> IO () + printAndRaise s supported e = do + putStrLn $ s ++ " exception: " ++ show e ++ + ", supported: " ++ show supported + E.throw e From 5feecd312eac305942f6255d16491e758f608280 Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Wed, 30 Nov 2016 19:02:54 -0500 Subject: [PATCH 10/10] Handle no shared ciphers more gracefully --- core/Network/TLS/Handshake/Server.hs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/core/Network/TLS/Handshake/Server.hs b/core/Network/TLS/Handshake/Server.hs index 90b247e9a..c28a060f4 100644 --- a/core/Network/TLS/Handshake/Server.hs +++ b/core/Network/TLS/Handshake/Server.hs @@ -117,13 +117,16 @@ handshakeServerWith sparams ctx clientHello@(ClientHello clientVersion _ clientS extraCreds <- (onServerNameIndication $ serverHooks sparams) serverName + -- The shared cipherlist can become empty after filtering for compatible + -- creds, check now before calling onCipherChoosing, which does not handle + -- empty lists. let ciphersFilteredVersion = filter (cipherAllowedForVersion chosenVersion) (commonCiphers extraCreds) - usedCipher = (onCipherChoosing $ serverHooks sparams) chosenVersion ciphersFilteredVersion - creds = extraCreds `mappend` (sharedCredentials $ ctxShared ctx) - - when (commonCipherIDs extraCreds == []) $ throwCore $ + when (null ciphersFilteredVersion) $ throwCore $ Error_Protocol ("no cipher in common with the client", True, HandshakeFailure) + let usedCipher = (onCipherChoosing $ serverHooks sparams) chosenVersion ciphersFilteredVersion + creds = extraCreds `mappend` (sharedCredentials $ ctxShared ctx) + cred <- case cipherKeyExchange usedCipher of CipherKeyExchange_RSA -> return $ credentialsFindForDecrypting creds CipherKeyExchange_DH_Anon -> return $ Nothing