bcgit / bc-java

Bouncy Castle Java Distribution (Mirror)
https://www.bouncycastle.org/java.html
MIT License
2.29k stars 1.13k forks source link

Export of key material from TlsContext post handshake fails in DTLS with version 1.62 #578

Open mondain opened 5 years ago

mondain commented 5 years ago

I believe there may be a bug in the handling of DTLS 1.2 connections in regard to exporting key material after handshake is complete; My method shown here byte[] keyingMaterial = tlsContext.exportKeyingMaterial(ExporterLabel.dtls_srtp, null, 2 * (cipher_key_length + cipher_salt_length)); works until I upgrade to BC 1.62 and then it fails seemingly requiring an extended master secret? Shown below are my security parameters and then the exception thrown:

2019-08-14 13:44:36,577 DtlsPacketTransformer - org.bouncycastle.tls.SecurityParameters@2ecfcb38[
entity=0,
renegotiating=false,
secureRenegotiation=true,
cipherSuite=49199,
compressionAlgorithm=0,
maxFragmentLength=-1,
prfAlgorithm=1,
verifyDataLength=12,
masterSecret=org.bouncycastle.tls.crypto.impl.bc.BcTlsSecret@6bcd4085,
clientRandom={-50,-54,-90,-99,-74,15,22,98,-99,-25,59,-74,0,-43,53,-101,33,83,-13,-102,-55,-4,69,125,-95,2,-93,-29,86,80,-72,93},serverRandom={37,-31,106,89,35,71,-41,-84,11,22,-82,73,40,23,62,-93,82,47,118,-3,-77,22,33,77,-24,6,-17,-75,-6,28,-58,-112},sessionHash={-66,-103,83,49,112,-56,14,-38,-118,5,-22,-97,6,5,-49,-127,-6,-68,121,120,-50,-47,-49,-40,-45,35,-117,-126,18,84,14,120},
sessionID={},
pskIdentity=<null>,
srpIdentity=<null>,
tlsServerEndPoint={2,111,-61,-56,113,-66,-107,21,-82,98,-16,69,-55,-11,88,-28,-74,69,-60,44,111,76,14,120,125,-116,20,-54,93,-124,65,-107},
tlsUnique={72,45,22,-16,-10,-120,-43,-47,-128,-12,-122,42},
encryptThenMAC=false,
extendedMasterSecret=false,
extendedPadding=false,
truncatedHMac=false,
applicationProtocol=<null>,
applicationProtocolSet=true,
clientServerNames=<null>,
clientSigAlgs=[{sha256,ecdsa}, {sha384,ecdsa}, {sha512,ecdsa}, {sha1,ecdsa}, {Intrinsic,rsa_pss_rsae_sha256}, {Intrinsic,rsa_pss_rsae_sha384}, {Intrinsic,rsa_pss_rsae_sha512}, {sha256,rsa}, {sha384,rsa}, {sha512,rsa}, {sha1,rsa}, {sha256,dsa}, {sha384,dsa}, {sha512,dsa}, {sha1,dsa}],
clientSigAlgsCert=[{sha256,ecdsa}, {sha384,ecdsa}, {sha512,ecdsa}, {sha1,ecdsa}, {Intrinsic,rsa_pss_rsae_sha256}, {Intrinsic,rsa_pss_rsae_sha384}, {Intrinsic,rsa_pss_rsae_sha512}, {sha256,rsa}, {sha384,rsa}, {sha512,rsa}, {sha1,rsa}, {sha256,dsa}, {sha384,dsa}, {sha512,dsa}, {sha1,dsa}],
clientSupportedGroups={29,23,24},
keyExchangeAlgorithm=19,
localCertificate=org.bouncycastle.tls.Certificate@112ea181,
peerCertificate=org.bouncycastle.tls.Certificate@fac4081,
negotiatedVersion=DTLS 1.2,
localVerifyData={35,-11,72,16,67,6,-96,-23,8,-54,125,29},
peerVerifyData={72,45,22,-16,-10,-120,-43,-47,-128,-12,-122,42}]

Exception thrown

2019-08-14 13:44:36,583 DtlsPacketTransformer - Connect exception
org.bouncycastle.tls.TlsFatalAlert: internal_error(80)
    at org.bouncycastle.tls.DTLSServerProtocol.accept(Unknown Source)
    at org.bouncycastle.tls.DTLSServerProtocol.accept(Unknown Source)
    at com.red5pro.io.rtp.transform.dtls.DtlsPacketTransformer.lambda$start$0(DtlsPacketTransformer.java:413)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalStateException: cannot export keying material without extended_master_secret
    at org.bouncycastle.tls.AbstractTlsContext.exportKeyingMaterial(Unknown Source)
    at com.red5pro.io.rtp.transform.dtls.DtlsPacketTransformer.initializeSRTPTransformer(DtlsPacketTransformer.java:252)
    at com.red5pro.io.rtp.transform.dtls.TlsServerImpl.notifyHandshakeComplete(TlsServerImpl.java:297)
    at org.bouncycastle.tls.AbstractTlsContext.handshakeComplete(Unknown Source)
    at org.bouncycastle.tls.DTLSServerProtocol.serverHandshake(Unknown Source)
    ... 8 common frames omitted

Clearly my security params has extended master secret disabled: extendedMasterSecret=false. Anyone know a solution? or is this a failure on my part to adjust to API changes?

peterdettman commented 5 years ago

This is not an API change, but an RFC change as a result of security issues rooted in the "triple handshake attack". Specifically, RFC 7627 5.4:

If a client or server chooses to continue with a full handshake without the extended master secret extension, then the new session becomes vulnerable to the man-in-the-middle key synchronization attack described in Section 1. Hence, the client or server MUST NOT export any key material based on the new master secret for any subsequent application-level authentication. In particular, it MUST disable [RFC5705] and any Extensible Authentication Protocol (EAP) relying on compound authentication [COMPOUND-AUTH].

Disabling RFC 5705 means disabling TlsContext.exportKeyingMaterial. RFC 5764 4.2 is explicit that SRTP keys are generated using the RFC 5705 mechanism, and so fall into this basket.

(Although you're probably focused on how to make this actually work, note that it is possible to abort the doomed handshake much earlier by overriding TlsPeer.requiresExtendedMasterSecret to return true.)

It may be possible to provide a workaround, but there are definite security implications for ignoring this restriction. What software is running the other end that doesn't support extended master secret (RFC 7627)?

mondain commented 5 years ago

I was testing with Chrome 76 and Firefox 68.0.1; in this case, it was Firefox which failed. Yes, I've got to make this work or drop-back to an older BC version. Thank you for explaining this change @peterdettman

mondain commented 5 years ago

Testing with Firefox 68.0.1 again this morning after this change on my server TlsExtensionsUtils.addExtendedMasterSecretExtension(extensions); and I get this:

DtlsPacketTransformer - Connect exception
org.bouncycastle.tls.TlsFatalAlert: unsupported_extension(110)
    at org.bouncycastle.tls.DTLSRecordLayer.processRecord(Unknown Source)
    at org.bouncycastle.tls.DTLSRecordLayer.receive(Unknown Source)
    at org.bouncycastle.tls.DTLSReliableHandshake.receiveMessage(Unknown Source)
    at org.bouncycastle.tls.DTLSServerProtocol.serverHandshake(Unknown Source)
    at org.bouncycastle.tls.DTLSServerProtocol.accept(Unknown Source)
    at org.bouncycastle.tls.DTLSServerProtocol.accept(Unknown Source)

I assume this means that the version of FF doesn't support the extended master secret.

mondain commented 5 years ago

Firefox bugzilla says they Resoved Fixed on https://bugzilla.mozilla.org/show_bug.cgi?id=1224875 but it would seem that its been backed out or disabled somewhere, so I posted a new issue with them https://bugzilla.mozilla.org/show_bug.cgi?id=1574188

peterdettman commented 5 years ago

Testing with Firefox 68.0.1 again this morning after this change on my server TlsExtensionsUtils.addExtendedMasterSecretExtension(extensions); and I get this:

Generally (and in this specific case) a TLS extension can't be initiated by the server, so the error in this case may simply be the Firefox client saying "I never asked for this".

It's also unnecessary to add that call in any case, as extended_master_secret negotiation is automatically handled by the protocol classes; BC clients will always offer extended_master_secret and BC servers will always accept it if offered.

The only 2 options are that either Firefox include extended_master_secret extension in their ClientHello, or that your server ignore its absence and (by some workaround) proceed to export keying material anyway.