diff options
Diffstat (limited to 'nearby/connections/ukey2/ukey2_jni/java')
13 files changed, 538 insertions, 539 deletions
diff --git a/nearby/connections/ukey2/ukey2_jni/java/build.gradle.kts b/nearby/connections/ukey2/ukey2_jni/java/build.gradle.kts index 58c58fc..78b0ebc 100644 --- a/nearby/connections/ukey2/ukey2_jni/java/build.gradle.kts +++ b/nearby/connections/ukey2/ukey2_jni/java/build.gradle.kts @@ -44,6 +44,7 @@ dependencies { implementation("com.google.code.findbugs:jsr305:3.0.2") implementation(kotlin("stdlib")) testImplementation("org.junit.jupiter:junit-jupiter:5.9.2") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.2") } kotlin { @@ -51,10 +52,10 @@ kotlin { } tasks.jmh { - jvmArgs.value(mutableListOf("-Djava.library.path=../../../../target/release")) + jvmArgs.value(mutableListOf("-Djava.library.path=$projectDir/../../../../target/release")) } tasks.test { useJUnitPlatform() - jvmArgs = mutableListOf("-Djava.library.path=../../../../target/debug") + jvmArgs = mutableListOf("-Djava.library.path=$projectDir/../../../../target/debug") } diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/jmh/java/com/google/security/cryptauth/lib/securegcm/Ukey2Benchmark.java b/nearby/connections/ukey2/ukey2_jni/java/src/jmh/java/com/google/security/cryptauth/lib/securegcm/Ukey2Benchmark.java deleted file mode 100644 index eb063cc..0000000 --- a/nearby/connections/ukey2/ukey2_jni/java/src/jmh/java/com/google/security/cryptauth/lib/securegcm/Ukey2Benchmark.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed 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 com.google.security.cryptauth.lib.securegcm; - -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.profile.GCProfiler; -import org.openjdk.jmh.runner.Runner; -import org.openjdk.jmh.runner.RunnerException; -import org.openjdk.jmh.runner.options.Options; -import org.openjdk.jmh.runner.options.OptionsBuilder; - -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.util.concurrent.TimeUnit; -import java.util.Random; - -/** - * Benchmark for encoding and decoding UKEY2 messages over the JNI, analogous to - * `ukey2_benches.rs`. The parameters and the operations also roughly matches the that of the Rust - * Criterion benchmark. That said, since the benchmark infrastructure is different, there will - * inevitably be differences the skews the number in certain ways – comparison of numbers from the - * different benchmarks should compared on order-of-magnitudes only. To get the JNI overhead, for - * example, it would be better use this JMH infra to measure a call into a no-op Rust function, - * which is a more apples-to-apples comparison. - * - * To run this benchmark, run - * cargo build -p ukey2_jni --release && ./gradlew jmh - */ -@State(Scope.Benchmark) -@OutputTimeUnit(TimeUnit.SECONDS) -@BenchmarkMode(Mode.Throughput) -public class Ukey2Benchmark { - - @State(Scope.Thread) - public static class ConnectionState { - D2DConnectionContextV1 connContext; - D2DConnectionContextV1 serverConnContext; - @Param({"10", "1024"}) - int sizeKibs; - byte[] inputBytes; - - @Setup - public void setup() throws Exception { - D2DHandshakeContext initiatorContext = - new D2DHandshakeContext(D2DHandshakeContext.Role.Initiator); - D2DHandshakeContext serverContext = - new D2DHandshakeContext(D2DHandshakeContext.Role.Responder); - serverContext.parseHandshakeMessage(initiatorContext.getNextHandshakeMessage()); - initiatorContext.parseHandshakeMessage(serverContext.getNextHandshakeMessage()); - serverContext.parseHandshakeMessage(initiatorContext.getNextHandshakeMessage()); - connContext = initiatorContext.toConnectionContext(); - serverConnContext = serverContext.toConnectionContext(); - Random random = new Random(); - inputBytes = new byte[sizeKibs * 1024]; - random.nextBytes(inputBytes); - } - } - - @Benchmark - @Fork(3) - @Warmup(iterations = 2, time = 500, timeUnit = TimeUnit.MILLISECONDS) - @Measurement(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) - public void encodeAndDecode(ConnectionState state, Blackhole blackhole) throws Exception { - byte[] encoded = state.connContext.encodeMessageToPeer(state.inputBytes, null); - byte[] decoded = state.serverConnContext.decodeMessageFromPeer(encoded, null); - blackhole.consume(decoded); - } -} diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/jmh/java/com/google/security/cryptauth/lib/securegcm/ukey2/Ukey2Benchmark.java b/nearby/connections/ukey2/ukey2_jni/java/src/jmh/java/com/google/security/cryptauth/lib/securegcm/ukey2/Ukey2Benchmark.java new file mode 100644 index 0000000..9cca229 --- /dev/null +++ b/nearby/connections/ukey2/ukey2_jni/java/src/jmh/java/com/google/security/cryptauth/lib/securegcm/ukey2/Ukey2Benchmark.java @@ -0,0 +1,78 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed 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 com.google.security.cryptauth.lib.securegcm.ukey2; + +import com.google.security.cryptauth.lib.securegcm.ukey2.D2DHandshakeContext.Role; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; + +import java.util.concurrent.TimeUnit; +import java.util.Random; + +/** + * Benchmark for encoding and decoding UKEY2 messages over the JNI, analogous to `ukey2_benches.rs`. + * The parameters and the operations also roughly matches the that of the Rust Criterion benchmark. + * That said, since the benchmark infrastructure is different, there will inevitably be differences + * the skews the number in certain ways – comparison of numbers from the different benchmarks should + * compared on order-of-magnitudes only. To get the JNI overhead, for example, it would be better + * use this JMH infra to measure a call into a no-op Rust function, which is a more apples-to-apples + * comparison. + * + * <p>To run this benchmark, run cargo build -p ukey2_jni --release && ./gradlew jmh + */ +@State(Scope.Benchmark) +@OutputTimeUnit(TimeUnit.SECONDS) +@BenchmarkMode(Mode.Throughput) +public class Ukey2Benchmark { + + @State(Scope.Thread) + public static class ConnectionState { + D2DConnectionContextV1 connContext; + D2DConnectionContextV1 serverConnContext; + + @Param({"10", "1024"}) + int sizeKibs; + + byte[] inputBytes; + + @Setup + public void setup() throws Exception { + D2DHandshakeContext initiatorContext = + new D2DHandshakeContext(Role.INITIATOR); + D2DHandshakeContext serverContext = + new D2DHandshakeContext(Role.RESPONDER); + serverContext.parseHandshakeMessage(initiatorContext.getNextHandshakeMessage()); + initiatorContext.parseHandshakeMessage(serverContext.getNextHandshakeMessage()); + serverContext.parseHandshakeMessage(initiatorContext.getNextHandshakeMessage()); + connContext = initiatorContext.toConnectionContext(); + serverConnContext = serverContext.toConnectionContext(); + Random random = new Random(); + inputBytes = new byte[sizeKibs * 1024]; + random.nextBytes(inputBytes); + } + } + + @Benchmark + @Fork(3) + @Warmup(iterations = 2, time = 500, timeUnit = TimeUnit.MILLISECONDS) + @Measurement(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) + public void encodeAndDecode(ConnectionState state, Blackhole blackhole) throws Exception { + byte[] encoded = state.connContext.encodeMessageToPeer(state.inputBytes, null); + byte[] decoded = state.serverConnContext.decodeMessageFromPeer(encoded, null); + blackhole.consume(decoded); + } +} diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextV1.java b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextV1.java deleted file mode 100644 index 7874cd9..0000000 --- a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextV1.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed 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 com.google.security.cryptauth.lib.securegcm; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class D2DConnectionContextV1 { - - static { - System.loadLibrary("ukey2_jni"); - } - - private static native byte[] encode_message_to_peer(long contextPtr, byte[] payload, byte[] associatedData) throws BadHandleException; - - private static native byte[] decode_message_from_peer(long contextPtr, byte[] message, byte[] associatedData) throws CryptoException; - - private static native byte[] get_session_unique(long contextPtr) throws BadHandleException; - - private static native int get_sequence_number_for_encoding(long contextPtr) throws BadHandleException; - - private static native int get_sequence_number_for_decoding(long contextPtr) throws BadHandleException; - - private static native byte[] save_session(long contextPtr) throws BadHandleException; - - private static native long from_saved_session(byte[] savedSessionInfo); - - private final long contextPtr; - - /** - * Java wrapper for D2DConnectionContextV1 to interact with the underlying Rust implementation - * - * @param contextPtr the handle to the Rust implementation. - */ - D2DConnectionContextV1(@Nonnull long contextPtr) { - this.contextPtr = contextPtr; - } - - /** - * Encode a message to the connection peer using session keys derived from the handshake. - * - * @param payload The message to be encrypted. - * @return The encrypted/encoded message. - */ - @Nonnull - public byte[] encodeMessageToPeer(@Nonnull byte[] payload, @Nullable byte[] associatedData) throws BadHandleException { - return encode_message_to_peer(contextPtr, payload, associatedData); - } - - /** - * Decodes/decrypts a message from the connection peer. - * - * @param message The message received over the connection. - * @return The decoded message from the connection peer. - */ - @Nonnull - public byte[] decodeMessageFromPeer(@Nonnull byte[] message, @Nullable byte[] associatedData) throws CryptoException { - return decode_message_from_peer(contextPtr, message, associatedData); - } - - /** - * A unique session identifier derived from session-specific information - * - * @return The session unique identifier - */ - @Nonnull - public byte[] getSessionUnique() throws BadHandleException { - return get_session_unique(contextPtr); - } - - /** - * Returns the encoding sequence number. - * - * @return the encoding sequence number. - */ - public int getSequenceNumberForEncoding() throws BadHandleException { - return get_sequence_number_for_encoding(contextPtr); - } - - /** - * Returns the decoding sequence number. - * - * @return the decoding sequence number. - */ - public int getSequenceNumberForDecoding() throws BadHandleException { - return get_sequence_number_for_decoding(contextPtr); - } - - /** - * Serializes the current session in a form usable by {@link D2DConnectionContextV1#fromSavedSession} - * - * @return a byte array representing the current session. - */ - @Nonnull - public byte[] saveSession() throws BadHandleException { - return save_session(contextPtr); - } - - /** - * Reconstructs and returns the session originally serialized by {@link D2DConnectionContextV1#saveSession} - * - * @param savedSessionInfo the byte array from saveSession() - * @return a D2DConnectionContextV1 session with the same properties as the context saved. - */ - public static D2DConnectionContextV1 fromSavedSession(@Nonnull byte[] savedSessionInfo) { - return new D2DConnectionContextV1(from_saved_session(savedSessionInfo)); - } - -} diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DHandshakeContext.java b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DHandshakeContext.java deleted file mode 100644 index 39f7aa9..0000000 --- a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DHandshakeContext.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed 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 com.google.security.cryptauth.lib.securegcm; - -import javax.annotation.Nonnull; - -public class D2DHandshakeContext { - static { - System.loadLibrary("ukey2_jni"); - } - - public enum Role { - INITIATOR, - RESPONDER, - } - - private final long contextPtr; - - private static native boolean is_handshake_complete(long contextPtr) throws BadHandleException; - - private static native long create_context(boolean isClient); - - private static native byte[] get_next_handshake_message(long contextPtr) throws BadHandleException; - - private static native void parse_handshake_message(long contextPtr, byte[] message) throws BadHandleException, HandshakeException; - - private static native byte[] get_verification_string(long contextPtr, int length) throws BadHandleException, HandshakeException; - - private static native long to_connection_context(long contextPtr) throws HandshakeException; - - public D2DHandshakeContext(@Nonnull Role role) { - this.contextPtr = create_context(role == Role.INITIATOR); - } - - /** - * Convenience constructor that creates a UKEY2 D2DHandshakeContext for the initiator role. - * - * @return a D2DHandshakeContext for the role of initiator in the handshake. - */ - public static D2DHandshakeContext forInitiator() { - return new D2DHandshakeContext(Role.INITIATOR); - } - - /** - * Convenience constructor that creates a UKEY2 D2DHandshakeContext for the initiator role. - * - * @return a D2DHandshakeContext for the role of responder/server in the handshake. - */ - public static D2DHandshakeContext forResponder() { - return new D2DHandshakeContext(Role.RESPONDER); - } - - /** - * Function that checks if the handshake is completed. - * - * @return true/false depending on if the handshake is complete. - */ - public boolean isHandshakeComplete() throws BadHandleException { - return is_handshake_complete(contextPtr); - } - - /** - * Gets the next handshake message in the exchange. - * - * @return handshake message encoded in a SecureMessage. - */ - @Nonnull - public byte[] getNextHandshakeMessage() throws BadHandleException { - return get_next_handshake_message(contextPtr); - } - - /** - * Parses the handshake message. - * - * @param message - handshake message from the other side. - */ - @Nonnull - public void parseHandshakeMessage(@Nonnull byte[] message) throws BadHandleException, HandshakeException { - parse_handshake_message(contextPtr, message); - } - - /** - * Returns an authentication string suitable for authenticating the handshake out-of-band. Note - * that the authentication string can be short (e.g., a 6 digit visual confirmation code). Note: - * this should only be called when {#isHandshakeComplete} returns true. - * This code is analogous to the authentication string described in the spec. - * - * @param length - The length of the returned verification string. - * @return - The returned verification string as a byte array. - * @throws BadHandleException - Thrown if the handle is no longer valid, for example after calling {@link D2DHandshakeContext#toConnectionContext} - * @throws HandshakeException - Thrown if the handshake is not complete when this function is called. - */ - @Nonnull - public byte[] getVerificationString(int length) throws BadHandleException, HandshakeException { - return get_verification_string(contextPtr, length); - } - - /** - * Function to create a secure communication channel from the handshake after confirming the auth string generated by - * the handshake out-of-band (i.e. via a user-facing UI). - * - * @return a new {@link D2DConnectionContextV1} with the next protocol specified when creating the D2DHandshakeContext. - * @throws HandshakeException if the handsshake is not complete when this function is called. - */ - public D2DConnectionContextV1 toConnectionContext() throws HandshakeException { - return new D2DConnectionContextV1(to_connection_context(contextPtr)); - } -} diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/BadHandleException.java b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException.java index 2efd7c4..78f0e5e 100644 --- a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/BadHandleException.java +++ b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException.java @@ -12,10 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -package com.google.security.cryptauth.lib.securegcm; +package com.google.security.cryptauth.lib.securegcm.ukey2; /** - * Represents an unrecoverable error (invalid handle) that has occurred during the handshake/connection. + * Represents an unrecoverable error (invalid handle) that has occurred during the + * handshake/connection. */ public class BadHandleException extends Exception { public BadHandleException(String message) { @@ -29,4 +30,4 @@ public class BadHandleException extends Exception { public BadHandleException(String message, Exception e) { super(message, e); } -}
\ No newline at end of file +} diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/CryptoException.java b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/CryptoException.java index 6abeb53..11b5c9b 100644 --- a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/CryptoException.java +++ b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/CryptoException.java @@ -12,11 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -package com.google.security.cryptauth.lib.securegcm; +package com.google.security.cryptauth.lib.securegcm.ukey2; -/** - * Represents an unrecoverable error that has occurred during the handshake procedure. - */ +/** Represents an unrecoverable error that has occurred during the handshake procedure. */ public class CryptoException extends Exception { public CryptoException(String message) { super(message); @@ -29,4 +27,4 @@ public class CryptoException extends Exception { public CryptoException(String message, Exception e) { super(message, e); } -}
\ No newline at end of file +} diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/D2DConnectionContextV1.java b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/D2DConnectionContextV1.java new file mode 100644 index 0000000..9ce2517 --- /dev/null +++ b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/D2DConnectionContextV1.java @@ -0,0 +1,139 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed 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 com.google.security.cryptauth.lib.securegcm.ukey2; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class D2DConnectionContextV1 { + + static { + var path = System.getProperty("java.library.path"); + + if (path == null) { + throw new RuntimeException("Path isn't set."); + } + + var paths = java.util.List.of(path.split(";")); + paths.forEach(System.out::println); + + System.loadLibrary("ukey2_jni"); + } + + private static native byte[] encode_message_to_peer( + long contextPtr, byte[] payload, byte[] associatedData) throws BadHandleException; + + private static native byte[] decode_message_from_peer( + long contextPtr, byte[] message, byte[] associatedData) throws CryptoException; + + private static native byte[] get_session_unique(long contextPtr) throws BadHandleException; + + private static native int get_sequence_number_for_encoding(long contextPtr) + throws BadHandleException; + + private static native int get_sequence_number_for_decoding(long contextPtr) + throws BadHandleException; + + private static native byte[] save_session(long contextPtr) throws BadHandleException; + + private static native long from_saved_session(byte[] savedSessionInfo); + + private final long contextPtr; + + /** + * Java wrapper for D2DConnectionContextV1 to interact with the underlying Rust implementation + * + * @param contextPtr the handle to the Rust implementation. + */ + D2DConnectionContextV1(@Nonnull long contextPtr) { + this.contextPtr = contextPtr; + } + + /** + * Encode a message to the connection peer using session keys derived from the handshake. + * + * @param payload The message to be encrypted. + * @return The encrypted/encoded message. + */ + @Nonnull + public byte[] encodeMessageToPeer(@Nonnull byte[] payload, @Nullable byte[] associatedData) + throws BadHandleException { + return encode_message_to_peer(contextPtr, payload, associatedData); + } + + /** + * Decodes/decrypts a message from the connection peer. + * + * @param message The message received over the connection. + * @return The decoded message from the connection peer. + */ + @Nonnull + public byte[] decodeMessageFromPeer(@Nonnull byte[] message, @Nullable byte[] associatedData) + throws CryptoException { + return decode_message_from_peer(contextPtr, message, associatedData); + } + + /** + * A unique session identifier derived from session-specific information + * + * @return The session unique identifier + */ + @Nonnull + public byte[] getSessionUnique() throws BadHandleException { + return get_session_unique(contextPtr); + } + + /** + * Returns the encoding sequence number. + * + * @return the encoding sequence number. + */ + public int getSequenceNumberForEncoding() throws BadHandleException { + return get_sequence_number_for_encoding(contextPtr); + } + + /** + * Returns the decoding sequence number. + * + * @return the decoding sequence number. + */ + public int getSequenceNumberForDecoding() throws BadHandleException { + return get_sequence_number_for_decoding(contextPtr); + } + + /** + * Serializes the current session in a form usable by {@link + * D2DConnectionContextV1#fromSavedSession} + * + * @return a byte array representing the current session. + */ + @Nonnull + public byte[] saveSession() throws BadHandleException { + return save_session(contextPtr); + } + + /** + * Reconstructs and returns the session originally serialized by {@link + * D2DConnectionContextV1#saveSession} + * + * @param savedSessionInfo the byte array from saveSession() + * @return a D2DConnectionContextV1 session with the same properties as the context saved. + */ + public static D2DConnectionContextV1 fromSavedSession(@Nonnull byte[] savedSessionInfo) { + return new D2DConnectionContextV1(from_saved_session(savedSessionInfo)); + } +} diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/D2DHandshakeContext.java b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/D2DHandshakeContext.java new file mode 100644 index 0000000..429295e --- /dev/null +++ b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/D2DHandshakeContext.java @@ -0,0 +1,129 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed 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 com.google.security.cryptauth.lib.securegcm.ukey2; + +import javax.annotation.Nonnull; + +public class D2DHandshakeContext { + static { + System.loadLibrary("ukey2_jni"); + } + + public enum Role { + INITIATOR, + RESPONDER, + } + + private final long contextPtr; + + private static native boolean is_handshake_complete(long contextPtr) throws BadHandleException; + + private static native long create_context(boolean isClient); + + private static native byte[] get_next_handshake_message(long contextPtr) + throws BadHandleException; + + private static native void parse_handshake_message(long contextPtr, byte[] message) + throws BadHandleException, HandshakeException; + + private static native byte[] get_verification_string(long contextPtr, int length) + throws BadHandleException, HandshakeException; + + private static native long to_connection_context(long contextPtr) throws HandshakeException; + + public D2DHandshakeContext(@Nonnull Role role) { + this.contextPtr = create_context(role == Role.INITIATOR); + } + + /** + * Convenience constructor that creates a UKEY2 D2DHandshakeContext for the initiator role. + * + * @return a D2DHandshakeContext for the role of initiator in the handshake. + */ + public static D2DHandshakeContext forInitiator() { + return new D2DHandshakeContext(Role.INITIATOR); + } + + /** + * Convenience constructor that creates a UKEY2 D2DHandshakeContext for the initiator role. + * + * @return a D2DHandshakeContext for the role of responder/server in the handshake. + */ + public static D2DHandshakeContext forResponder() { + return new D2DHandshakeContext(Role.RESPONDER); + } + + /** + * Function that checks if the handshake is completed. + * + * @return true/false depending on if the handshake is complete. + */ + public boolean isHandshakeComplete() throws BadHandleException { + return is_handshake_complete(contextPtr); + } + + /** + * Gets the next handshake message in the exchange. + * + * @return handshake message encoded in a SecureMessage. + */ + @Nonnull + public byte[] getNextHandshakeMessage() throws BadHandleException { + return get_next_handshake_message(contextPtr); + } + + /** + * Parses the handshake message. + * + * @param message - handshake message from the other side. + */ + @Nonnull + public void parseHandshakeMessage(@Nonnull byte[] message) + throws BadHandleException, HandshakeException { + parse_handshake_message(contextPtr, message); + } + + /** + * Returns an authentication string suitable for authenticating the handshake out-of-band. Note + * that the authentication string can be short (e.g., a 6 digit visual confirmation code). Note: + * this should only be called when {#isHandshakeComplete} returns true. This code is analogous to + * the authentication string described in the spec. + * + * @param length - The length of the returned verification string. + * @return - The returned verification string as a byte array. + * @throws BadHandleException - Thrown if the handle is no longer valid, for example after calling + * {@link D2DHandshakeContext#toConnectionContext} + * @throws HandshakeException - Thrown if the handshake is not complete when this function is + * called. + */ + @Nonnull + public byte[] getVerificationString(int length) throws BadHandleException, HandshakeException { + return get_verification_string(contextPtr, length); + } + + /** + * Function to create a secure communication channel from the handshake after confirming the auth + * string generated by the handshake out-of-band (i.e. via a user-facing UI). + * + * @return a new {@link D2DConnectionContextV1} with the next protocol specified when creating the + * D2DHandshakeContext. + * @throws HandshakeException if the handsshake is not complete when this function is called. + */ + public D2DConnectionContextV1 toConnectionContext() throws HandshakeException { + return new D2DConnectionContextV1(to_connection_context(contextPtr)); + } +} diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/HandshakeException.java b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/HandshakeException.java index 17928e9..20d3112 100644 --- a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/HandshakeException.java +++ b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/HandshakeException.java @@ -12,21 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -package com.google.security.cryptauth.lib.securegcm; +package com.google.security.cryptauth.lib.securegcm.ukey2; -/** - * Represents an unrecoverable error that has occurred during the handshake procedure. - */ +/** Represents an unrecoverable error that has occurred during the handshake procedure. */ public class HandshakeException extends Exception { - public HandshakeException(String message) { - super(message); - } + public HandshakeException(String message) { + super(message); + } - public HandshakeException(Exception e) { - super(e); - } + public HandshakeException(Exception e) { + super(e); + } - public HandshakeException(String message, Exception e) { - super(message, e); - } -}
\ No newline at end of file + public HandshakeException(String message, Exception e) { + super(message, e); + } +} diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/SessionRestoreException.java b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/SessionRestoreException.java index c780973..026f8c5 100644 --- a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/SessionRestoreException.java +++ b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/SessionRestoreException.java @@ -12,11 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -package com.google.security.cryptauth.lib.securegcm; +package com.google.security.cryptauth.lib.securegcm.ukey2; -/** - * Represents an unrecoverable error that has occurred during the handshake procedure. - */ +/** Represents an unrecoverable error that has occurred during the handshake procedure. */ public class SessionRestoreException extends Exception { public SessionRestoreException(String message) { super(message); @@ -29,4 +27,4 @@ public class SessionRestoreException extends Exception { public SessionRestoreException(String message, Exception e) { super(message, e); } -}
\ No newline at end of file +} diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/test/java/com/google/security/cryptauth/lib/securegcm/TestUkey2Protocol.kt b/nearby/connections/ukey2/ukey2_jni/java/src/test/java/com/google/security/cryptauth/lib/securegcm/TestUkey2Protocol.kt deleted file mode 100644 index 79cbd15..0000000 --- a/nearby/connections/ukey2/ukey2_jni/java/src/test/java/com/google/security/cryptauth/lib/securegcm/TestUkey2Protocol.kt +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed 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. - */ - -/* - * This Java source file was generated by the Gradle 'init' task. - */ -package com.google.security.cryptauth.lib.securegcm - -import java.nio.charset.StandardCharsets -import org.junit.jupiter.api.Assertions.assertArrayEquals -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertFalse -import org.junit.jupiter.api.Assertions.assertNotEquals -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertDoesNotThrow -import org.junit.jupiter.api.assertThrows - -// Driver code -// Tests exception handling and the handshake routine, as well as encrypting/decrypting short message between the server and initiator contexts. -@Suppress("UNUSED_VARIABLE") -class TestUkey2Protocol { - @Test - fun testHandshake() { - val initiatorContext = - D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR) - assertFalse(initiatorContext.isHandshakeComplete) - val serverContext = - D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER) - assertFalse(serverContext.isHandshakeComplete) - assertDoesNotThrow { - serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) - initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage) - serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) - assertTrue(initiatorContext.isHandshakeComplete) - assertTrue(serverContext.isHandshakeComplete) - } - } - - @Test - fun testSendReceiveMessage() { - val initiatorContext = - D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR) - val serverContext = - D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER) - assertDoesNotThrow { - serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) - initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage) - serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) - val connContext = initiatorContext.toConnectionContext() - val serverConnContext = serverContext.toConnectionContext() - val initialShareString = "Nearby sharing to server" - val encoded = connContext.encodeMessageToPeer( - initialShareString.toByteArray( - StandardCharsets.UTF_8 - ), null - ) - val response = - String(serverConnContext.decodeMessageFromPeer(encoded, null), StandardCharsets.UTF_8) - assertEquals(response, initialShareString) - } - } - - @Test - fun testSaveRestoreSession() { - val initiatorContext = - D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR) - val serverContext = - D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER) - assertDoesNotThrow { - serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) - initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage) - serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) - val connContext = initiatorContext.toConnectionContext() - val serverConnContext = serverContext.toConnectionContext() - val initiatorSavedSession = connContext.saveSession() - val restored = D2DConnectionContextV1.fromSavedSession(initiatorSavedSession) - assertArrayEquals(connContext.sessionUnique, restored.sessionUnique) - val initialShareString = "Nearby sharing to server" - val encoded = serverConnContext.encodeMessageToPeer( - initialShareString.toByteArray( - StandardCharsets.UTF_8 - ), null - ) - val response = String(restored.decodeMessageFromPeer(encoded, null), StandardCharsets.UTF_8) - assertEquals(response, initialShareString) - } - } - - @Test - fun testSaveRestoreBadSession() { - val initiatorContext = - D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR) - val serverContext = - D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER) - val deriveInitiatorSavedSession = { - assertDoesNotThrow { - serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) - initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage) - serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) - val connContext = initiatorContext.toConnectionContext() - val serverConnContext = serverContext.toConnectionContext() - connContext.saveSession() - } - } - assertThrows<SessionRestoreException> { - val unused = D2DConnectionContextV1.fromSavedSession(deriveInitiatorSavedSession().copyOfRange(0, 20)) - } - } - - @Test - fun tryReuseHandshakeContext() { - val initiatorContext = - D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR) - val serverContext = - D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER) - assertDoesNotThrow { - serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) - initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage) - serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) - val connContext = initiatorContext.toConnectionContext() - val serverConnContext = serverContext.toConnectionContext() - } - assertThrows<BadHandleException> { - val unused = serverContext.nextHandshakeMessage - } - } - - @Test - fun testSendReceiveMessageWithAssociatedData() { - val initiatorContext = - D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR) - val serverContext = - D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER) - val associatedData = "Associated data.".toByteArray() - assertDoesNotThrow { - serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) - initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage) - serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) - val connContext = initiatorContext.toConnectionContext() - val serverConnContext = serverContext.toConnectionContext() - val initialShareString = "Nearby sharing to server" - val encoded = connContext.encodeMessageToPeer( - initialShareString.toByteArray( - StandardCharsets.UTF_8 - ), associatedData - ) - val response = - String(serverConnContext.decodeMessageFromPeer(encoded, associatedData), StandardCharsets.UTF_8) - assertEquals(response, initialShareString) - } - } - - @Test - fun testVerificationString() { - val initiatorContext = - D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR) - val serverContext = - D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER) - assertDoesNotThrow { - serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) - initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage) - serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) - } - assert(serverContext.isHandshakeComplete) - assert(initiatorContext.isHandshakeComplete) - assertArrayEquals(serverContext.getVerificationString(32), initiatorContext.getVerificationString(32)) - } -}
\ No newline at end of file diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/test/java/com/google/security/cryptauth/lib/securegcm/ukey2/TestUkey2Protocol.kt b/nearby/connections/ukey2/ukey2_jni/java/src/test/java/com/google/security/cryptauth/lib/securegcm/ukey2/TestUkey2Protocol.kt new file mode 100644 index 0000000..f770977 --- /dev/null +++ b/nearby/connections/ukey2/ukey2_jni/java/src/test/java/com/google/security/cryptauth/lib/securegcm/ukey2/TestUkey2Protocol.kt @@ -0,0 +1,167 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed 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. + */ + +/* + * This Java source file was generated by the Gradle 'init' task. + */ +package com.google.security.cryptauth.lib.securegcm.ukey2 + +import java.nio.charset.StandardCharsets +import org.junit.jupiter.api.Assertions.assertArrayEquals +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertDoesNotThrow +import org.junit.jupiter.api.assertThrows + +// Driver code +// Tests exception handling and the handshake routine, as well as encrypting/decrypting short +// message between the server and initiator contexts. +@Suppress("UNUSED_VARIABLE") +class TestUkey2Protocol { + @Test + fun testHandshake() { + val initiatorContext = D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR) + println("got initial context") + assertFalse(initiatorContext.isHandshakeComplete) + val serverContext = D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER) + assertFalse(serverContext.isHandshakeComplete) + assertDoesNotThrow { + serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) + initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage) + serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) + assertTrue(initiatorContext.isHandshakeComplete) + assertTrue(serverContext.isHandshakeComplete) + } + } + + @Test + fun testSendReceiveMessage() { + val initiatorContext = D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR) + val serverContext = D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER) + assertDoesNotThrow { + serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) + initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage) + serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) + val connContext = initiatorContext.toConnectionContext() + val serverConnContext = serverContext.toConnectionContext() + val initialShareString = "Nearby sharing to server" + val encoded = + connContext.encodeMessageToPeer( + initialShareString.toByteArray(StandardCharsets.UTF_8), null) + val response = + String(serverConnContext.decodeMessageFromPeer(encoded, null), StandardCharsets.UTF_8) + assertEquals(response, initialShareString) + } + } + + @Test + fun testSaveRestoreSession() { + val initiatorContext = D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR) + val serverContext = D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER) + assertDoesNotThrow { + serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) + initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage) + serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) + val connContext = initiatorContext.toConnectionContext() + val serverConnContext = serverContext.toConnectionContext() + val initiatorSavedSession = connContext.saveSession() + val restored = D2DConnectionContextV1.fromSavedSession(initiatorSavedSession) + assertArrayEquals(connContext.sessionUnique, restored.sessionUnique) + val initialShareString = "Nearby sharing to server" + val encoded = + serverConnContext.encodeMessageToPeer( + initialShareString.toByteArray(StandardCharsets.UTF_8), null) + val response = String(restored.decodeMessageFromPeer(encoded, null), StandardCharsets.UTF_8) + assertEquals(response, initialShareString) + } + } + + @Test + fun testSaveRestoreBadSession() { + val initiatorContext = D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR) + val serverContext = D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER) + val deriveInitiatorSavedSession = { + assertDoesNotThrow { + serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) + initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage) + serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) + val connContext = initiatorContext.toConnectionContext() + val serverConnContext = serverContext.toConnectionContext() + connContext.saveSession() + } + } + assertThrows<SessionRestoreException> { + val unused = + D2DConnectionContextV1.fromSavedSession(deriveInitiatorSavedSession().copyOfRange(0, 20)) + } + } + + @Test + fun tryReuseHandshakeContext() { + val initiatorContext = D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR) + val serverContext = D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER) + assertDoesNotThrow { + serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) + initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage) + serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) + val connContext = initiatorContext.toConnectionContext() + val serverConnContext = serverContext.toConnectionContext() + } + assertThrows<BadHandleException> { + val unused = serverContext.nextHandshakeMessage + } + } + + @Test + fun testSendReceiveMessageWithAssociatedData() { + val initiatorContext = D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR) + val serverContext = D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER) + val associatedData = "Associated data.".toByteArray() + assertDoesNotThrow { + serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) + initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage) + serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) + val connContext = initiatorContext.toConnectionContext() + val serverConnContext = serverContext.toConnectionContext() + val initialShareString = "Nearby sharing to server" + val encoded = + connContext.encodeMessageToPeer( + initialShareString.toByteArray(StandardCharsets.UTF_8), associatedData) + val response = + String( + serverConnContext.decodeMessageFromPeer(encoded, associatedData), + StandardCharsets.UTF_8) + assertEquals(response, initialShareString) + } + } + + @Test + fun testVerificationString() { + val initiatorContext = D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR) + val serverContext = D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER) + assertDoesNotThrow { + serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) + initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage) + serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage) + } + assert(serverContext.isHandshakeComplete) + assert(initiatorContext.isHandshakeComplete) + assertArrayEquals( + serverContext.getVerificationString(32), initiatorContext.getVerificationString(32)) + } +} |