This is an automated email from the ASF dual-hosted git repository.

markt-asf pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat-native.git

commit 6a6784e8ad7c31b7392d1fae9ce025096af437a5
Author: Mark Thomas <[email protected]>
AuthorDate: Mon Sep 21 11:59:53 2026 +0100

    Add PSK support for TLS 1.2
    
    Co-authored-by: GitHub Copilot (GPT-5.6 Sol) <[email protected]>
---
 .../apache/tomcat/jni/PreSharedKeySelector.java    | 33 ++++++++
 java/org/apache/tomcat/jni/SSLContext.java         |  9 ++
 native/include/ssl_private.h                       |  4 +
 native/src/sslcontext.c                            | 97 ++++++++++++++++++++++
 xdocs/miscellaneous/changelog.xml                  |  4 +
 5 files changed, 147 insertions(+)

diff --git a/java/org/apache/tomcat/jni/PreSharedKeySelector.java 
b/java/org/apache/tomcat/jni/PreSharedKeySelector.java
new file mode 100644
index 000000000..0cafc2a90
--- /dev/null
+++ b/java/org/apache/tomcat/jni/PreSharedKeySelector.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.jni;
+
+/**
+ * Is called during a TLSv1.2 handshake and hooked into OpenSSL via {@code 
SSL_CTX_set_psk_server_callback}.
+ */
+public interface PreSharedKeySelector {
+
+    /**
+     * Selects the pre-shared key for the provided identity.
+     *
+     * @param ssl      the SSL instance
+     * @param identity the PSK identity provided by the client
+     *
+     * @return the pre-shared key, or {@code null} if the identity is not 
recognized
+     */
+    byte[] select(long ssl, String identity);
+}
diff --git a/java/org/apache/tomcat/jni/SSLContext.java 
b/java/org/apache/tomcat/jni/SSLContext.java
index e1d2940c5..fa56214fe 100644
--- a/java/org/apache/tomcat/jni/SSLContext.java
+++ b/java/org/apache/tomcat/jni/SSLContext.java
@@ -471,6 +471,15 @@ public final class SSLContext {
      */
     public static native void setCertVerifyCallback(long ctx, 
CertificateVerifier verifier);
 
+    /**
+     * Allow to hook {@link PreSharedKeySelector} into the TLSv1.2 handshake 
processing. This will call
+     * {@code SSL_CTX_set_psk_server_callback}.
+     *
+     * @param ctx      Server context to use.
+     * @param selector the selector to call during handshake, or {@code null} 
to remove the current selector
+     */
+    public static native void setPskServerCallback(long ctx, 
PreSharedKeySelector selector);
+
     /**
      * Set application layer protocol for application layer protocol 
negotiation extension
      *
diff --git a/native/include/ssl_private.h b/native/include/ssl_private.h
index b4e829c3a..8b6575cab 100644
--- a/native/include/ssl_private.h
+++ b/native/include/ssl_private.h
@@ -195,6 +195,10 @@ struct tcn_ssl_ctxt_t {
     jobject verifier;
     jmethodID verifier_method;
 
+    /* TLSv1.2 server-side pre-shared key selector */
+    jobject psk_selector;
+    jmethodID psk_selector_method;
+
     /* Holds the alpn protocols, each of them prefixed with the len of the 
protocol */
     unsigned char   *alpn_proto_data;
     unsigned int    alpn_proto_len;
diff --git a/native/src/sslcontext.c b/native/src/sslcontext.c
index 71d158189..c9ce9c73a 100644
--- a/native/src/sslcontext.c
+++ b/native/src/sslcontext.c
@@ -61,6 +61,14 @@ static apr_status_t ssl_context_cleanup(void *data)
         }
         c->verifier_method = NULL;
 
+        if (c->psk_selector) {
+            JNIEnv *e;
+            tcn_get_java_env(&e);
+            (*e)->DeleteGlobalRef(e, c->psk_selector);
+            c->psk_selector = NULL;
+        }
+        c->psk_selector_method = NULL;
+
         if (c->alpn_proto_data) {
             free(c->alpn_proto_data);
             c->alpn_proto_data = NULL;
@@ -1482,6 +1490,95 @@ TCN_IMPLEMENT_CALL(void, SSLContext, 
setCertVerifyCallback)(TCN_STDARGS, jlong c
     }
 }
 
+#ifndef OPENSSL_NO_PSK
+static unsigned int SSL_psk_server(SSL *ssl, const char *identity, unsigned 
char *psk, unsigned int max_psk_len)
+{
+    tcn_ssl_ctxt_t *c = SSL_get_app_data2(ssl);
+    JNIEnv *e;
+    jstring identity_string = NULL;
+    jbyteArray key = NULL;
+    jsize key_len;
+    unsigned int result = 0;
+
+    if (c == NULL || c->psk_selector == NULL || c->psk_selector_method == NULL 
|| identity == NULL ||
+            tcn_get_java_env(&e) != JNI_OK) {
+        return 0;
+    }
+
+    identity_string = (*e)->NewStringUTF(e, identity);
+    if (identity_string == NULL) {
+        goto cleanup;
+    }
+
+    key = (*e)->CallObjectMethod(e, c->psk_selector, c->psk_selector_method, 
P2J(ssl), identity_string);
+    if ((*e)->ExceptionCheck(e) || key == NULL) {
+        goto cleanup;
+    }
+
+    key_len = (*e)->GetArrayLength(e, key);
+    if (key_len <= 0 || (unsigned int) key_len > max_psk_len) {
+        goto cleanup;
+    }
+
+    (*e)->GetByteArrayRegion(e, key, 0, key_len, (jbyte *)psk);
+    if ((*e)->ExceptionCheck(e)) {
+        goto cleanup;
+    }
+    result = (unsigned int) key_len;
+
+cleanup:
+    if ((*e)->ExceptionCheck(e)) {
+        (*e)->ExceptionClear(e);
+    }
+    if (key != NULL) {
+        (*e)->DeleteLocalRef(e, key);
+    }
+    if (identity_string != NULL) {
+        (*e)->DeleteLocalRef(e, identity_string);
+    }
+    return result;
+}
+#endif
+
+TCN_IMPLEMENT_CALL(void, SSLContext, setPskServerCallback)(TCN_STDARGS, jlong 
ctx, jobject selector)
+{
+#ifdef OPENSSL_NO_PSK
+    UNREFERENCED(o);
+    UNREFERENCED(ctx);
+    UNREFERENCED(selector);
+    tcn_Throw(e, "OpenSSL does not support PSK");
+#else
+    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
+    jobject new_selector = NULL;
+    jmethodID new_method = NULL;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(ctx != 0);
+
+    if (selector != NULL) {
+        jclass selector_class = (*e)->GetObjectClass(e, selector);
+        new_method = (*e)->GetMethodID(e, selector_class, "select", 
"(JLjava/lang/String;)[B");
+        (*e)->DeleteLocalRef(e, selector_class);
+        if (new_method == NULL) {
+            return;
+        }
+
+        new_selector = (*e)->NewGlobalRef(e, selector);
+        if (new_selector == NULL) {
+            return;
+        }
+    }
+
+    SSL_CTX_set_psk_server_callback(c->ctx, selector == NULL ? NULL : 
SSL_psk_server);
+
+    if (c->psk_selector != NULL) {
+        (*e)->DeleteGlobalRef(e, c->psk_selector);
+    }
+    c->psk_selector = new_selector;
+    c->psk_selector_method = new_method;
+#endif
+}
+
 TCN_IMPLEMENT_CALL(jboolean, SSLContext, setSessionIdContext)(TCN_STDARGS, 
jlong ctx, jbyteArray sidCtx)
 {
     tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
diff --git a/xdocs/miscellaneous/changelog.xml 
b/xdocs/miscellaneous/changelog.xml
index 6eed1301c..2eeaa964a 100644
--- a/xdocs/miscellaneous/changelog.xml
+++ b/xdocs/miscellaneous/changelog.xml
@@ -51,6 +51,10 @@
     <update>
       Make Windows 10 / 11 the default target version for Windows builds. 
(rjung)
     </update>
+    <add>
+      Add support for configuring a server-side TLSv1.2 pre-shared key
+      callback. (markt)
+    </add>
     <fix>
       Fix a potential crash when negotiating ALPN. (markt)
     </fix>


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to