xref: /aosp_15_r20/external/aws-crt-java/src/native/custom_key_op_handler.c (revision 3c7ae9de214676c52d19f01067dc1a404272dc11)
1 /**
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  * SPDX-License-Identifier: Apache-2.0.
4  */
5 #include "custom_key_op_handler.h"
6 
7 #include "java_class_ids.h"
8 #include "tls_context_pkcs11_options.h"
9 #include <aws/common/string.h>
10 #include <aws/io/tls_channel_handler.h>
11 
12 struct aws_jni_custom_key_op_handler {
13     /**
14      * The C class containing the key operations. We extend/define it's VTable to allow
15      * us to have it call into the customer's Java code.
16      */
17     struct aws_custom_key_op_handler base;
18 
19     /** A pointer to the JVM for getting JNI threads */
20     JavaVM *jvm;
21 
22     /**
23      * A reference to the Java class that this struct is linked to.
24      * The interface, strings, etc, can be gotten from this class.
25      */
26     jobject jni_custom_key_op;
27 
28     /** The allocator to use */
29     struct aws_allocator *allocator;
30 };
31 
s_aws_custom_key_op_handler_perform_operation(struct aws_custom_key_op_handler * key_op_handler,struct aws_tls_key_operation * operation)32 static void s_aws_custom_key_op_handler_perform_operation(
33     struct aws_custom_key_op_handler *key_op_handler,
34     struct aws_tls_key_operation *operation) {
35 
36     struct aws_jni_custom_key_op_handler *op_handler = (struct aws_jni_custom_key_op_handler *)key_op_handler->impl;
37     AWS_FATAL_ASSERT(op_handler != NULL);
38 
39     jbyteArray jni_input_data = NULL;
40     jobject jni_operation = NULL;
41     bool success = false;
42     AWS_ASSERT(operation != NULL);
43 
44     /* Get the Java ENV */
45     JNIEnv *env = aws_jni_acquire_thread_env(op_handler->jvm);
46     if (env == NULL) {
47         /* JVM is likely shutting down. Do not crash but log error. */
48         AWS_LOGF_ERROR(
49             AWS_LS_COMMON_IO,
50             "java_custom_key_op_handler=%p perform operation: Could not get Java ENV!",
51             (void *)op_handler);
52         goto clean_up;
53     }
54 
55     /* Create DirectByteBuffer */
56     struct aws_byte_cursor input_data = aws_tls_key_operation_get_input(operation);
57     jni_input_data = aws_jni_byte_array_from_cursor(env, &input_data);
58     if (jni_input_data == NULL) {
59         aws_jni_check_and_clear_exception(env);
60         goto clean_up;
61     }
62 
63     /* Create TlsKeyOperation */
64     jni_operation = (*env)->NewObject(
65         env,
66         tls_key_operation_properties.cls,
67         tls_key_operation_properties.constructor,
68         (jlong)(intptr_t)operation,
69         jni_input_data,
70         (jint)aws_tls_key_operation_get_type(operation),
71         (jint)aws_tls_key_operation_get_signature_algorithm(operation),
72         (jint)aws_tls_key_operation_get_digest_algorithm(operation));
73     if (jni_operation == NULL) {
74         aws_jni_check_and_clear_exception(env);
75         goto clean_up;
76     }
77 
78     /**
79      * Invoke TlsKeyOperationHandler.performOperation() through the invokePerformOperation
80      * function. This function will also catch any exceptions and clear the operation
81      * with an exception should it occur.
82      */
83     (*env)->CallStaticVoidMethod(
84         env,
85         tls_key_operation_properties.cls,
86         tls_key_operation_properties.invoke_operation_id,
87         op_handler->jni_custom_key_op,
88         jni_operation);
89     /**
90      * This should never fail because the function we're calling, invokePerformOperation,
91      * wraps the user callback in a try-catch block that will catch any exceptions.
92      */
93     AWS_FATAL_ASSERT(!aws_jni_check_and_clear_exception(env));
94     success = true;
95 
96 clean_up:
97     if (jni_input_data) {
98         (*env)->DeleteLocalRef(env, jni_input_data);
99     }
100     if (jni_operation) {
101         (*env)->DeleteLocalRef(env, jni_operation);
102     }
103     if (!success) {
104         aws_tls_key_operation_complete_with_error(operation, AWS_ERROR_UNKNOWN);
105     }
106 
107     /* Release the Java ENV */
108     aws_jni_release_thread_env(op_handler->jvm, env);
109 }
110 
s_aws_custom_key_op_handler_destroy(struct aws_custom_key_op_handler * key_op_handler)111 static void s_aws_custom_key_op_handler_destroy(struct aws_custom_key_op_handler *key_op_handler) {
112 
113     struct aws_jni_custom_key_op_handler *op_handler = (struct aws_jni_custom_key_op_handler *)key_op_handler->impl;
114 
115     /* Get the Java ENV */
116     JNIEnv *env = aws_jni_acquire_thread_env(op_handler->jvm);
117     if (env == NULL) {
118         /* JVM is likely shutting down. Do not crash but log error. */
119         AWS_LOGF_ERROR(
120             AWS_LS_COMMON_IO, "java_custom_key_op_handler=%p destroy: Could not get Java ENV!", (void *)op_handler);
121         return;
122     }
123 
124     /* Release the global reference */
125     if (op_handler->jni_custom_key_op) {
126         (*env)->DeleteGlobalRef(env, op_handler->jni_custom_key_op);
127     }
128 
129     /* Release the Java ENV */
130     aws_jni_release_thread_env(op_handler->jvm, env);
131 
132     /* Release the Java struct */
133     aws_mem_release(op_handler->allocator, op_handler);
134 }
135 
136 static struct aws_custom_key_op_handler_vtable s_aws_custom_key_op_handler_vtable = {
137     .on_key_operation = s_aws_custom_key_op_handler_perform_operation,
138 };
139 
aws_custom_key_op_handler_java_new(JNIEnv * env,jobject jni_custom_key_op)140 struct aws_custom_key_op_handler *aws_custom_key_op_handler_java_new(JNIEnv *env, jobject jni_custom_key_op) {
141 
142     struct aws_allocator *allocator = aws_jni_get_allocator();
143 
144     struct aws_jni_custom_key_op_handler *java_custom_key_op_handler =
145         aws_mem_calloc(allocator, 1, sizeof(struct aws_jni_custom_key_op_handler));
146 
147     if ((*env)->GetJavaVM(env, &java_custom_key_op_handler->jvm) != 0) {
148         aws_jni_throw_runtime_exception(env, "failed to get JVM");
149         aws_mem_release(allocator, java_custom_key_op_handler);
150         return NULL;
151     }
152 
153     aws_ref_count_init(
154         &java_custom_key_op_handler->base.ref_count,
155         &java_custom_key_op_handler->base,
156         (aws_simple_completion_callback *)s_aws_custom_key_op_handler_destroy);
157     java_custom_key_op_handler->base.vtable = &s_aws_custom_key_op_handler_vtable;
158     java_custom_key_op_handler->base.impl = (void *)java_custom_key_op_handler;
159     /* Make a global reference so the Java interface is kept alive */
160     java_custom_key_op_handler->jni_custom_key_op = (*env)->NewGlobalRef(env, jni_custom_key_op);
161     AWS_FATAL_ASSERT(java_custom_key_op_handler->jni_custom_key_op != NULL);
162     java_custom_key_op_handler->allocator = allocator;
163 
164     AWS_LOGF_DEBUG(
165         AWS_LS_COMMON_IO,
166         "java_custom_key_op_handler=%p: Initalizing Custom Key Operations",
167         (void *)java_custom_key_op_handler);
168 
169     return &java_custom_key_op_handler->base;
170 }
171 
aws_custom_key_op_handler_java_release(struct aws_custom_key_op_handler * custom_key_op_handler)172 void aws_custom_key_op_handler_java_release(struct aws_custom_key_op_handler *custom_key_op_handler) {
173 
174     if (custom_key_op_handler == NULL) {
175         return;
176     }
177     struct aws_jni_custom_key_op_handler *java_custom_key_op_handler =
178         (struct aws_jni_custom_key_op_handler *)custom_key_op_handler->impl;
179 
180     AWS_LOGF_DEBUG(
181         AWS_LS_COMMON_IO,
182         "java_custom_key_op_handler=%p: Releasing Custom Key Operations (may destroy custom key operations if "
183         "this Java class holds the last reference)",
184         (void *)java_custom_key_op_handler);
185 
186     /**
187      * Release the reference (which will only clean everything up if this is the last thing holding a reference)
188      */
189     aws_custom_key_op_handler_release(&java_custom_key_op_handler->base);
190 }
191