From 3ece8a4aebd750a6d2766b088d77ee2ebf7312b8 Mon Sep 17 00:00:00 2001 From: Mark Rotteveel Date: Wed, 28 Feb 2024 13:07:18 +0100 Subject: [PATCH] #784 Don't try plugins if they will not work on current protocol --- .../chacha64/ChaCha64EncryptionPlugin.java | 2 +- .../chacha64/ChaCha64EncryptionPluginSpi.java | 9 ++- .../ChaCha64EncryptionPluginSpiTest.java | 62 +++++++++++++++++++ .../ng/wire/crypt/CryptConnectionInfo.java | 42 +++++++++++++ .../ng/wire/crypt/EncryptionPluginSpi.java | 19 ++++++ .../crypt/arc4/Arc4EncryptionPluginSpi.java | 8 +++ .../chacha/ChaChaEncryptionPluginSpi.java | 8 +++ .../ng/wire/version13/V13WireOperations.java | 14 ++++- .../arc4/Arc4EncryptionPluginSpiTest.java | 55 ++++++++++++++++ .../chacha/ChaChaEncryptionPluginSpiTest.java | 62 +++++++++++++++++++ 10 files changed, 276 insertions(+), 5 deletions(-) create mode 100644 chacha64-plugin/src/test/java/org/firebirdsql/jaybird/chacha64/ChaCha64EncryptionPluginSpiTest.java create mode 100644 src/main/org/firebirdsql/gds/ng/wire/crypt/CryptConnectionInfo.java create mode 100644 src/test/org/firebirdsql/gds/ng/wire/crypt/arc4/Arc4EncryptionPluginSpiTest.java create mode 100644 src/test/org/firebirdsql/gds/ng/wire/crypt/chacha/ChaChaEncryptionPluginSpiTest.java diff --git a/chacha64-plugin/src/main/java/org/firebirdsql/jaybird/chacha64/ChaCha64EncryptionPlugin.java b/chacha64-plugin/src/main/java/org/firebirdsql/jaybird/chacha64/ChaCha64EncryptionPlugin.java index fcb6d35ba..5a1adddca 100644 --- a/chacha64-plugin/src/main/java/org/firebirdsql/jaybird/chacha64/ChaCha64EncryptionPlugin.java +++ b/chacha64-plugin/src/main/java/org/firebirdsql/jaybird/chacha64/ChaCha64EncryptionPlugin.java @@ -45,7 +45,7 @@ * @author Mark Rotteveel * @since 6 */ -public class ChaCha64EncryptionPlugin implements EncryptionPlugin { +public final class ChaCha64EncryptionPlugin implements EncryptionPlugin { private static final String CHA_CHA_CIPHER_NAME = "ChaCha"; diff --git a/chacha64-plugin/src/main/java/org/firebirdsql/jaybird/chacha64/ChaCha64EncryptionPluginSpi.java b/chacha64-plugin/src/main/java/org/firebirdsql/jaybird/chacha64/ChaCha64EncryptionPluginSpi.java index a4775d34e..25801b361 100644 --- a/chacha64-plugin/src/main/java/org/firebirdsql/jaybird/chacha64/ChaCha64EncryptionPluginSpi.java +++ b/chacha64-plugin/src/main/java/org/firebirdsql/jaybird/chacha64/ChaCha64EncryptionPluginSpi.java @@ -19,6 +19,8 @@ package org.firebirdsql.jaybird.chacha64; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.firebirdsql.gds.impl.wire.WireProtocolConstants; +import org.firebirdsql.gds.ng.wire.crypt.CryptConnectionInfo; import org.firebirdsql.gds.ng.wire.crypt.CryptSessionConfig; import org.firebirdsql.gds.ng.wire.crypt.EncryptionIdentifier; import org.firebirdsql.gds.ng.wire.crypt.EncryptionPlugin; @@ -35,7 +37,7 @@ * @author Mark Rotteveel * @since 6 */ -public class ChaCha64EncryptionPluginSpi implements EncryptionPluginSpi { +public final class ChaCha64EncryptionPluginSpi implements EncryptionPluginSpi { static final EncryptionIdentifier CHA_CHA_64_ID = new EncryptionIdentifier("Symmetric", "ChaCha64"); // Use registered Bouncy Castle if possible, otherwise use our own unregistered instance @@ -52,6 +54,11 @@ public EncryptionPlugin createEncryptionPlugin(CryptSessionConfig cryptSessionCo return new ChaCha64EncryptionPlugin(cryptSessionConfig, provider); } + @Override + public boolean isSupported(CryptConnectionInfo cryptConnectionInfo) { + return cryptConnectionInfo.protocolVersion() >= WireProtocolConstants.PROTOCOL_VERSION16; + } + private static Provider createProvider() { return new BouncyCastleProvider(); } diff --git a/chacha64-plugin/src/test/java/org/firebirdsql/jaybird/chacha64/ChaCha64EncryptionPluginSpiTest.java b/chacha64-plugin/src/test/java/org/firebirdsql/jaybird/chacha64/ChaCha64EncryptionPluginSpiTest.java new file mode 100644 index 000000000..507e1d98a --- /dev/null +++ b/chacha64-plugin/src/test/java/org/firebirdsql/jaybird/chacha64/ChaCha64EncryptionPluginSpiTest.java @@ -0,0 +1,62 @@ +/* + * Firebird Open Source JDBC Driver + * + * Distributable under LGPL license. + * You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * LGPL License for more details. + * + * This file was created by members of the firebird development team. + * All individual contributions remain the Copyright (C) of those + * individuals. Contributors to this file are either listed here or + * can be obtained from a source control history command. + * + * All rights reserved. + */ +package org.firebirdsql.jaybird.chacha64; + +import org.firebirdsql.gds.ng.wire.crypt.CryptConnectionInfo; +import org.firebirdsql.gds.ng.wire.crypt.EncryptionIdentifier; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.firebirdsql.gds.impl.wire.WireProtocolConstants.PROTOCOL_VERSION13; +import static org.firebirdsql.gds.impl.wire.WireProtocolConstants.PROTOCOL_VERSION15; +import static org.firebirdsql.gds.impl.wire.WireProtocolConstants.PROTOCOL_VERSION16; +import static org.firebirdsql.gds.impl.wire.WireProtocolConstants.PROTOCOL_VERSION18; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author Mark Rotteveel + */ +class ChaCha64EncryptionPluginSpiTest { + + @Test + void encryptionIdentifier() { + assertEquals(new EncryptionIdentifier("Symmetric", "ChaCha64"), new ChaCha64EncryptionPluginSpi().encryptionIdentifier()); + } + + @ParameterizedTest + @ValueSource(ints = { PROTOCOL_VERSION16, PROTOCOL_VERSION18 }) + void isSupported_true(int protocolVersion) { + assertTrue(new ChaCha64EncryptionPluginSpi().isSupported(new ConnectionInfoImpl(protocolVersion))); + } + + @ParameterizedTest + // NOTE: Implementation also reports false for PROTOCOL_VERSION10 - 12, which don't support wire encryption, + // but we don't check those versions + @ValueSource(ints = { PROTOCOL_VERSION13, PROTOCOL_VERSION15 }) + void isSupported_false(int protocolVersion) { + assertFalse(new ChaCha64EncryptionPluginSpi().isSupported(new ConnectionInfoImpl(protocolVersion))); + } + + private record ConnectionInfoImpl(int protocolVersion) implements CryptConnectionInfo { + } + +} \ No newline at end of file diff --git a/src/main/org/firebirdsql/gds/ng/wire/crypt/CryptConnectionInfo.java b/src/main/org/firebirdsql/gds/ng/wire/crypt/CryptConnectionInfo.java new file mode 100644 index 000000000..94107017a --- /dev/null +++ b/src/main/org/firebirdsql/gds/ng/wire/crypt/CryptConnectionInfo.java @@ -0,0 +1,42 @@ +/* + * Firebird Open Source JDBC Driver + * + * Distributable under LGPL license. + * You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * LGPL License for more details. + * + * This file was created by members of the firebird development team. + * All individual contributions remain the Copyright (C) of those + * individuals. Contributors to this file are either listed here or + * can be obtained from a source control history command. + * + * All rights reserved. + */ +package org.firebirdsql.gds.ng.wire.crypt; + +/** + * Details of the connection, which the SPI can use to decide if they can work. + *

+ * NOTE: This class is currently only internal to Jaybird, consider the API as unstable. + *

+ * + * @author Mark Rotteveel + * @since 6 + */ +public interface CryptConnectionInfo { + + /** + * Protocol version of the connection. + *

+ * The protocol version is masked, so use the relevant {@code PROTOCOL_VERSIONnn} constants from + * {@link org.firebirdsql.gds.impl.wire.WireProtocolConstants} or equivalents for checks. + *

+ * + * @return protocol version, {@code 0} means unknown (shouldn't occur normally) + */ + int protocolVersion(); +} diff --git a/src/main/org/firebirdsql/gds/ng/wire/crypt/EncryptionPluginSpi.java b/src/main/org/firebirdsql/gds/ng/wire/crypt/EncryptionPluginSpi.java index 566266d7a..12715c994 100644 --- a/src/main/org/firebirdsql/gds/ng/wire/crypt/EncryptionPluginSpi.java +++ b/src/main/org/firebirdsql/gds/ng/wire/crypt/EncryptionPluginSpi.java @@ -48,4 +48,23 @@ public interface EncryptionPluginSpi { * @return Encryption plugin */ EncryptionPlugin createEncryptionPlugin(CryptSessionConfig cryptSessionConfig); + + /** + * Reports if the encryption plugin can work. + *

+ * The {@code connectionInfo} can be used to check compatibility with the connection, but other checks may be done + * as well. If the plugin expects to always work, it can simply return {@code true}. + *

+ *

+ * NOTE: Returning {@code true} does not express a guarantee the plugin will work, instead {@code false} expresses + * that the plugin cannot (or should not) be tried to use, because it will fail anyway. + *

+ * + * @param cryptConnectionInfo + * information on the connection + * @return {@code true} if the SPI expects the plugin to work, {@code false} if the plugin will not work + * @since 6 + */ + boolean isSupported(CryptConnectionInfo cryptConnectionInfo); + } diff --git a/src/main/org/firebirdsql/gds/ng/wire/crypt/arc4/Arc4EncryptionPluginSpi.java b/src/main/org/firebirdsql/gds/ng/wire/crypt/arc4/Arc4EncryptionPluginSpi.java index 3280e1e04..e6d907b17 100644 --- a/src/main/org/firebirdsql/gds/ng/wire/crypt/arc4/Arc4EncryptionPluginSpi.java +++ b/src/main/org/firebirdsql/gds/ng/wire/crypt/arc4/Arc4EncryptionPluginSpi.java @@ -18,6 +18,7 @@ */ package org.firebirdsql.gds.ng.wire.crypt.arc4; +import org.firebirdsql.gds.ng.wire.crypt.CryptConnectionInfo; import org.firebirdsql.gds.ng.wire.crypt.CryptSessionConfig; import org.firebirdsql.gds.ng.wire.crypt.EncryptionIdentifier; import org.firebirdsql.gds.ng.wire.crypt.EncryptionPlugin; @@ -42,4 +43,11 @@ public EncryptionIdentifier encryptionIdentifier() { public EncryptionPlugin createEncryptionPlugin(CryptSessionConfig cryptSessionConfig) { return new Arc4EncryptionPlugin(cryptSessionConfig); } + + @Override + public boolean isSupported(CryptConnectionInfo cryptConnectionInfo) { + // TODO Maybe check if ARC4 requirements are allowed by the security config? + return true; + } + } diff --git a/src/main/org/firebirdsql/gds/ng/wire/crypt/chacha/ChaChaEncryptionPluginSpi.java b/src/main/org/firebirdsql/gds/ng/wire/crypt/chacha/ChaChaEncryptionPluginSpi.java index b9133fc8e..499fe5fa0 100644 --- a/src/main/org/firebirdsql/gds/ng/wire/crypt/chacha/ChaChaEncryptionPluginSpi.java +++ b/src/main/org/firebirdsql/gds/ng/wire/crypt/chacha/ChaChaEncryptionPluginSpi.java @@ -18,6 +18,8 @@ */ package org.firebirdsql.gds.ng.wire.crypt.chacha; +import org.firebirdsql.gds.impl.wire.WireProtocolConstants; +import org.firebirdsql.gds.ng.wire.crypt.CryptConnectionInfo; import org.firebirdsql.gds.ng.wire.crypt.CryptSessionConfig; import org.firebirdsql.gds.ng.wire.crypt.EncryptionIdentifier; import org.firebirdsql.gds.ng.wire.crypt.EncryptionPlugin; @@ -42,4 +44,10 @@ public EncryptionIdentifier encryptionIdentifier() { public EncryptionPlugin createEncryptionPlugin(CryptSessionConfig cryptSessionConfig) { return new ChaChaEncryptionPlugin(cryptSessionConfig); } + + @Override + public boolean isSupported(CryptConnectionInfo cryptConnectionInfo) { + return cryptConnectionInfo.protocolVersion() >= WireProtocolConstants.PROTOCOL_VERSION16; + } + } diff --git a/src/main/org/firebirdsql/gds/ng/wire/version13/V13WireOperations.java b/src/main/org/firebirdsql/gds/ng/wire/version13/V13WireOperations.java index e68243124..fdc60a265 100644 --- a/src/main/org/firebirdsql/gds/ng/wire/version13/V13WireOperations.java +++ b/src/main/org/firebirdsql/gds/ng/wire/version13/V13WireOperations.java @@ -33,6 +33,7 @@ import org.firebirdsql.gds.ng.wire.GenericResponse; import org.firebirdsql.gds.ng.wire.WireConnection; import org.firebirdsql.gds.ng.wire.auth.ClientAuthBlock; +import org.firebirdsql.gds.ng.wire.crypt.CryptConnectionInfo; import org.firebirdsql.gds.ng.wire.crypt.CryptSessionConfig; import org.firebirdsql.gds.ng.wire.crypt.EncryptionIdentifier; import org.firebirdsql.gds.ng.wire.crypt.EncryptionInitInfo; @@ -200,16 +201,23 @@ private void tryKnownServerKeys() throws IOException, SQLException { private Optional tryKnownServerKey(KnownServerKey.PluginSpecificData pluginSpecificData, SQLExceptionChainBuilder chainBuilder) throws IOException { + record ConnectionInfoImpl(int protocolVersion) implements CryptConnectionInfo { + } + EncryptionIdentifier encryptionIdentifier = pluginSpecificData.encryptionIdentifier(); - EncryptionPluginSpi currentEncryptionSpi = + EncryptionPluginSpi encryptionPluginSpi = EncryptionPluginRegistry.getEncryptionPluginSpi(encryptionIdentifier); - if (currentEncryptionSpi == null) { + if (encryptionPluginSpi == null) { log.log(TRACE, "No wire encryption plugin available for {0}", encryptionIdentifier); return Optional.empty(); + } else if (!encryptionPluginSpi.isSupported(new ConnectionInfoImpl(getConnection().getProtocolVersion()))) { + log.log(TRACE, "Wire encryption plugin {0} skipped, not supported", encryptionIdentifier); + return Optional.empty(); } + try (CryptSessionConfig cryptSessionConfig = getCryptSessionConfig(encryptionIdentifier, pluginSpecificData.specificData())) { - EncryptionPlugin encryptionPlugin = currentEncryptionSpi.createEncryptionPlugin(cryptSessionConfig); + EncryptionPlugin encryptionPlugin = encryptionPluginSpi.createEncryptionPlugin(cryptSessionConfig); EncryptionInitInfo encryptionInitInfo = encryptionPlugin.initializeEncryption(); if (encryptionInitInfo.isSuccess()) { enableEncryption(encryptionInitInfo); diff --git a/src/test/org/firebirdsql/gds/ng/wire/crypt/arc4/Arc4EncryptionPluginSpiTest.java b/src/test/org/firebirdsql/gds/ng/wire/crypt/arc4/Arc4EncryptionPluginSpiTest.java new file mode 100644 index 000000000..a65d60969 --- /dev/null +++ b/src/test/org/firebirdsql/gds/ng/wire/crypt/arc4/Arc4EncryptionPluginSpiTest.java @@ -0,0 +1,55 @@ +/* + * Firebird Open Source JDBC Driver + * + * Distributable under LGPL license. + * You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * LGPL License for more details. + * + * This file was created by members of the firebird development team. + * All individual contributions remain the Copyright (C) of those + * individuals. Contributors to this file are either listed here or + * can be obtained from a source control history command. + * + * All rights reserved. + */ +package org.firebirdsql.gds.ng.wire.crypt.arc4; + +import org.firebirdsql.gds.ng.wire.crypt.CryptConnectionInfo; +import org.firebirdsql.gds.ng.wire.crypt.EncryptionIdentifier; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.firebirdsql.gds.impl.wire.WireProtocolConstants.PROTOCOL_VERSION13; +import static org.firebirdsql.gds.impl.wire.WireProtocolConstants.PROTOCOL_VERSION15; +import static org.firebirdsql.gds.impl.wire.WireProtocolConstants.PROTOCOL_VERSION16; +import static org.firebirdsql.gds.impl.wire.WireProtocolConstants.PROTOCOL_VERSION18; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests for {@link Arc4EncryptionPluginSpi}. + */ +class Arc4EncryptionPluginSpiTest { + + @Test + void encryptionIdentifier() { + assertEquals(new EncryptionIdentifier("Symmetric", "Arc4"), new Arc4EncryptionPluginSpi().encryptionIdentifier()); + } + + @ParameterizedTest + // NOTE: Implementation always reports true, also for PROTOCOL_VERSION10 - 12, which don't support wire encryption, + // but we don't check those versions + @ValueSource(ints = { PROTOCOL_VERSION13, PROTOCOL_VERSION15, PROTOCOL_VERSION16, PROTOCOL_VERSION18 }) + void isSupported_true(int protocolVersion) { + assertTrue(new Arc4EncryptionPluginSpi().isSupported(new ConnectionInfoImpl(protocolVersion))); + } + + private record ConnectionInfoImpl(int protocolVersion) implements CryptConnectionInfo { + } + +} \ No newline at end of file diff --git a/src/test/org/firebirdsql/gds/ng/wire/crypt/chacha/ChaChaEncryptionPluginSpiTest.java b/src/test/org/firebirdsql/gds/ng/wire/crypt/chacha/ChaChaEncryptionPluginSpiTest.java new file mode 100644 index 000000000..520b39642 --- /dev/null +++ b/src/test/org/firebirdsql/gds/ng/wire/crypt/chacha/ChaChaEncryptionPluginSpiTest.java @@ -0,0 +1,62 @@ +/* + * Firebird Open Source JDBC Driver + * + * Distributable under LGPL license. + * You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * LGPL License for more details. + * + * This file was created by members of the firebird development team. + * All individual contributions remain the Copyright (C) of those + * individuals. Contributors to this file are either listed here or + * can be obtained from a source control history command. + * + * All rights reserved. + */ +package org.firebirdsql.gds.ng.wire.crypt.chacha; + +import org.firebirdsql.gds.ng.wire.crypt.CryptConnectionInfo; +import org.firebirdsql.gds.ng.wire.crypt.EncryptionIdentifier; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.firebirdsql.gds.impl.wire.WireProtocolConstants.PROTOCOL_VERSION13; +import static org.firebirdsql.gds.impl.wire.WireProtocolConstants.PROTOCOL_VERSION15; +import static org.firebirdsql.gds.impl.wire.WireProtocolConstants.PROTOCOL_VERSION16; +import static org.firebirdsql.gds.impl.wire.WireProtocolConstants.PROTOCOL_VERSION18; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests for {@link ChaChaEncryptionPluginSpi}. + */ +class ChaChaEncryptionPluginSpiTest { + + @Test + void encryptionIdentifier() { + assertEquals(new EncryptionIdentifier("Symmetric", "ChaCha"), new ChaChaEncryptionPluginSpi().encryptionIdentifier()); + } + + @ParameterizedTest + @ValueSource(ints = { PROTOCOL_VERSION16, PROTOCOL_VERSION18 }) + void isSupported_true(int protocolVersion) { + assertTrue(new ChaChaEncryptionPluginSpi().isSupported(new ConnectionInfoImpl(protocolVersion))); + } + + @ParameterizedTest + // NOTE: Implementation also reports false for PROTOCOL_VERSION10 - 12, which don't support wire encryption, + // but we don't check those versions + @ValueSource(ints = { PROTOCOL_VERSION13, PROTOCOL_VERSION15 }) + void isSupported_false(int protocolVersion) { + assertFalse(new ChaChaEncryptionPluginSpi().isSupported(new ConnectionInfoImpl(protocolVersion))); + } + + private record ConnectionInfoImpl(int protocolVersion) implements CryptConnectionInfo { + } + +} \ No newline at end of file