From de1ea087feea4ac97a647a12e1aa149a88cd3d99 Mon Sep 17 00:00:00 2001 From: Jonathan Lennox Date: Sat, 2 Nov 2024 16:24:49 +0000 Subject: [PATCH 1/2] Add support for DTLS raw keys. --- .../org/jitsi/nlj/dtls/CertificateInfo.kt | 2 + .../kotlin/org/jitsi/nlj/dtls/DtlsConfig.kt | 4 ++ .../kotlin/org/jitsi/nlj/dtls/DtlsStack.kt | 10 +++- .../kotlin/org/jitsi/nlj/dtls/DtlsUtils.kt | 51 ++++++++++++++++--- .../org/jitsi/nlj/dtls/TlsClientImpl.kt | 27 +++++++++- .../org/jitsi/nlj/dtls/TlsServerImpl.kt | 29 ++++++++++- .../src/main/resources/reference.conf | 3 ++ .../kotlin/org/jitsi/nlj/dtls/DtlsTest.kt | 8 ++- .../transport/dtls/DtlsTransport.kt | 11 ++++ pom.xml | 2 +- 10 files changed, 136 insertions(+), 11 deletions(-) diff --git a/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/CertificateInfo.kt b/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/CertificateInfo.kt index a15b179a82..3a3ce7af26 100644 --- a/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/CertificateInfo.kt +++ b/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/CertificateInfo.kt @@ -28,7 +28,9 @@ import java.security.KeyPair data class CertificateInfo( val keyPair: KeyPair, val certificate: org.bouncycastle.tls.Certificate, + val rawKeyCertificate: org.bouncycastle.tls.Certificate, val localFingerprintHashFunction: String, val localFingerprint: String, + val localRawKeyFingerprint: String, val creationTimestampMs: Long ) diff --git a/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/DtlsConfig.kt b/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/DtlsConfig.kt index 6764aa94f8..fdccf1c9f4 100644 --- a/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/DtlsConfig.kt +++ b/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/DtlsConfig.kt @@ -36,6 +36,10 @@ class DtlsConfig { } } + val negotiateRawKeyFingerprints: Boolean by config { + "jmt.dtls.negotiate-raw-key-fingerprints".from(JitsiConfig.newConfig) + } + companion object { val config = DtlsConfig() } diff --git a/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/DtlsStack.kt b/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/DtlsStack.kt index 4d7c32ba5c..7adb656cb9 100644 --- a/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/DtlsStack.kt +++ b/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/DtlsStack.kt @@ -68,11 +68,19 @@ class DtlsStack( val localFingerprint: String get() = certificateInfo.localFingerprint + val localRawKeyFingerprint: String + get() = certificateInfo.localRawKeyFingerprint + /** * The remote fingerprints sent to us over the signaling path. */ var remoteFingerprints: Map = HashMap() + /** + * The remote raw key fingerprints. + */ + var remoteRawKeyFingerprints: Map = HashMap() + /** * A handler which will be invoked when DTLS application data is received */ @@ -174,7 +182,7 @@ class DtlsStack( */ private fun verifyAndValidateRemoteCertificate(remoteCertificate: Certificate?) { remoteCertificate?.let { - DtlsUtils.verifyAndValidateCertificate(it, remoteFingerprints) + DtlsUtils.verifyAndValidateCertificate(it, remoteFingerprints, remoteRawKeyFingerprints) // The above throws an exception if the checks fail. logger.cdebug { "Fingerprints verified." } } ?: run { diff --git a/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/DtlsUtils.kt b/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/DtlsUtils.kt index c1259c3ba1..ad2fb041ad 100644 --- a/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/DtlsUtils.kt +++ b/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/DtlsUtils.kt @@ -16,10 +16,12 @@ package org.jitsi.nlj.dtls import org.bouncycastle.asn1.ASN1Encoding +import org.bouncycastle.asn1.ASN1Object import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500NameBuilder import org.bouncycastle.asn1.x500.style.BCStyle import org.bouncycastle.asn1.x509.Certificate +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder import org.bouncycastle.jce.ECNamedCurveTable import org.bouncycastle.jce.provider.BouncyCastleProvider @@ -27,11 +29,14 @@ import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder import org.bouncycastle.operator.bc.BcDefaultDigestProvider import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder import org.bouncycastle.tls.AlertDescription +import org.bouncycastle.tls.CertificateEntry +import org.bouncycastle.tls.CertificateType import org.bouncycastle.tls.TlsContext import org.bouncycastle.tls.TlsUtils import org.bouncycastle.tls.crypto.TlsSecret import org.bouncycastle.tls.crypto.impl.bc.BcTlsCertificate import org.bouncycastle.tls.crypto.impl.bc.BcTlsCrypto +import org.bouncycastle.tls.crypto.impl.bc.BcTlsRawKeyCertificate import org.jitsi.utils.logging2.Logger import org.jitsi.utils.logging2.cdebug import org.jitsi.utils.logging2.cerror @@ -68,14 +73,24 @@ class DtlsUtils { val localFingerprintHashFunction = x509certificate.getHashFunction() val localFingerprint = x509certificate.getFingerprint(localFingerprintHashFunction) + val sPKI = x509certificate.subjectPublicKeyInfo + val rawKeyFingerprint = sPKI.getFingerprint(localFingerprintHashFunction) + val certificate = org.bouncycastle.tls.Certificate( arrayOf(BcTlsCertificate(BC_TLS_CRYPTO, x509certificate)) ) + val rawKeyCertificate = org.bouncycastle.tls.Certificate( + CertificateType.RawPublicKey, + null, + arrayOf(CertificateEntry(BcTlsRawKeyCertificate(BC_TLS_CRYPTO, sPKI), null)) + ) return CertificateInfo( keyPair, certificate, + rawKeyCertificate, localFingerprintHashFunction, localFingerprint, + rawKeyFingerprint, System.currentTimeMillis() ) } @@ -158,14 +173,26 @@ class DtlsUtils { */ fun verifyAndValidateCertificate( certificateInfo: org.bouncycastle.tls.Certificate, - remoteFingerprints: Map + remoteFingerprints: Map, + remoteRawKeyFingerprints: Map ) { if (certificateInfo.certificateList.isEmpty()) { throw DtlsException("No remote fingerprints.") } + val type = certificateInfo.certificateType for (currCertificate in certificateInfo.certificateList) { - val x509Cert = Certificate.getInstance(currCertificate.encoded) - verifyAndValidateCertificate(x509Cert, remoteFingerprints) + when (type) { + CertificateType.X509 -> { + val x509Cert = Certificate.getInstance(currCertificate.encoded) + verifyAndValidateCertificate(x509Cert, remoteFingerprints) + } + CertificateType.RawPublicKey -> { + val sPKI = SubjectPublicKeyInfo.getInstance(currCertificate.encoded) + verifyAndValidateRawPublicKey(sPKI, remoteRawKeyFingerprints) + } + else -> + throw DtlsException("Invalid certificate type") + } } } @@ -229,6 +256,18 @@ class DtlsUtils { } } + private fun verifyAndValidateRawPublicKey( + sPKI: SubjectPublicKeyInfo, + remoteRawKeyFingerprints: Map + ) { + if (!remoteRawKeyFingerprints.any { (hash, fingerprint) -> + sPKI.getFingerprint(hash) == fingerprint + } + ) { + throw DtlsException("No remote raw key fingerprint matches SubjectPublicKeyInfo") + } + } + /** * Determine and return the hash function (as a [String]) used by this certificate */ @@ -242,10 +281,10 @@ class DtlsUtils { } /** - * Computes the fingerprint of a [org.bouncycastle.asn1.x509.Certificate] using [hashFunction] and returns it - * as a [String] + * Computes the fingerprint of a [ASN1Object] (e.g. a [org.bouncycastle.asn1.x509.Certificate]) + * using [hashFunction] and returns it as a [String] */ - private fun Certificate.getFingerprint(hashFunction: String): String { + private fun ASN1Object.getFingerprint(hashFunction: String): String { val digAlgId = DefaultDigestAlgorithmIdentifierFinder().find(hashFunction.uppercase()) val digest = BcDefaultDigestProvider.INSTANCE.get(digAlgId) val input: ByteArray = getEncoded(ASN1Encoding.DER) diff --git a/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/TlsClientImpl.kt b/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/TlsClientImpl.kt index 3800b3dffe..fa40c4bf70 100644 --- a/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/TlsClientImpl.kt +++ b/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/TlsClientImpl.kt @@ -17,8 +17,10 @@ package org.jitsi.nlj.dtls import edu.umd.cs.findbugs.annotations.SuppressFBWarnings import org.bouncycastle.crypto.util.PrivateKeyFactory +import org.bouncycastle.tls.AlertDescription import org.bouncycastle.tls.Certificate import org.bouncycastle.tls.CertificateRequest +import org.bouncycastle.tls.CertificateType import org.bouncycastle.tls.DefaultTlsClient import org.bouncycastle.tls.ExporterLabel import org.bouncycastle.tls.ExtensionType @@ -28,6 +30,7 @@ import org.bouncycastle.tls.SignatureAlgorithm import org.bouncycastle.tls.SignatureAndHashAlgorithm import org.bouncycastle.tls.TlsAuthentication import org.bouncycastle.tls.TlsCredentials +import org.bouncycastle.tls.TlsFatalAlert import org.bouncycastle.tls.TlsSRTPUtils import org.bouncycastle.tls.TlsServerCertificate import org.bouncycastle.tls.TlsSession @@ -81,12 +84,20 @@ class TlsClientImpl( return object : TlsAuthentication { override fun getClientCredentials(certificateRequest: CertificateRequest): TlsCredentials { // NOTE: can't set clientCredentials when it is declared because 'context' won't be set yet + val cert = when (context.securityParametersHandshake.clientCertificateType) { + CertificateType.RawPublicKey -> + certificateInfo.rawKeyCertificate + CertificateType.X509 -> + certificateInfo.certificate + else -> + throw TlsFatalAlert(AlertDescription.internal_error) + } if (clientCredentials == null) { clientCredentials = BcDefaultTlsCredentialedSigner( TlsCryptoParameters(context), (context.crypto as BcTlsCrypto), PrivateKeyFactory.createKey(certificateInfo.keyPair.private.encoded), - certificateInfo.certificate, + cert, if (TlsUtils.isSignatureAlgorithmsExtensionAllowed(context.serverVersion)) { SignatureAndHashAlgorithm( HashAlgorithm.sha256, @@ -123,6 +134,20 @@ class TlsClientImpl( return clientExtensions } + override fun getAllowedClientCertificateTypes(): ShortArray? { + if (DtlsConfig.config.negotiateRawKeyFingerprints) { + return shortArrayOf(CertificateType.X509, CertificateType.RawPublicKey) + } + return null + } + + override fun getAllowedServerCertificateTypes(): ShortArray? { + if (DtlsConfig.config.negotiateRawKeyFingerprints) { + return shortArrayOf(CertificateType.X509, CertificateType.RawPublicKey) + } + return null + } + override fun processServerExtensions(serverExtensions: Hashtable<*, *>?) { // TODO: a few cases we should be throwing alerts for in here. see old TlsClientImpl val useSRTPData = TlsSRTPUtils.getUseSRTPExtension(serverExtensions) diff --git a/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/TlsServerImpl.kt b/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/TlsServerImpl.kt index f7d1dfd29c..5273fe266f 100644 --- a/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/TlsServerImpl.kt +++ b/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/TlsServerImpl.kt @@ -20,6 +20,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings import org.bouncycastle.crypto.util.PrivateKeyFactory import org.bouncycastle.tls.Certificate import org.bouncycastle.tls.CertificateRequest +import org.bouncycastle.tls.CertificateType import org.bouncycastle.tls.ClientCertificateType import org.bouncycastle.tls.DefaultTlsServer import org.bouncycastle.tls.ExporterLabel @@ -29,6 +30,7 @@ import org.bouncycastle.tls.SignatureAlgorithm import org.bouncycastle.tls.SignatureAndHashAlgorithm import org.bouncycastle.tls.TlsCredentialedDecryptor import org.bouncycastle.tls.TlsCredentialedSigner +import org.bouncycastle.tls.TlsExtensionsUtils import org.bouncycastle.tls.TlsSRTPUtils import org.bouncycastle.tls.TlsSession import org.bouncycastle.tls.TlsUtils @@ -64,6 +66,8 @@ class TlsServerImpl( private var session: TlsSession? = null + private var useRawKeys: Boolean = false + /** * Only set after a handshake has completed */ @@ -96,6 +100,17 @@ class TlsServerImpl( val protectionProfiles = useSRTPData.protectionProfiles chosenSrtpProtectionProfile = DtlsUtils.chooseSrtpProtectionProfile(SrtpConfig.protectionProfiles, protectionProfiles.asIterable()) + + if (DtlsConfig.config.negotiateRawKeyFingerprints) { + val remoteServerCertTypes = TlsExtensionsUtils.getServerCertificateTypeExtensionClient(clientExtensions) + val remoteClientCertTypes = TlsExtensionsUtils.getClientCertificateTypeExtensionClient(clientExtensions) + + if (remoteServerCertTypes?.contains(CertificateType.RawPublicKey) == true && + remoteClientCertTypes?.contains(CertificateType.RawPublicKey) == true + ) { + useRawKeys = true + } + } } override fun getCipherSuites() = DtlsConfig.config.cipherSuites.toIntArray() @@ -109,15 +124,27 @@ class TlsServerImpl( } override fun getECDSASignerCredentials(): TlsCredentialedSigner { + val cert = if (useRawKeys) { + certificateInfo.rawKeyCertificate + } else { + certificateInfo.certificate + } return BcDefaultTlsCredentialedSigner( TlsCryptoParameters(context), (context.crypto as BcTlsCrypto), PrivateKeyFactory.createKey(certificateInfo.keyPair.private.encoded), - certificateInfo.certificate, + cert, SignatureAndHashAlgorithm(HashAlgorithm.sha256, SignatureAlgorithm.ecdsa) ) } + override fun getAllowedClientCertificateTypes(): ShortArray? { + if (useRawKeys) { + return shortArrayOf(CertificateType.RawPublicKey) + } + return null + } + override fun getCertificateRequest(): CertificateRequest { val signatureAlgorithms = Vector(1) signatureAlgorithms.add(SignatureAndHashAlgorithm(HashAlgorithm.sha256, SignatureAlgorithm.ecdsa)) diff --git a/jitsi-media-transform/src/main/resources/reference.conf b/jitsi-media-transform/src/main/resources/reference.conf index 52ee7c5e21..a005330e1f 100644 --- a/jitsi-media-transform/src/main/resources/reference.conf +++ b/jitsi-media-transform/src/main/resources/reference.conf @@ -43,6 +43,9 @@ jmt { // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, // TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ] + + // Whether to send and recognize fingerprints for raw keys + negotiate-raw-key-fingerprints = false } srtp { // The maximum number of packets that can be discarded early (without going through the SRTP stack for diff --git a/jitsi-media-transform/src/test/kotlin/org/jitsi/nlj/dtls/DtlsTest.kt b/jitsi-media-transform/src/test/kotlin/org/jitsi/nlj/dtls/DtlsTest.kt index 2b0d9db85e..804f5830f5 100644 --- a/jitsi-media-transform/src/test/kotlin/org/jitsi/nlj/dtls/DtlsTest.kt +++ b/jitsi-media-transform/src/test/kotlin/org/jitsi/nlj/dtls/DtlsTest.kt @@ -33,7 +33,7 @@ import kotlin.concurrent.thread class DtlsTest : ShouldSpec() { override fun isolationMode(): IsolationMode? = IsolationMode.InstancePerLeaf private val debugEnabled = true - private val pcapEnabled = false + private val pcapEnabled = true private val logger = StdoutLogger(_level = Level.OFF) fun debug(s: String) { @@ -50,9 +50,15 @@ class DtlsTest : ShouldSpec() { dtlsClient.remoteFingerprints = mapOf( dtlsServer.localFingerprintHashFunction to dtlsServer.localFingerprint ) + dtlsClient.remoteRawKeyFingerprints = mapOf( + dtlsServer.localFingerprintHashFunction to dtlsServer.localRawKeyFingerprint + ) dtlsServer.remoteFingerprints = mapOf( dtlsClient.localFingerprintHashFunction to dtlsClient.localFingerprint ) + dtlsServer.remoteRawKeyFingerprints = mapOf( + dtlsClient.localFingerprintHashFunction to dtlsClient.localRawKeyFingerprint + ) // The DTLS server's send is wired directly to the DTLS client's receive dtlsServer.outgoingDataHandler = object : DtlsStack.OutgoingDataHandler { diff --git a/jvb/src/main/kotlin/org/jitsi/videobridge/transport/dtls/DtlsTransport.kt b/jvb/src/main/kotlin/org/jitsi/videobridge/transport/dtls/DtlsTransport.kt index e6db0a8293..6413437b83 100644 --- a/jvb/src/main/kotlin/org/jitsi/videobridge/transport/dtls/DtlsTransport.kt +++ b/jvb/src/main/kotlin/org/jitsi/videobridge/transport/dtls/DtlsTransport.kt @@ -18,6 +18,7 @@ package org.jitsi.videobridge.transport.dtls import org.ice4j.util.Buffer import org.jitsi.nlj.dtls.DtlsClient +import org.jitsi.nlj.dtls.DtlsConfig import org.jitsi.nlj.dtls.DtlsServer import org.jitsi.nlj.dtls.DtlsStack import org.jitsi.nlj.srtp.TlsRole @@ -28,6 +29,7 @@ import org.jitsi.utils.logging2.createChildLogger import org.jitsi.utils.queue.PacketQueue import org.jitsi.videobridge.util.TaskPools import org.jitsi.xmpp.extensions.jingle.DtlsFingerprintPacketExtension +import org.jitsi.xmpp.extensions.jingle.DtlsRawKeyFingerprintPacketExtension import org.jitsi.xmpp.extensions.jingle.IceUdpTransportPacketExtension import java.util.concurrent.atomic.AtomicBoolean @@ -192,6 +194,15 @@ class DtlsTransport(parentLogger: Logger, id: String) { if (cryptex) { fingerprintPE.cryptex = true } + if (DtlsConfig.config.negotiateRawKeyFingerprints) { + val rawKeyFingerprintPE = iceUdpTransportPe.getFirstChildOfType( + DtlsRawKeyFingerprintPacketExtension::class.java + ) ?: run { + DtlsRawKeyFingerprintPacketExtension().also { iceUdpTransportPe.addChildExtension(it) } + } + rawKeyFingerprintPE.fingerprint = dtlsStack.localRawKeyFingerprint + rawKeyFingerprintPE.hash = dtlsStack.localFingerprintHashFunction + } } fun enqueueBuffer(buffer: Buffer) = dtlsQueue.add(buffer) diff --git a/pom.xml b/pom.xml index 96df738c07..09cc434b5a 100644 --- a/pom.xml +++ b/pom.xml @@ -111,7 +111,7 @@ ${project.groupId} jitsi-xmpp-extensions - 1.0-81-g3816e5a + 1.0-SNAPSHOT From 516aaee6a271555b4865fb6f8d6843ce7e4c07f9 Mon Sep 17 00:00:00 2001 From: Jonathan Lennox Date: Sun, 3 Nov 2024 13:23:26 +0000 Subject: [PATCH 2/2] Actually set raw key fingerprints. Add some debugging info. --- .../main/kotlin/org/jitsi/nlj/dtls/DtlsUtils.kt | 14 ++++++++++++-- .../main/kotlin/org/jitsi/videobridge/Endpoint.kt | 14 ++++++++++++++ .../kotlin/org/jitsi/videobridge/relay/Relay.kt | 14 ++++++++++++++ .../videobridge/transport/dtls/DtlsTransport.kt | 7 +++++++ 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/DtlsUtils.kt b/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/DtlsUtils.kt index ad2fb041ad..b6af6042d7 100644 --- a/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/DtlsUtils.kt +++ b/jitsi-media-transform/src/main/kotlin/org/jitsi/nlj/dtls/DtlsUtils.kt @@ -261,10 +261,19 @@ class DtlsUtils { remoteRawKeyFingerprints: Map ) { if (!remoteRawKeyFingerprints.any { (hash, fingerprint) -> - sPKI.getFingerprint(hash) == fingerprint + try { + sPKI.getFingerprint(hash) == fingerprint + } catch (e: Exception) { + // Swallow exception for unknown hash functions + false + } } ) { - throw DtlsException("No remote raw key fingerprint matches SubjectPublicKeyInfo") + val expected = sPKI.getFingerprint("sha-256") + throw DtlsException( + "No remote raw key fingerprint matches SubjectPublicKeyInfo. " + + "Expected: sha-256 $expected. Seen: ${remoteRawKeyFingerprints.entries.joinToString()}" + ) } } @@ -286,6 +295,7 @@ class DtlsUtils { */ private fun ASN1Object.getFingerprint(hashFunction: String): String { val digAlgId = DefaultDigestAlgorithmIdentifierFinder().find(hashFunction.uppercase()) + ?: throw DtlsException("digest algorithm $hashFunction unknown") val digest = BcDefaultDigestProvider.INSTANCE.get(digAlgId) val input: ByteArray = getEncoded(ASN1Encoding.DER) val output = ByteArray(digest.digestSize) diff --git a/jvb/src/main/kotlin/org/jitsi/videobridge/Endpoint.kt b/jvb/src/main/kotlin/org/jitsi/videobridge/Endpoint.kt index 1b5ce37861..3eaecca24a 100644 --- a/jvb/src/main/kotlin/org/jitsi/videobridge/Endpoint.kt +++ b/jvb/src/main/kotlin/org/jitsi/videobridge/Endpoint.kt @@ -89,6 +89,7 @@ import org.jitsi.videobridge.util.looksLikeDtls import org.jitsi.videobridge.websocket.colibriWebSocketServiceSupplier import org.jitsi.xmpp.extensions.colibri.WebSocketPacketExtension import org.jitsi.xmpp.extensions.jingle.DtlsFingerprintPacketExtension +import org.jitsi.xmpp.extensions.jingle.DtlsRawKeyFingerprintPacketExtension import org.jitsi.xmpp.extensions.jingle.IceUdpTransportPacketExtension import org.jitsi.xmpp.util.XmlStringBuilderUtil.Companion.toStringOpt import org.jitsi_modified.sctp4j.SctpDataCallback @@ -803,6 +804,19 @@ class Endpoint @JvmOverloads constructor( val setup = fingerprintExtensions.first().setup dtlsTransport.setSetupAttribute(setup) } + + val remoteRawKeyFingerprints = mutableMapOf() + val rawKeyFingerprintExtensions = + transportInfo.getChildExtensionsOfType(DtlsRawKeyFingerprintPacketExtension::class.java) + rawKeyFingerprintExtensions.forEach { rawKeyFingerprintExtension -> + if (rawKeyFingerprintExtension.hash != null && rawKeyFingerprintExtension.fingerprint != null) { + remoteRawKeyFingerprints[rawKeyFingerprintExtension.hash] = rawKeyFingerprintExtension.fingerprint + } else { + logger.info("Ignoring empty DtlsRawKeyFingerprint extension: ${transportInfo.toStringOpt()}") + } + } + dtlsTransport.setRemoteRawKeyFingerprints(remoteRawKeyFingerprints) + iceTransport.startConnectivityEstablishment(transportInfo) } diff --git a/jvb/src/main/kotlin/org/jitsi/videobridge/relay/Relay.kt b/jvb/src/main/kotlin/org/jitsi/videobridge/relay/Relay.kt index cbb6e70e1c..2a977bffb2 100644 --- a/jvb/src/main/kotlin/org/jitsi/videobridge/relay/Relay.kt +++ b/jvb/src/main/kotlin/org/jitsi/videobridge/relay/Relay.kt @@ -102,6 +102,7 @@ import org.jitsi.videobridge.websocket.colibriWebSocketServiceSupplier import org.jitsi.xmpp.extensions.colibri.WebSocketPacketExtension import org.jitsi.xmpp.extensions.colibri2.Sctp import org.jitsi.xmpp.extensions.jingle.DtlsFingerprintPacketExtension +import org.jitsi.xmpp.extensions.jingle.DtlsRawKeyFingerprintPacketExtension import org.jitsi.xmpp.extensions.jingle.IceUdpTransportPacketExtension import org.jitsi.xmpp.util.XmlStringBuilderUtil.Companion.toStringOpt import org.jitsi_modified.sctp4j.SctpClientSocket @@ -630,6 +631,19 @@ class Relay @JvmOverloads constructor( val setup = fingerprintExtensions.first().setup dtlsTransport.setSetupAttribute(setup) } + + val remoteRawKeyFingerprints = mutableMapOf() + val rawKeyFingerprintExtensions = + transportInfo.getChildExtensionsOfType(DtlsRawKeyFingerprintPacketExtension::class.java) + rawKeyFingerprintExtensions.forEach { rawKeyFingerprintExtension -> + if (rawKeyFingerprintExtension.hash != null && rawKeyFingerprintExtension.fingerprint != null) { + remoteRawKeyFingerprints[rawKeyFingerprintExtension.hash] = rawKeyFingerprintExtension.fingerprint + } else { + logger.info("Ignoring empty DtlsRawKeyFingerprint extension: ${transportInfo.toStringOpt()}") + } + } + dtlsTransport.setRemoteRawKeyFingerprints(remoteRawKeyFingerprints) + iceTransport.startConnectivityEstablishment(transportInfo) val websocketExtension = transportInfo.getFirstChildOfType(WebSocketPacketExtension::class.java) diff --git a/jvb/src/main/kotlin/org/jitsi/videobridge/transport/dtls/DtlsTransport.kt b/jvb/src/main/kotlin/org/jitsi/videobridge/transport/dtls/DtlsTransport.kt index 6413437b83..153fe02566 100644 --- a/jvb/src/main/kotlin/org/jitsi/videobridge/transport/dtls/DtlsTransport.kt +++ b/jvb/src/main/kotlin/org/jitsi/videobridge/transport/dtls/DtlsTransport.kt @@ -175,6 +175,13 @@ class DtlsTransport(parentLogger: Logger, id: String) { dtlsStack.remoteFingerprints = remoteFingerprints } + fun setRemoteRawKeyFingerprints(remoteRawKeyFingerprints: Map) { + if (remoteRawKeyFingerprints.isEmpty()) { + return + } + dtlsStack.remoteRawKeyFingerprints = remoteRawKeyFingerprints + } + /** * Describe the properties of this [DtlsTransport] into the given * [IceUdpTransportPacketExtension]