1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 //! JNI bindings for the ukey2 rust implementation
16
17 #![allow(unsafe_code, clippy::expect_used)]
18 //TODO: remove this and fix instances of unwrap/panic
19 #![allow(clippy::unwrap_used, clippy::panic)]
20
21 use jni::objects::{JByteArray, JClass, JIntArray, JThrowable};
22 use jni::sys::{jboolean, jbyteArray, jint, jintArray, jlong, JNI_TRUE};
23 use jni::JNIEnv;
24 use lazy_static::lazy_static;
25 use lock_adapter::NoPoisonMutex;
26 use rand::Rng;
27 use rand_chacha::rand_core::SeedableRng;
28 use rand_chacha::ChaCha20Rng;
29 use std::collections::HashMap;
30
31 #[cfg(not(feature = "std"))]
32 use lock_adapter::spin::Mutex;
33 #[cfg(feature = "std")]
34 use lock_adapter::stdlib::Mutex;
35
36 use ukey2_connections::{
37 D2DConnectionContextV1, D2DHandshakeContext, DecodeError, DeserializeError, HandleMessageError,
38 HandshakeError, HandshakeImplementation, InitiatorD2DHandshakeContext, NextProtocol,
39 ServerD2DHandshakeContext,
40 };
41
42 use crypto_provider_default::CryptoProviderImpl as CryptoProvider;
43 // Handle management
44
45 type D2DBox = Box<dyn D2DHandshakeContext>;
46 type ConnectionBox = Box<D2DConnectionContextV1>;
47
48 lazy_static! {
49 static ref HANDLE_MAPPING: Mutex<HashMap<u64, D2DBox>> = Mutex::new(HashMap::new());
50 static ref CONNECTION_HANDLE_MAPPING: Mutex<HashMap<u64, ConnectionBox>> =
51 Mutex::new(HashMap::new());
52 static ref RNG: Mutex<ChaCha20Rng> = Mutex::new(ChaCha20Rng::from_entropy());
53 }
54
generate_handle() -> u6455 fn generate_handle() -> u64 {
56 RNG.lock().gen()
57 }
58
insert_handshake_handle(item: D2DBox) -> u6459 pub(crate) fn insert_handshake_handle(item: D2DBox) -> u64 {
60 let mut handle = generate_handle();
61 let mut map = HANDLE_MAPPING.lock();
62 while map.contains_key(&handle) {
63 handle = generate_handle();
64 }
65
66 let result = map.insert(handle, item);
67 // result should always be None since we checked that handle map does not contain the key already
68 assert!(result.is_none());
69 handle
70 }
71
insert_conn_handle(item: ConnectionBox) -> u6472 pub(crate) fn insert_conn_handle(item: ConnectionBox) -> u64 {
73 let mut handle = generate_handle();
74 let mut map = CONNECTION_HANDLE_MAPPING.lock();
75 while map.contains_key(&handle) {
76 handle = generate_handle();
77 }
78
79 let result = map.insert(handle, item);
80 // result should always be None since we checked that handle map does not contain the key already
81 assert!(result.is_none());
82 handle
83 }
84
85 #[derive(Debug)]
86 enum JniError {
87 BadHandle,
88 DecodeError(DecodeError),
89 HandleMessageError(HandleMessageError),
90 HandshakeError(HandshakeError),
91 }
92
93 /// Tells the caller whether the handshake has completed or not. If the handshake is complete,
94 /// the caller may call `to_connection_context`to obtain a connection context.
95 #[no_mangle]
Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DHandshakeContext_is_1handshake_1complete( mut env: JNIEnv, _: JClass, context_handle: jlong, ) -> jboolean96 pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DHandshakeContext_is_1handshake_1complete(
97 mut env: JNIEnv,
98 _: JClass,
99 context_handle: jlong,
100 ) -> jboolean {
101 let mut is_complete = false;
102 if let Some(ctx) = HANDLE_MAPPING.lock().get(&(context_handle as u64)) {
103 is_complete = ctx.is_handshake_complete();
104 } else {
105 env.throw_new("com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException", "")
106 .expect("failed to find error class");
107 }
108 is_complete as jboolean
109 }
110
111 /// Creates a new handshake context
112 // Safety:
113 // - Valid pointer: We know the message pointer is safe as it is coming directly from the JVM.
114 // - This pointer is nullable, but we null-check and default to AES-CBC-256_HMAC-SHA256 otherwise.
115 // - Lifetime - the jintArray passed in here is consumed immediately and is copied into a Rust array,
116 // so this data does not outlive this frame.
117 // - Aliasing - there is no other JObject representing this as it is only used in one place.
118 #[allow(clippy::not_unsafe_ptr_arg_deref)]
119 #[no_mangle]
Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DHandshakeContext_create_1context( mut env: JNIEnv, _: JClass, is_client: jboolean, next_protocols: jintArray, ) -> jlong120 pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DHandshakeContext_create_1context(
121 mut env: JNIEnv,
122 _: JClass,
123 is_client: jboolean,
124 next_protocols: jintArray,
125 ) -> jlong {
126 let next_protocols = if next_protocols.is_null() {
127 vec![NextProtocol::Aes256CbcHmacSha256]
128 } else {
129 let next_protocols_raw = unsafe { JIntArray::from_raw(next_protocols) };
130 let next_protocols_len =
131 env.get_array_length(&next_protocols_raw).expect("Array should be valid!");
132 let mut next_protocol_buf =
133 vec![0; usize::try_from(next_protocols_len).expect("len should be valid usize!")];
134 env.get_int_array_region(&next_protocols_raw, 0, &mut next_protocol_buf)
135 .expect("Should've extracted next protocols!");
136 next_protocol_buf
137 .iter()
138 .map(|p| match *p {
139 0 => NextProtocol::Aes256CbcHmacSha256,
140 1 => NextProtocol::Aes256GcmSiv,
141 _ => {
142 env.throw_new(
143 "com/google/security/cryptauth/lib/securegcm/ukey2/HandshakeException",
144 "Unsupported next protocols selected! Supported: [0, 1]",
145 )
146 .expect("failed to find error class");
147 unreachable!()
148 }
149 })
150 .collect()
151 };
152
153 if is_client == JNI_TRUE {
154 let client_obj = Box::new(InitiatorD2DHandshakeContext::<CryptoProvider>::new(
155 HandshakeImplementation::PublicKeyInProtobuf,
156 next_protocols,
157 ));
158 insert_handshake_handle(client_obj) as jlong
159 } else {
160 let server_obj = Box::new(ServerD2DHandshakeContext::<CryptoProvider>::new(
161 HandshakeImplementation::PublicKeyInProtobuf,
162 &next_protocols,
163 ));
164 insert_handshake_handle(server_obj) as jlong
165 }
166 }
167
168 /// Constructs the next message that should be sent in the handshake.
169 #[no_mangle]
Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DHandshakeContext_get_1next_1handshake_1message( mut env: JNIEnv, _: JClass, context_handle: jlong, ) -> jbyteArray170 pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DHandshakeContext_get_1next_1handshake_1message(
171 mut env: JNIEnv,
172 _: JClass,
173 context_handle: jlong,
174 ) -> jbyteArray {
175 let empty_arr = env.new_byte_array(0).unwrap();
176 let next_message = if let Some(ctx) = HANDLE_MAPPING.lock().get(&(context_handle as u64)) {
177 ctx.get_next_handshake_message()
178 } else {
179 env.throw_new("com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException", "")
180 .expect("failed to find error class");
181 None
182 };
183 // TODO error handling
184 if let Some(message) = next_message {
185 env.byte_array_from_slice(message.as_slice()).unwrap()
186 } else {
187 empty_arr
188 }
189 .into_raw()
190 }
191
192 /// Parses a handshake message and advances the internal state of the context.
193 // Safety: We know the message pointer is safe as it is coming directly from the JVM.
194 #[allow(clippy::not_unsafe_ptr_arg_deref)]
195 #[no_mangle]
Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DHandshakeContext_parse_1handshake_1message( mut env: JNIEnv, _: JClass, context_handle: jlong, message: jbyteArray, )196 pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DHandshakeContext_parse_1handshake_1message(
197 mut env: JNIEnv,
198 _: JClass,
199 context_handle: jlong,
200 message: jbyteArray,
201 ) {
202 let rust_buffer = env.convert_byte_array(unsafe { JByteArray::from_raw(message) }).unwrap();
203 let result = if let Some(ctx) = HANDLE_MAPPING.lock().get_mut(&(context_handle as u64)) {
204 ctx.handle_handshake_message(rust_buffer.as_slice()).map_err(JniError::HandleMessageError)
205 } else {
206 env.throw_new("com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException", "")
207 .expect("failed to find error class");
208 Err(JniError::BadHandle)
209 };
210 if let Err(e) = result {
211 if !env.exception_check().unwrap() {
212 let msg =
213 match e {
214 JniError::BadHandle => "Bad handle",
215 JniError::DecodeError(_) => "Unable to decode message",
216 JniError::HandleMessageError(hme) => match hme {
217 HandleMessageError::InvalidState | HandleMessageError::BadMessage => {
218 "Unable to handle message"
219 }
220 HandleMessageError::ErrorMessage(error_msg) => {
221 let exception: JThrowable = env.new_object(
222 "com/google/security/cryptauth/lib/securegcm/ukey2/AlertException",
223 "(Ljava/lang/String;[B)V",
224 &[
225 (&env
226 .new_string("Failed to handle message, sending alert")
227 .expect("valid str message for alert exception"))
228 .into(),
229 (&env
230 .byte_array_from_slice(&error_msg)
231 .expect("valid byte array for alert exception"))
232 .into(),
233 ],
234 ).expect("Did not successfully create AlertException").into();
235 env.throw(exception).expect("Throw alert exception");
236 ""
237 }
238 },
239 JniError::HandshakeError(_) => "Handshake incomplete",
240 };
241 if !env.exception_check().unwrap() {
242 env.throw_new(
243 "com/google/security/cryptauth/lib/securegcm/ukey2/HandshakeException",
244 msg,
245 )
246 .expect("failed to find error class");
247 }
248 }
249 }
250 }
251
252 /// Returns the `CompletedHandshake` using the results from this handshake context. May only
253 /// be called if `is_handshake_complete` returns true.
254 #[no_mangle]
Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DHandshakeContext_get_1verification_1string( mut env: JNIEnv, _: JClass, context_handle: jlong, length: jint, ) -> jbyteArray255 pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DHandshakeContext_get_1verification_1string(
256 mut env: JNIEnv,
257 _: JClass,
258 context_handle: jlong,
259 length: jint,
260 ) -> jbyteArray {
261 let empty_array = env.new_byte_array(0).unwrap();
262 let result = if let Some(ctx) = HANDLE_MAPPING.lock().get_mut(&(context_handle as u64)) {
263 ctx.to_completed_handshake()
264 .map_err(|_| JniError::HandshakeError(HandshakeError::HandshakeNotComplete))
265 .map(|h| h.auth_string::<CryptoProvider>().derive_vec(length as usize).unwrap())
266 } else {
267 env.throw_new("com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException", "")
268 .expect("failed to find error class");
269 Err(JniError::BadHandle)
270 };
271 if let Err(e) = result {
272 if !env.exception_check().unwrap() {
273 env.throw_new(
274 "com/google/security/cryptauth/lib/securegcm/ukey2/HandshakeException",
275 match e {
276 JniError::BadHandle => "Bad handle",
277 JniError::DecodeError(_) => "Unable to decode message",
278 JniError::HandleMessageError(_) => "Unable to handle message",
279 JniError::HandshakeError(_) => "Handshake incomplete",
280 },
281 )
282 .expect("failed to find error class");
283 }
284 empty_array
285 } else {
286 let ret_vec = result.unwrap();
287 env.byte_array_from_slice(&ret_vec).unwrap()
288 }
289 .into_raw()
290 }
291
292 /// Creates a [`D2DConnectionContextV1`] using the results of the handshake. May only be called
293 /// if `is_handshake_complete` returns true.
294 #[no_mangle]
Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DHandshakeContext_to_1connection_1context( mut env: JNIEnv, _: JClass, context_handle: jlong, ) -> jlong295 pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DHandshakeContext_to_1connection_1context(
296 mut env: JNIEnv,
297 _: JClass,
298 context_handle: jlong,
299 ) -> jlong {
300 let conn_context = if let Some(ctx) = HANDLE_MAPPING.lock().get_mut(&(context_handle as u64)) {
301 ctx.to_connection_context().map_err(JniError::HandshakeError)
302 } else {
303 Err(JniError::BadHandle)
304 };
305 if let Err(error) = conn_context {
306 env.throw_new(
307 "com/google/security/cryptauth/lib/securegcm/ukey2/HandshakeException",
308 match error {
309 JniError::BadHandle => "Bad context handle",
310 JniError::HandshakeError(_) => "Handshake not complete",
311 JniError::DecodeError(_) | JniError::HandleMessageError(_) => "Unknown exception",
312 },
313 )
314 .expect("failed to find error class");
315 return -1;
316 } else {
317 let _ = HANDLE_MAPPING.lock().remove(&(context_handle as u64));
318 }
319 insert_conn_handle(Box::new(conn_context.unwrap())) as jlong
320 }
321
322 /// Once initiator and responder have exchanged public keys, use this method to encrypt and
323 /// sign a payload. Both initiator and responder devices can use this message.
324 // Safety: We know the payload and associated_data pointers are safe as they are coming directly
325 // from the JVM.
326 #[allow(clippy::not_unsafe_ptr_arg_deref)]
327 #[no_mangle]
Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DConnectionContextV1_encode_1message_1to_1peer( mut env: JNIEnv, _: JClass, context_handle: jlong, payload: jbyteArray, associated_data: jbyteArray, ) -> jbyteArray328 pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DConnectionContextV1_encode_1message_1to_1peer(
329 mut env: JNIEnv,
330 _: JClass,
331 context_handle: jlong,
332 payload: jbyteArray,
333 associated_data: jbyteArray,
334 ) -> jbyteArray {
335 // We create the empty array here so we don't run into issues requesting a new byte array from
336 // the JNI env while an exception is being thrown.
337 let empty_array = env.new_byte_array(0).unwrap();
338 let result = if let Some(ctx) =
339 CONNECTION_HANDLE_MAPPING.lock().get_mut(&(context_handle as u64))
340 {
341 Ok(ctx.encode_message_to_peer::<CryptoProvider, _>(
342 env.convert_byte_array(unsafe { JByteArray::from_raw(payload) }).unwrap().as_slice(),
343 if associated_data.is_null() {
344 None
345 } else {
346 Some(
347 env.convert_byte_array(unsafe { JByteArray::from_raw(associated_data) })
348 .unwrap(),
349 )
350 },
351 ))
352 } else {
353 Err(JniError::BadHandle)
354 };
355 if let Ok(ret_vec) = result {
356 env.byte_array_from_slice(ret_vec.as_slice()).expect("unable to create jByteArray")
357 } else {
358 env.throw_new("com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException", "")
359 .expect("failed to find error class");
360 empty_array
361 }
362 .into_raw()
363 }
364
365 /// Once `InitiatorHello` and `ResponderHello` (and payload) are exchanged, use this method to
366 /// decrypt and verify a message received from the other device. Both initiator and responder
367 /// devices can use this message.
368 // Safety: We know the message and associated_data pointers are safe as they are coming directly
369 // from the JVM.
370 #[allow(clippy::not_unsafe_ptr_arg_deref)]
371 #[no_mangle]
Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DConnectionContextV1_decode_1message_1from_1peer( mut env: JNIEnv, _: JClass, context_handle: jlong, message: jbyteArray, associated_data: jbyteArray, ) -> jbyteArray372 pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DConnectionContextV1_decode_1message_1from_1peer(
373 mut env: JNIEnv,
374 _: JClass,
375 context_handle: jlong,
376 message: jbyteArray,
377 associated_data: jbyteArray,
378 ) -> jbyteArray {
379 let empty_array = env.new_byte_array(0).unwrap();
380 let result = if let Some(ctx) =
381 CONNECTION_HANDLE_MAPPING.lock().get_mut(&(context_handle as u64))
382 {
383 ctx.decode_message_from_peer::<CryptoProvider, _>(
384 env.convert_byte_array(unsafe { JByteArray::from_raw(message) }).unwrap().as_slice(),
385 if associated_data.is_null() {
386 None
387 } else {
388 Some(
389 env.convert_byte_array(unsafe { JByteArray::from_raw(associated_data) })
390 .unwrap(),
391 )
392 },
393 )
394 .map_err(JniError::DecodeError)
395 } else {
396 Err(JniError::BadHandle)
397 };
398 if let Ok(message) = result {
399 env.byte_array_from_slice(message.as_slice()).expect("unable to create jByteArray")
400 } else {
401 env.throw_new(
402 "com/google/security/cryptauth/lib/securegcm/ukey2/CryptoException",
403 match result.unwrap_err() {
404 JniError::BadHandle => "Bad context handle",
405 JniError::DecodeError(e) => match e {
406 DecodeError::BadData => "Bad data",
407 DecodeError::BadSequenceNumber => "Bad sequence number",
408 },
409 // None of these should ever occur in this case.
410 JniError::HandleMessageError(_) | JniError::HandshakeError(_) => "Unknown error",
411 },
412 )
413 .expect("failed to find exception class");
414 empty_array
415 }
416 .into_raw()
417 }
418
419 /// Returns the last sequence number used to encode a message.
420 #[no_mangle]
Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DConnectionContextV1_get_1sequence_1number_1for_1encoding( mut env: JNIEnv, _: JClass, context_handle: jlong, ) -> jint421 pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DConnectionContextV1_get_1sequence_1number_1for_1encoding(
422 mut env: JNIEnv,
423 _: JClass,
424 context_handle: jlong,
425 ) -> jint {
426 if let Some(ctx) = CONNECTION_HANDLE_MAPPING.lock().get(&(context_handle as u64)) {
427 ctx.get_sequence_number_for_encoding()
428 } else {
429 env.throw_new("com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException", "")
430 .expect("failed to find error class");
431 -1
432 }
433 }
434
435 /// Returns the last sequence number used to decode a message.
436 #[no_mangle]
Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DConnectionContextV1_get_1sequence_1number_1for_1decoding( mut env: JNIEnv, _: JClass, context_handle: jlong, ) -> jint437 pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DConnectionContextV1_get_1sequence_1number_1for_1decoding(
438 mut env: JNIEnv,
439 _: JClass,
440 context_handle: jlong,
441 ) -> jint {
442 if let Some(ctx) = CONNECTION_HANDLE_MAPPING.lock().get(&(context_handle as u64)) {
443 ctx.get_sequence_number_for_decoding()
444 } else {
445 env.throw_new("com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException", "")
446 .expect("failed to find error class");
447 -1
448 }
449 }
450
451 /// Creates a saved session that can later be used for resumption. The session data may be
452 /// persisted, but it must be stored in a secure location.
453 #[no_mangle]
Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DConnectionContextV1_save_1session( mut env: JNIEnv, _: JClass, context_handle: jlong, ) -> jbyteArray454 pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DConnectionContextV1_save_1session(
455 mut env: JNIEnv,
456 _: JClass,
457 context_handle: jlong,
458 ) -> jbyteArray {
459 let empty_array = env.new_byte_array(0).unwrap();
460 if let Some(ctx) = CONNECTION_HANDLE_MAPPING.lock().get(&(context_handle as u64)) {
461 env.byte_array_from_slice(ctx.save_session().as_slice()).expect("unable to save session")
462 } else {
463 env.throw_new("com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException", "")
464 .expect("failed to find error class");
465 empty_array
466 }
467 .into_raw()
468 }
469
470 /// Creates a connection context from a saved session.
471 // Safety: We know the session_info pointer is safe because it is coming directly from the JVM.
472 #[no_mangle]
473 #[allow(clippy::not_unsafe_ptr_arg_deref)]
Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DConnectionContextV1_from_1saved_1session( mut env: JNIEnv, _: JClass, session_info: jbyteArray, ) -> jlong474 pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DConnectionContextV1_from_1saved_1session(
475 mut env: JNIEnv,
476 _: JClass,
477 session_info: jbyteArray,
478 ) -> jlong {
479 let session_info_rust = env
480 .convert_byte_array(unsafe { JByteArray::from_raw(session_info) })
481 .expect("bad session_info data");
482 let ctx =
483 D2DConnectionContextV1::from_saved_session::<CryptoProvider>(session_info_rust.as_slice());
484 if ctx.is_err() {
485 env.throw_new(
486 "com/google/security/cryptauth/lib/securegcm/ukey2/SessionRestoreException",
487 match ctx.err().unwrap() {
488 DeserializeError::BadDataLength => "DeserializeError: bad session_info length",
489 DeserializeError::BadProtocolVersion => "DeserializeError: bad protocol version",
490 DeserializeError::BadData => "DeserializeError: bad data",
491 },
492 )
493 .expect("failed to find exception class");
494 return -1;
495 }
496 let final_ctx = ctx.ok().unwrap();
497 let conn_context_final = Box::new(final_ctx);
498 insert_conn_handle(conn_context_final) as jlong
499 }
500
501 /// Returns a cryptographic digest (SHA256) of the session keys prepended by the SHA256 hash
502 /// of the ASCII string "D2D". Since the server and client share the same session keys, the
503 /// resulting session unique is also the same.
504 #[no_mangle]
Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DConnectionContextV1_get_1session_1unique( mut env: JNIEnv, _: JClass, context_handle: jlong, ) -> jbyteArray505 pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DConnectionContextV1_get_1session_1unique(
506 mut env: JNIEnv,
507 _: JClass,
508 context_handle: jlong,
509 ) -> jbyteArray {
510 let empty_array = env.new_byte_array(0).unwrap();
511 if let Some(ctx) = CONNECTION_HANDLE_MAPPING.lock().get(&(context_handle as u64)) {
512 env.byte_array_from_slice(ctx.get_session_unique::<CryptoProvider>().as_slice())
513 .expect("unable to get unique session id")
514 } else {
515 env.throw_new("com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException", "")
516 .expect("failed to find error class");
517 empty_array
518 }
519 .into_raw()
520 }
521