summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-08 16:02:02 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-08 16:02:02 +0000
commit01f299c0fecd6d81825755f6f33ba8535720f3e7 (patch)
tree7e00d65b458ba1d02ad2a4552a241baf89fbb895
parent8b1d1a35cd0dfcc45d2113f0e1c18488d8784516 (diff)
parentcd9c844bd676ff9ff69a74bc49e157149a7f0918 (diff)
downloadukey2-android12-mainline-tzdata2-release.tar.gz
Change-Id: I02bead3409dc2824a92b88f874dd1f264d9a91f6
-rw-r--r--.gitignore2
-rw-r--r--.gitmodules16
-rw-r--r--Android.bp19
-rw-r--r--CMakeLists.txt50
-rw-r--r--METADATA7
l---------NOTICE1
-rw-r--r--README.md65
-rw-r--r--build.gradle46
-rw-r--r--cmake/local_build_protobuf.cmake42
-rw-r--r--cmake/local_build_setup.cmake26
-rw-r--r--cmake/proto_defs.cmake42
-rw-r--r--src/.gradle/3.2.1/taskArtifacts/fileSnapshots.binbin0 -> 18577 bytes
-rw-r--r--src/.gradle/3.2.1/taskArtifacts/taskArtifacts.binbin0 -> 18786 bytes
-rw-r--r--src/.gradle/3.2.1/taskArtifacts/taskArtifacts.lockbin0 -> 17 bytes
-rw-r--r--src/main/CMakeLists.txt18
-rw-r--r--src/main/cpp/CMakeLists.txt17
-rw-r--r--src/main/cpp/include/securegcm/d2d_connection_context_v1.h89
-rw-r--r--src/main/cpp/include/securegcm/d2d_crypto_ops.h78
-rw-r--r--src/main/cpp/include/securegcm/java_util.h57
-rw-r--r--src/main/cpp/include/securegcm/ukey2_handshake.h263
-rw-r--r--src/main/cpp/src/securegcm/CMakeLists.txt47
-rw-r--r--src/main/cpp/src/securegcm/d2d_connection_context_v1.cc228
-rw-r--r--src/main/cpp/src/securegcm/d2d_crypto_ops.cc151
-rw-r--r--src/main/cpp/src/securegcm/java_util.cc60
-rw-r--r--src/main/cpp/src/securegcm/ukey2_handshake.cc715
-rw-r--r--src/main/cpp/src/securegcm/ukey2_shell.cc297
-rw-r--r--src/main/cpp/test/securegcm/CMakeLists.txt31
-rw-r--r--src/main/cpp/test/securegcm/d2d_connection_context_v1_test.cc124
-rw-r--r--src/main/cpp/test/securegcm/d2d_crypto_ops_test.cc158
-rw-r--r--src/main/cpp/test/securegcm/java_util_test.cc84
-rw-r--r--src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContext.java28
-rw-r--r--src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextV0.java28
-rw-r--r--src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextV1.java28
-rw-r--r--src/main/java/com/google/security/cryptauth/lib/securegcm/D2DCryptoOps.java35
-rw-r--r--src/main/java/com/google/security/cryptauth/lib/securegcm/D2DDiffieHellmanKeyExchangeHandshake.java45
-rw-r--r--src/main/java/com/google/security/cryptauth/lib/securegcm/D2DHandshakeContext.java28
-rw-r--r--src/main/java/com/google/security/cryptauth/lib/securegcm/D2DSpakeEd25519Handshake.java648
-rw-r--r--src/main/java/com/google/security/cryptauth/lib/securegcm/Ed25519.java45
-rw-r--r--src/main/java/com/google/security/cryptauth/lib/securegcm/EnrollmentCryptoOps.java28
-rw-r--r--src/main/java/com/google/security/cryptauth/lib/securegcm/HandshakeException.java28
-rw-r--r--src/main/java/com/google/security/cryptauth/lib/securegcm/KeyEncoding.java37
-rw-r--r--src/main/java/com/google/security/cryptauth/lib/securegcm/SecureGcmConstants.java28
-rw-r--r--src/main/java/com/google/security/cryptauth/lib/securegcm/TransportCryptoOps.java28
-rw-r--r--src/main/java/com/google/security/cryptauth/lib/securegcm/Ukey2Handshake.java28
-rw-r--r--src/main/java/com/google/security/cryptauth/lib/securemessage/CryptoOps.java107
-rw-r--r--src/main/java/com/google/security/cryptauth/lib/securemessage/PublicKeyProtoUtil.java60
-rw-r--r--src/main/java/com/google/security/cryptauth/lib/securemessage/SecureMessageBuilder.java28
-rw-r--r--src/main/java/com/google/security/cryptauth/lib/securemessage/SecureMessageParser.java28
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextTest.java568
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securegcm/D2DDiffieHellmanKeyExchangeHandshakeTest.java432
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ed25519Test.java195
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securegcm/EnrollmentCryptoOpsTest.java134
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securegcm/KeyEncodingTest.java189
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securegcm/TransportCryptoOpsTest.java110
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2CppCompatibilityTest.java124
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2HandshakeTest.java818
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2ShellCppWrapper.java342
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securemessage/CryptoOpsTest.java172
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securemessage/NullsGoogle3Test.java42
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securemessage/PublicKeyProtoUtilTest.java412
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securemessage/SecureMessageSimpleTestVectorTest.java403
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securemessage/SecureMessageTest.java766
-rw-r--r--src/main/proto/CMakeLists.txt32
-rw-r--r--src/main/proto/device_to_device_messages.proto38
-rw-r--r--src/main/proto/passwordless_auth_payloads.proto31
-rw-r--r--src/main/proto/proximity_payloads.proto43
-rw-r--r--src/main/proto/securegcm.proto57
-rw-r--r--src/main/proto/securemessage.proto32
-rw-r--r--src/main/proto/ukey.proto47
m---------third_party/absl0
m---------third_party/gtest0
m---------third_party/protobuf0
m---------third_party/secure_message0
73 files changed, 1056 insertions, 7949 deletions
diff --git a/.gitignore b/.gitignore
index f53951a..9d4d4f8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,3 @@
build/**
.gradle/**
-.idea/**
+
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 2624f19..0000000
--- a/.gitmodules
+++ /dev/null
@@ -1,16 +0,0 @@
-[submodule "third_party/secure_message"]
- path = third_party/secure_message
- url = https://github.com/google/securemessage
- branch = master
-[submodule "third_party/gtest"]
- path = third_party/gtest
- url = https://github.com/google/googletest
- branch = master
-[submodule "third_party/protobuf"]
- path = third_party/protobuf
- url = https://github.com/protocolbuffers/protobuf
- branch = master
-[submodule "third_party/absl"]
- path = third_party/absl
- url = https://github.com/abseil/abseil-cpp
- branch = master
diff --git a/Android.bp b/Android.bp
index 1376405..b99ed63 100644
--- a/Android.bp
+++ b/Android.bp
@@ -15,23 +15,6 @@
// Android build file for building the library in AOSP
// https://android.googlesource.com/platform/external/ukey2
-package {
- default_applicable_licenses: ["external_ukey2_license"],
-}
-
-// Added automatically by a large-scale-change
-// http://go/android-license-faq
-license {
- name: "external_ukey2_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- ],
- license_text: [
- "LICENSE",
- ],
-}
-
java_library {
name: "ukey2",
proto: {
@@ -40,7 +23,7 @@ java_library {
},
srcs: [
"**/*.proto",
- "src/main/java/**/*.java",
+ "**/*.java",
],
libs: [
"guava",
diff --git a/CMakeLists.txt b/CMakeLists.txt
deleted file mode 100644
index 02ddda2..0000000
--- a/CMakeLists.txt
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright 2020 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
-#
-# https://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.
-
-cmake_minimum_required(VERSION 3.0.2)
-
-project(ukey2)
-
-option(ukey2_USE_LOCAL_PROTOBUF
- "Use local copy of protobuf library and compiler" OFF)
-
-option(ukey2_USE_LOCAL_ABSL
- "Use local copy of abseil-cpp library" OFF)
-
-include(cmake/proto_defs.cmake)
-include(cmake/local_build_setup.cmake)
-
-if (ukey2_USE_LOCAL_PROTOBUF)
- include(cmake/local_build_protobuf.cmake)
-endif()
-
-if (ukey2_USE_LOCAL_ABSL)
- if (NOT TARGET absl::base)
- add_subdirectory(third_party/absl)
- endif()
-else()
- find_package(absl REQUIRED)
-endif()
-
-find_package(Protobuf REQUIRED)
-
-enable_testing()
-
-add_subdirectory(src/main)
-if (NOT TARGET securemessage)
-add_subdirectory(third_party/secure_message)
-endif()
-if (NOT TARGET gtest)
-add_subdirectory(third_party/gtest)
-endif()
diff --git a/METADATA b/METADATA
index d08b2ea..1548a33 100644
--- a/METADATA
+++ b/METADATA
@@ -5,9 +5,8 @@ description:
third_party {
url {
type: ARCHIVE
- value: "https://github.com/google/ukey2.git"
+ value: "https://user.git.corp.google.com/michalp/ukey2/"
}
- version: "0275885d8e6038c39b8a8ca55e75d1d4d1727f47"
- license_type: NOTICE
- last_upgrade_date { year: 2021 month: 2 day: 8 }
+ version: "1.0"
+ last_upgrade_date { year: 2018 month: 12 day: 28 }
}
diff --git a/NOTICE b/NOTICE
new file mode 120000
index 0000000..7a694c9
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1 @@
+LICENSE \ No newline at end of file
diff --git a/README.md b/README.md
index 87a652d..2f56370 100644
--- a/README.md
+++ b/README.md
@@ -3,9 +3,7 @@ This is not an officially supported Google product
**Coathored by:** Alexei Czeskis, Thai Duong, Eduardo' Vela'' \<Nava\>, and Adam Stubblefield.
-**Status:**
-Implemented in Java by Alexei Czeskis (aczeskis@google.com)
-Ported from Java to C++ by Tim Song (tengs@google.com)
+**Status:** Implemented in Java (aczeskis@google.com)
**Design reviewers:** Thai Duong, Bruno Blanchet, Martin Abadi, and Bo Wang
@@ -329,64 +327,3 @@ size of the messages were:
|`ClientFinished`| 79 |
-# Checking out source code
-
-```
-git clone https://github.com/google/ukey2
-cd ukey2
-git submodule update --init --recursive
-```
-
-# Building and tesging C++ code
-
-## Build
-```
-cd <source root>
-mkdir build; cd build
-cmake -Dukey2_USE_LOCAL_PROTOBUF=ON -Dukey2_USE_LOCAL_ABSL=ON ..
-make
-```
-## Running C++ tests
-```
-cd <source root>/build
-ctest -V
-```
-
-# Buillding Java library and running Java Tests
-
-NOTE: c++ build must be completed as described above, before running java tests.
-This requirement exists because Java build runs a c++/java compatibility test, and
-this test depends on c++ test helper binary (found in build/src/main/cpp/test/securegcm/ukey2_test).
-Gradle build does not know how to build this artifact. Java test uses a relative
-path to the artifact, and expects tests to be run from <source root> as follows:
-
-Pre-reqs: gradle
-
-1. Create gradle wrapper for a specific gradle version.
-This project was built with Gradle-6.1.1.
-If you have an incompatible version of gradle it is recommended that
-you setup gradle wrapper first.
-1.1. The simplest is to run
-```
-cd <source root>
-gradle wrapper --gradle-version=6.1.1
-
-```
-
-1.2. If this fails, this is likely because current gradle version is unable to parse the build.gradle
-file. In this case, create an empty directory outside your project tree, and create a wrapper there.
-```
-mkdir -p $HOME/scratch/gradle-wrapper-611
-cd $HOME/scratch/gradle-wrapper-611
-gradle wrapper --gradle-version=6.1.1
-cp -a gradle gradlew gradlew.bat <source root>
-```
-
-2. Once you get gradle wrapper installed, run test command
-
-```
-cd <source root>
-./gradlew test -i
-```
-
-This will build and execute all the tests.
diff --git a/build.gradle b/build.gradle
index e319422..bb46e75 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,59 +1,23 @@
-// Copyright 2020 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
-//
-// https://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.
-
apply plugin: 'java'
apply plugin: 'com.google.protobuf'
repositories {
- mavenCentral()
+ jcenter()
}
buildscript {
repositories {
- mavenCentral()
+ jcenter()
}
dependencies {
- classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.12'
+ classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.7'
}
}
dependencies {
- compile group: 'com.google.truth.extensions', name: 'truth-java8-extension', version: '0.41'
- testCompile group: 'com.google.guava', name: 'guava-testlib', version: '29.0-jre'
- testImplementation 'junit:junit:4.13'
compile "com.google.code.findbugs:jsr305:3.0.0"
- compile "com.google.protobuf:protobuf-java:3.8.0"
- compile "com.google.guava:guava:19.0"
-}
-
-sourceSets {
- main {
- java {
- srcDir 'src/main/java'
- srcDir 'build/generated/source/proto/main/java'
- }
- }
- test {
- java {
- srcDir 'src/main/javatest'
- srcDir 'build/generated/source/proto/main/java'
- }
- }
+ compile "com.google.protobuf:protobuf-java:3.4.0"
+ compile "com.google.guava:guava:r05"
}
-test {
- useJUnit()
- maxHeapSize = '1G'
-}
diff --git a/cmake/local_build_protobuf.cmake b/cmake/local_build_protobuf.cmake
deleted file mode 100644
index 3a04d55..0000000
--- a/cmake/local_build_protobuf.cmake
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2020 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
-#
-# https://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.
-
-if (NOT EXISTS ${TOOLS_INSTALL_PREFIX}/bin/protoc)
- set(PKG_BUILD_ROOT ${TOOLS_BUILD_ROOT}/protobuf)
- set(PKG_SRC_ROOT ${CMAKE_SOURCE_DIR}/third_party/protobuf)
- execute_process(
- COMMAND mkdir -p ${PKG_BUILD_ROOT}
- )
- execute_process(
- COMMAND cmake ${PKG_SRC_ROOT}/cmake
- WORKING_DIRECTORY ${PKG_BUILD_ROOT}
- )
- execute_process(
- COMMAND make -j${N_CPUS}
- WORKING_DIRECTORY ${PKG_BUILD_ROOT}
- )
- execute_process(
- COMMAND make check
- WORKING_DIRECTORY ${PKG_BUILD_ROOT}
- RESULT_VARIABLE test_exit_code
- ERROR_QUIET
- )
- if (NOT ${test_exit_code} EQUAL "0")
- message(FATAL_ERROR "Protobuf tests failed; can't use this protobuf")
- endif()
- execute_process(
- COMMAND /bin/bash -c "DESTDIR=${TOOLS_INSTALL_ROOT} make install"
- WORKING_DIRECTORY ${PKG_BUILD_ROOT}
- )
-endif()
diff --git a/cmake/local_build_setup.cmake b/cmake/local_build_setup.cmake
deleted file mode 100644
index a7917fe..0000000
--- a/cmake/local_build_setup.cmake
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright 2020 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
-#
-# https://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.
-
-include(ProcessorCount)
-ProcessorCount(N_CPUS)
-
-if (N_CPUS EQUAL 0)
- set (N_CPUS 1)
-endif()
-
-set (TOOLS_ROOT ${CMAKE_BINARY_DIR}/stage)
-set (TOOLS_BUILD_ROOT ${TOOLS_ROOT}/build)
-set (TOOLS_INSTALL_ROOT ${TOOLS_ROOT}/install)
-set (TOOLS_INSTALL_PREFIX ${TOOLS_INSTALL_ROOT}/usr/local)
-set (CMAKE_FIND_ROOT_PATH ${TOOLS_INSTALL_ROOT})
diff --git a/cmake/proto_defs.cmake b/cmake/proto_defs.cmake
deleted file mode 100644
index aae0ce9..0000000
--- a/cmake/proto_defs.cmake
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2020 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
-#
-# https://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.
-
-function(add_cc_proto_library NAME)
- set(single)
- set(multi_args PROTOS INCS DEPS)
- cmake_parse_arguments(PARSE_ARGV 1 args "" "${single}" "${multi_args}")
-
- protobuf_generate(
- PROTOS ${args_PROTOS}
- LANGUAGE cpp
- OUT_VAR ${NAME}_var
- )
-
- add_library(${NAME}
- ${${NAME}_var}
- )
-
- target_link_libraries(${NAME}
- PUBLIC
- ${Protobuf_LIBRARIES}
- ${args_DEPS}
- )
-
- target_include_directories(${NAME}
- PUBLIC
- ${Protobuf_INCLUDE_DIRS}
- ${args_INCS}
- ${CMAKE_CURRENT_BINARY_DIR}
- )
-endfunction()
diff --git a/src/.gradle/3.2.1/taskArtifacts/fileSnapshots.bin b/src/.gradle/3.2.1/taskArtifacts/fileSnapshots.bin
new file mode 100644
index 0000000..f0cc1a8
--- /dev/null
+++ b/src/.gradle/3.2.1/taskArtifacts/fileSnapshots.bin
Binary files differ
diff --git a/src/.gradle/3.2.1/taskArtifacts/taskArtifacts.bin b/src/.gradle/3.2.1/taskArtifacts/taskArtifacts.bin
new file mode 100644
index 0000000..b1f5426
--- /dev/null
+++ b/src/.gradle/3.2.1/taskArtifacts/taskArtifacts.bin
Binary files differ
diff --git a/src/.gradle/3.2.1/taskArtifacts/taskArtifacts.lock b/src/.gradle/3.2.1/taskArtifacts/taskArtifacts.lock
new file mode 100644
index 0000000..6fa2a90
--- /dev/null
+++ b/src/.gradle/3.2.1/taskArtifacts/taskArtifacts.lock
Binary files differ
diff --git a/src/main/CMakeLists.txt b/src/main/CMakeLists.txt
deleted file mode 100644
index 776826c..0000000
--- a/src/main/CMakeLists.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright 2020 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
-#
-# https://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.
-
-set(UKEY_SRC_ROOT ${CMAKE_CURRENT_LIST_DIR})
-set(UKEY_BINARY_ROOT ${CMAKE_CURRENT_BINARY_DIR})
-add_subdirectory(cpp)
-add_subdirectory(proto)
diff --git a/src/main/cpp/CMakeLists.txt b/src/main/cpp/CMakeLists.txt
deleted file mode 100644
index 919e096..0000000
--- a/src/main/cpp/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright 2020 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
-#
-# https://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.
-
-include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
-add_subdirectory(src/securegcm)
-add_subdirectory(test/securegcm)
diff --git a/src/main/cpp/include/securegcm/d2d_connection_context_v1.h b/src/main/cpp/include/securegcm/d2d_connection_context_v1.h
deleted file mode 100644
index 098e654..0000000
--- a/src/main/cpp/include/securegcm/d2d_connection_context_v1.h
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2020 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
-//
-// https://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.
-
-#ifndef SECURITY_CRYPTAUTH_LIB_SECUREGCM_D2D_CONNECTION_CONTEXT_V1_H_
-#define SECURITY_CRYPTAUTH_LIB_SECUREGCM_D2D_CONNECTION_CONTEXT_V1_H_
-
-#include <memory>
-#include <string>
-
-#include "securemessage/crypto_ops.h"
-
-namespace securegcm {
-
-// The full context of a secure connection. This class has methods to encode and
-// decode messages that are to be sent to another device.
-//
-// This class should be kept compatible with the Java implementation in
-// java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextV1.java
-class D2DConnectionContextV1 {
- public:
- D2DConnectionContextV1(const securemessage::CryptoOps::SecretKey& encode_key,
- const securemessage::CryptoOps::SecretKey& decode_key,
- uint32_t encode_sequence_number,
- uint32_t decode_sequence_number);
-
- // Once the initiator and responder have negotiated a secret key, use this
- // method to encrypt and sign |payload|. Both initiator and responder devices
- // can use this message.
- //
- // On failure, nullptr is returned.
- std::unique_ptr<string> EncodeMessageToPeer(const string& payload);
-
- // Once the initiator and responder have negotiated a secret key, use this
- // method to decrypt and verify a |message| received from the other device.
- // Both initiator and responder devices can use this message.
- //
- // On failure, nullptr is returned.
- std::unique_ptr<string> DecodeMessageFromPeer(const string& message);
-
- // Returns a cryptographic digest (SHA256) of the session keys prepended by
- // the SHA256 hash of the ASCII string "D2D".
- //
- // On failure, nullptr is returned.
- std::unique_ptr<string> GetSessionUnique();
-
- // Creates a saved session that can be later used for resumption. Note,
- // this must be stored in a secure location.
- std::unique_ptr<string> SaveSession();
-
- // Parse a saved session info and attempt to construct a resumed context.
- //
- // The session info passed to this method should be one that was generated
- // by |SaveSession|.
- //
- // On failure, nullptr is returned.
- static std::unique_ptr<D2DConnectionContextV1> FromSavedSession(
- const string& savedSessionInfo);
-
- private:
- // The key used to encode payloads.
- const securemessage::CryptoOps::SecretKey encode_key_;
-
- // The key used to decode received messages.
- const securemessage::CryptoOps::SecretKey decode_key_;
-
- // The current sequence number for encoding.
- uint32_t encode_sequence_number_;
-
- // The current sequence number for decoding.
- uint32_t decode_sequence_number_;
-
- // A friend to access private variables for testing.
- friend class D2DConnectionContextV1Peer;
-};
-
-} // namespace securegcm
-
-#endif // SECURITY_CRYPTAUTH_LIB_SECUREGCM_D2D_CONNECTION_CONTEXT_V1_H_
diff --git a/src/main/cpp/include/securegcm/d2d_crypto_ops.h b/src/main/cpp/include/securegcm/d2d_crypto_ops.h
deleted file mode 100644
index eeeeb20..0000000
--- a/src/main/cpp/include/securegcm/d2d_crypto_ops.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2020 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
-//
-// https://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.
-
-#ifndef SECURITY_CRYPTAUTH_LIB_SECUREGCM_D2D_CRYPTO_OPS_H_
-#define SECURITY_CRYPTAUTH_LIB_SECUREGCM_D2D_CRYPTO_OPS_H_
-
-#include <memory>
-#include <string>
-
-#include "proto/securegcm.pb.h"
-#include "securemessage/crypto_ops.h"
-
-namespace securegcm {
-
-// A collection of static utility methods for the Device to Device communication
-// (D2D) library.
-//
-// A class is used here in preference to a namespace to provide a closer
-// correspondence with the Java equivalent class:
-// //java/com/google/security/cryptauth/lib/securegcm/D2DCryptoOps.java
-class D2DCryptoOps {
- public:
- // Encapsulates a payload type specifier, and a corresponding message as the
- // raw payload.
- //
- // Note: Type is defined in securegcm.proto.
- class Payload {
- public:
- Payload(Type type, const std::string& message);
-
- Type type() const { return type_; }
-
- const std::string& message() const { return message_; }
-
- private:
- const Type type_;
- const std::string message_;
- };
-
- // The salt, SHA256 of "D2D".
- static const uint8_t kSalt[];
- static const size_t kSaltLength;
-
- // Used by a device to send a secure |Payload| to another device.
- static std::unique_ptr<std::string> SigncryptPayload(
- const Payload& payload,
- const securemessage::CryptoOps::SecretKey& secret_key);
-
- // Used by a device to recover a secure |Payload| sent by another device.
- static std::unique_ptr<Payload> VerifyDecryptPayload(
- const std::string& signcrypted_message,
- const securemessage::CryptoOps::SecretKey& secret_key);
-
- // Used to derive a distinct key for each initiator and responder from the
- // |master_key|. Use a different |purpose| for each role.
- static std::unique_ptr<securemessage::CryptoOps::SecretKey>
- DeriveNewKeyForPurpose(const securemessage::CryptoOps::SecretKey& master_key,
- const std::string& purpose);
-
- private:
- // Prevent instantiation.
- D2DCryptoOps();
-};
-
-} // namespace securegcm
-
-#endif // SECURITY_CRYPTAUTH_LIB_SECUREGCM_D2D_CRYPTO_OPS_H_
diff --git a/src/main/cpp/include/securegcm/java_util.h b/src/main/cpp/include/securegcm/java_util.h
deleted file mode 100644
index 8783af4..0000000
--- a/src/main/cpp/include/securegcm/java_util.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2020 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
-//
-// https://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.
-
-// Utility functions for Java-compatible operations.
-#ifndef SECURITY_CRYPTAUTH_LIB_SECUREGCM_JAVA_UTIL_H_
-#define SECURITY_CRYPTAUTH_LIB_SECUREGCM_JAVA_UTIL_H_
-
-#include "securemessage/byte_buffer.h"
-
-namespace securegcm {
-namespace java_util {
-
-// Perform multiplication with Java overflow semantics
-// (https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html):
-// If an integer multiplication overflows, then the result is the low-order
-// bits of the mathematical product as represented in some sufficiently
-// large two's-complement format.
-int32_t JavaMultiply(int32_t lhs, int32_t rhs);
-
-// Perform addition with Java overflow semantics:
-// (https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html):
-// If an integer addition overflows, then the result is the low-order bits of
-// the mathematical sum as represented in some sufficiently large
-// two's-complement format.
-int32_t JavaAdd(int32_t lhs, int32_t rhs);
-
-// To be compatible with the Java implementation, we need to use the same
-// algorithm as the Arrays#hashCode(byte[]) function in Java:
-// "The value returned by this method is the same value that would be obtained
-// by invoking the hashCode method on a List containing a sequence of Byte
-// instances representing the elements of a in the same order."
-//
-// According to List#hashCode(), this algorithm is:
-// int hashCode = 1;
-// for (Byte b : list) {
-// hashCode = 31 * hashCode + (b == null ? b : b.hashCode());
-// }
-//
-// Finally, Byte#hashCode() is defined as "equal to the result of invoking
-// Byte#intValue()".
-int32_t JavaHashCode(const securemessage::ByteBuffer& byte_buffer);
-
-} // namespace java_util
-} // namespace securegcm
-
-#endif // SECURITY_CRYPTAUTH_LIB_SECUREGCM_JAVA_UTIL_H_
diff --git a/src/main/cpp/include/securegcm/ukey2_handshake.h b/src/main/cpp/include/securegcm/ukey2_handshake.h
deleted file mode 100644
index 8455fd7..0000000
--- a/src/main/cpp/include/securegcm/ukey2_handshake.h
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2020 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
-//
-// https://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.
-
-#ifndef SECURITY_CRYPTAUTH_LIB_SECUREGCM_UKEY2_HANDSHAKE_H_
-#define SECURITY_CRYPTAUTH_LIB_SECUREGCM_UKEY2_HANDSHAKE_H_
-
-#include <map>
-#include <memory>
-
-#include "proto/ukey.pb.h"
-#include "securegcm/d2d_connection_context_v1.h"
-#include "securemessage/crypto_ops.h"
-
-namespace securegcm {
-
-// Implements UKEY2 and produces a |D2DConnectionContextV1|.
-// This class should be kept compatible with the Java implementation in
-// //java/com/google/security/cryptauth/lib/securegcm/Ukey2Handshake.java
-//
-// For usage examples, see ukey2_shell.cc. This file contains a shell exercising
-// both the initiator and responder handshake roles.
-class UKey2Handshake {
- public:
- // Handshake states:
- // kInProgress:
- // The handshake is in progress, caller should use
- // |GetNextHandshakeMessage()| and |ParseHandshakeMessage()| to continue
- // the handshake.
- //
- // kVerificationNeeded:
- // The handshake is complete, but pending verification of the
- // authentication string. Clients should use |GetVerificationString()|
- // to get the verification string and use out-of-band methods to
- // authenticate the handshake.
- //
- // kVerificationInProgress:
- // The handshake is complete, verification string has been generated,
- // but has not been confirmed. After authenticating the handshake
- // out-of-band, use |VerifyHandshake()| to mark the handshake as
- // verified.
- //
- // kFinished:
- // The handshake is finished, and the caller can use
- // |ToConnectionContext()| to produce a |D2DConnectionContextV1|.
- //
- // kAlreadyUsed:
- // The hanshake has already been used and should be destroyed.
- //
- // kError:
- // The handshake produced an error and should be destroyed.
- enum class State {
- kInProgress,
- kVerificationNeeded,
- kVerificationInProgress,
- kFinished,
- kAlreadyUsed,
- kError,
- };
-
- // Currently implemented UKEY2 handshake ciphers. Each cipher is a tuple
- // consisting of a key negotiation cipher and a hash function used for a
- // commitment. Currently the ciphers are:
- // +-----------------------------------------------------+
- // | Enum | Key negotiation | Hash function |
- // +-------------+-----------------------+---------------+
- // | P256_SHA512 | ECDH using NIST P-256 | SHA512 |
- // +-----------------------------------------------------+
- //
- // Note that these should correspond to values in
- // device_to_device_messages.proto.
- enum class HandshakeCipher : int {
- // TODO(aczeskis): add CURVE25519_SHA512
-
- P256_SHA512 = securegcm::P256_SHA512,
- };
-
- // Creates a |UKey2Handshake| with a particular |cipher| that can be used by
- // an initiator / client.
- static std::unique_ptr<UKey2Handshake> ForInitiator(HandshakeCipher cipher);
-
- // Creates a |UKey2Handshake| with a particular |cipher| that can be used by
- // a responder / server.
- static std::unique_ptr<UKey2Handshake> ForResponder(HandshakeCipher cipher);
-
- // Returns the current state of the handshake.
- State GetHandshakeState() const;
-
- // Returns the last error message. Empty string if there was no error.
- const string& GetLastError() const;
-
- // Gets the next handshake message suitable for sending on the wire.
- // If |nullptr| is returned, check |GetLastError()| for the error message.
- std::unique_ptr<string> GetNextHandshakeMessage();
-
- // Parses the given |handshake_message|, updating the internal state.
- struct ParseResult {
- // True if |handshake_message| is parsed successfully. If |false|, call
- // |GetLastError()| for the error message.
- bool success;
-
- // May be set if parsing fails. This value should be sent to the remote
- // device before disconnecting.
- std::unique_ptr<string> alert_to_send;
- };
- ParseResult ParseHandshakeMessage(const string& handshake_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 the state returned from
- // |GetHandshakeState()| is |State::VERIFICATION_NEEDED|, which means this can
- // only be called once.
- //
- // |byte_length|: The length of the output. Min length is 1; max length is 32.
- // If |nullptr| is returned, check |GetLastError()| for the error message.
- std::unique_ptr<string> GetVerificationString(int byte_length);
-
- // Invoked to let the handshake state machine know that caller has validated
- // the authentication string obtained via |GetVerificationString()|.
- // Note: This should only be called when the state returned by
- // |GetHandshakeState()| is |State::VERIFICATION_IN_PROGRESS|.
- //
- // If |false| is returned, check |GetLastError()| for the error message.
- bool VerifyHandshake();
-
- // Can be called to generate a |D2DConnectionContextV1|. Returns nullptr on
- // failure.
- // Note: This should only be called when the state returned by
- // |GetHandshakeState()| is |State::FINISHED|.
- //
- // If |nullptr| is returned, check |GetLastError()| for the error message.
- std::unique_ptr<D2DConnectionContextV1> ToConnectionContext();
-
- private:
- // Enums for internal state machinery.
- enum class InternalState : int {
- CLIENT_START,
- CLIENT_WAITING_FOR_SERVER_INIT,
- CLIENT_AFTER_SERVER_INIT,
-
- // Responder/server state
- SERVER_START,
- SERVER_AFTER_CLIENT_INIT,
- SERVER_WAITING_FOR_CLIENT_FINISHED,
-
- // Common completion state
- HANDSHAKE_VERIFICATION_NEEDED,
- HANDSHAKE_VERIFICATION_IN_PROGRESS,
- HANDSHAKE_FINISHED,
- HANDSHAKE_ALREADY_USED,
- HANDSHAKE_ERROR,
- };
-
- // Helps us remember our role in the handshake.
- enum class HandshakeRole {
- CLIENT,
- SERVER
- };
-
- // Prevent public instantiation. Callers should use |ForInitiator()| or
- // |ForResponder()|.
- UKey2Handshake(InternalState state, HandshakeCipher cipher);
-
- // Attempts to parse Ukey2ClientInit, wrapped inside a Ukey2Message.
- // See go/ukey2 for details.
- ParseResult ParseClientInitUkey2Message(const string& handshake_message);
-
- // Attempts to parse Ukey2ServerInit, wrapped inside a Ukey2Message.
- // See go/ukey2 for details.
- ParseResult ParseServerInitUkey2Message(const string& handshake_message);
-
- // Attempts to parse Ukey2ClientFinish, wrapped inside a Ukey2Message.
- // See go/ukey2 for details.
- ParseResult ParseClientFinishUkey2Message(const string& handshake_message);
-
- // Convenience function to set |last_error_| and create a ParseResult with a
- // given alert.
- ParseResult CreateFailedResultWithAlert(Ukey2Alert::AlertType alert_type,
- const string& error_message);
-
- // Convenience function to set |last_error_| and create a failed ParseResult
- // without an alert.
- ParseResult CreateFailedResultWithoutAlert(const string& error_message);
-
- // Convenience function to create a successful ParseResult.
- ParseResult CreateSuccessResult();
-
- // Verifies that the peer's commitment stored in |peer_commitment_| is the
- // same as that obtained from |handshake_message|.
- bool VerifyCommitment(const string& handshake_message);
-
- // Generates a commitment for the P256_SHA512 cipher.
- std::unique_ptr<Ukey2ClientInit::CipherCommitment>
- GenerateP256Sha512Commitment();
-
- // Creates a serialized Ukey2Message, wrapping an inner ClientInit message.
- std::unique_ptr<string> MakeClientInitUkey2Message();
-
- // Creates a serialized Ukey2Message, wrapping an inner ServerInit message.
- std::unique_ptr<string> MakeServerInitUkey2Message();
-
- // Creates a serialized Ukey2Message of a given |type|, wrapping |data|.
- std::unique_ptr<string> MakeUkey2Message(Ukey2Message::Type type,
- const string& data);
-
- // Called when an error occurs to set |handshake_state_| and |last_error_|.
- void SetError(const string& error_message);
-
- // The current state of the handshake.
- InternalState handshake_state_;
-
- // The cipher to use for the handshake.
- const HandshakeCipher handshake_cipher_;
-
- // The role to perform, i.e. client or server.
- const HandshakeRole handshake_role_;
-
- // A newly generated key-pair for this handshake.
- std::unique_ptr<securemessage::CryptoOps::KeyPair> our_key_pair_;
-
- // The peer's public key retrieved from a handshake message.
- std::unique_ptr<securemessage::CryptoOps::PublicKey> their_public_key_;
-
- // The secret key derived from |our_key_pair_| and |their_public_key_|.
- std::unique_ptr<securemessage::CryptoOps::SecretKey> derived_secret_key_;
-
- // The raw bytes of the Ukey2ClientInit, wrapped inside a Ukey2Message.
- // Empty string if not initialized.
- string wrapped_client_init_;
-
- // The raw bytes of the Ukey2ServerInit, wrapped inside a Ukey2Message.
- // Empty string if not initialized.
- string wrapped_server_init_;
-
- // The commitment of the peer retrieved from a handshake message. Empty string
- // if not initialized.
- string peer_commitment_;
-
- // Map from ciphers to the raw bytes of message 3 (which is a wrapped
- // Ukey2ClientFinished message).
- // Note: Currently only one cipher is supported, so at most one entry exists
- // in this map.
- std::map<HandshakeCipher, string> raw_message3_map_;
-
- // Contains the last error message.
- string last_error_;
-};
-
-} // namespace securegcm
-
-#endif // SECURITY_CRYPTAUTH_LIB_SECUREGCM_UKEY2_HANDSHAKE_H_
diff --git a/src/main/cpp/src/securegcm/CMakeLists.txt b/src/main/cpp/src/securegcm/CMakeLists.txt
deleted file mode 100644
index b562756..0000000
--- a/src/main/cpp/src/securegcm/CMakeLists.txt
+++ /dev/null
@@ -1,47 +0,0 @@
-# Copyright 2020 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
-#
-# https://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.
-
-add_library(ukey2 STATIC
- d2d_connection_context_v1.cc
- d2d_crypto_ops.cc
- java_util.cc
- ukey2_handshake.cc
-)
-
-target_include_directories(ukey2
- PUBLIC
- ${PROJECT_SOURCE_DIR}/src/main/cpp/include
-)
-
-target_link_libraries(ukey2
- PUBLIC
- proto_device_to_device_messages_cc_proto
- proto_securegcm_cc_proto
- proto_ukey_cc_proto
- securemessage
-)
-
-add_executable(ukey2_shell
- ukey2_shell.cc
-)
-
-target_link_libraries(ukey2_shell
- PUBLIC
- securemessage
- ukey2
- absl::base
- absl::container
- absl::flags
- absl::flags_parse
-)
diff --git a/src/main/cpp/src/securegcm/d2d_connection_context_v1.cc b/src/main/cpp/src/securegcm/d2d_connection_context_v1.cc
deleted file mode 100644
index 8a9a612..0000000
--- a/src/main/cpp/src/securegcm/d2d_connection_context_v1.cc
+++ /dev/null
@@ -1,228 +0,0 @@
-// Copyright 2020 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
-//
-// https://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.
-
-#include "securegcm/d2d_connection_context_v1.h"
-
-#include <limits>
-#include <sstream>
-
-#include "proto/device_to_device_messages.pb.h"
-#include "proto/securegcm.pb.h"
-#include "securegcm/d2d_crypto_ops.h"
-#include "securegcm/java_util.h"
-#include "securemessage/secure_message_builder.h"
-#include "securemessage/util.h"
-
-namespace securegcm {
-
-using securemessage::CryptoOps;
-using securemessage::ByteBuffer;
-using securemessage::Util;
-
-namespace {
-
-// Fields to fill in the GcmMetadata proto.
-const Type kGcmMetadataType = DEVICE_TO_DEVICE_MESSAGE;
-
-// Represents the version of this context.
-const uint8_t kProtocolVersion = 1;
-
-// The following represent the starting positions of the each entry within
-// the string representation of this D2DConnectionContextV1.
-//
-// The saved session has a 1 byte protocol version, two 4 byte sequence numbers,
-// and two 32 byte AES keys: (1 + 4 + 4 + 32 + 32 = 73).
-
-// The two sequence numbers are 4 bytes each.
-const int kSequenceNumberLength = 4;
-
-// 32 byte AES keys.
-const int kAesKeyLength = 32;
-
-// The encode sequence number starts at 1 to account for the 1 byte version
-// number.
-const int kEncodeSequenceStart = 1;
-const int kEncodeSequenceEnd = kEncodeSequenceStart + kSequenceNumberLength;
-
-const int kDecodeSequenceStart = kEncodeSequenceEnd;
-const int kDecodeSequenceEnd = kDecodeSequenceStart + kSequenceNumberLength;
-
-const int kEncodeKeyStart = kDecodeSequenceEnd;
-const int kEncodeKeyEnd = kEncodeKeyStart + kAesKeyLength;
-
-const int kDecodeKeyStart = kEncodeKeyEnd;
-const int kSavedSessionLength = kDecodeKeyStart + kAesKeyLength;
-
-// Convenience function to creates a DeviceToDeviceMessage proto with |payload|
-// and |sequence_number|.
-DeviceToDeviceMessage CreateDeviceToDeviceMessage(const std::string& payload,
- uint32_t sequence_number) {
- DeviceToDeviceMessage device_to_device_message;
- device_to_device_message.set_sequence_number(sequence_number);
- device_to_device_message.set_message(payload);
- return device_to_device_message;
-}
-
-// Convert 4 bytes in big-endian representation into an unsigned int.
-uint32_t BytesToUnsignedInt(std::vector<uint8_t> bytes) {
- return bytes[0] << 24 | bytes[1] << 12 | bytes[2] << 8 | bytes[3];
-}
-
-// Convert an unsigned int into a 4 byte big-endian representation.
-std::vector<uint8_t> UnsignedIntToBytes(uint32_t val) {
- return {static_cast<uint8_t>(val >> 24), static_cast<uint8_t>(val >> 12),
- static_cast<uint8_t>(val >> 8), static_cast<uint8_t>(val)};
-}
-
-} // namespace
-
-D2DConnectionContextV1::D2DConnectionContextV1(
- const CryptoOps::SecretKey& encode_key,
- const CryptoOps::SecretKey& decode_key, uint32_t encode_sequence_number,
- uint32_t decode_sequence_number)
- : encode_key_(encode_key),
- decode_key_(decode_key),
- encode_sequence_number_(encode_sequence_number),
- decode_sequence_number_(decode_sequence_number) {}
-
-std::unique_ptr<std::string> D2DConnectionContextV1::EncodeMessageToPeer(
- const std::string& payload) {
- encode_sequence_number_++;
- const DeviceToDeviceMessage message =
- CreateDeviceToDeviceMessage(payload, encode_sequence_number_);
-
- const D2DCryptoOps::Payload payload_with_type(kGcmMetadataType,
- message.SerializeAsString());
- return D2DCryptoOps::SigncryptPayload(payload_with_type, encode_key_);
-}
-
-std::unique_ptr<std::string> D2DConnectionContextV1::DecodeMessageFromPeer(
- const std::string& message) {
- std::unique_ptr<D2DCryptoOps::Payload> payload =
- D2DCryptoOps::VerifyDecryptPayload(message, decode_key_);
- if (!payload) {
- Util::LogError("DecodeMessageFromPeer: Failed to verify message.");
- return nullptr;
- }
-
- if (kGcmMetadataType != payload->type()) {
- Util::LogError("DecodeMessageFromPeer: Wrong message type in D2D message.");
- return nullptr;
- }
-
- DeviceToDeviceMessage d2d_message;
- if (!d2d_message.ParseFromString(payload->message())) {
- Util::LogError("DecodeMessageFromPeer: Unable to parse D2D message proto.");
- return nullptr;
- }
-
- decode_sequence_number_++;
- if (d2d_message.sequence_number() != decode_sequence_number_) {
- std::ostringstream stream;
- stream << "DecodeMessageFromPeer: Seqno in D2D message ("
- << d2d_message.sequence_number()
- << ") does not match expected seqno (" << decode_sequence_number_
- << ").";
- Util::LogError(stream.str());
- return nullptr;
- }
-
- return std::unique_ptr<std::string>(d2d_message.release_message());
-}
-
-std::unique_ptr<std::string> D2DConnectionContextV1::GetSessionUnique() {
- const ByteBuffer encode_key_data = encode_key_.data();
- const ByteBuffer decode_key_data = decode_key_.data();
- const int32_t encode_key_hash = java_util::JavaHashCode(encode_key_data);
- const int32_t decode_key_hash = java_util::JavaHashCode(decode_key_data);
-
- const ByteBuffer& first_buffer =
- encode_key_hash < decode_key_hash ? encode_key_data : decode_key_data;
- const ByteBuffer& second_buffer =
- encode_key_hash < decode_key_hash ? decode_key_data : encode_key_data;
-
- ByteBuffer data_to_hash(D2DCryptoOps::kSalt, D2DCryptoOps::kSaltLength);
- data_to_hash = ByteBuffer::Concat(data_to_hash, first_buffer);
- data_to_hash = ByteBuffer::Concat(data_to_hash, second_buffer);
-
- std::unique_ptr<ByteBuffer> hash = CryptoOps::Sha256(data_to_hash);
- if (!hash) {
- Util::LogError("GetSessionUnique: SHA-256 hash failed.");
- return nullptr;
- }
-
- return std::unique_ptr<std::string>(new std::string(hash->String()));
-}
-
-// Structure of saved session is:
-//
-// +---------------------------------------------------------------------------+
-// | 1 Byte | 4 Bytes | 4 Bytes | 32 Bytes | 32 Bytes |
-// +---------------------------------------------------------------------------+
-// | Version | encode seq number | decode seq number | encode key | decode key |
-// +---------------------------------------------------------------------------+
-//
-// The sequence numbers are represented in big-endian.
-std::unique_ptr<std::string> D2DConnectionContextV1::SaveSession() {
- ByteBuffer byteBuffer = ByteBuffer(&kProtocolVersion, static_cast<size_t>(1));
-
- // Append encode sequence number.
- std::vector<uint8_t> encode_sequence_number_bytes =
- UnsignedIntToBytes(encode_sequence_number_);
- for (int i = 0; i < encode_sequence_number_bytes.size(); i++) {
- byteBuffer.Append(static_cast<size_t>(1), encode_sequence_number_bytes[i]);
- }
-
- // Append decode sequence number.
- std::vector<uint8_t> decode_sequence_number_bytes =
- UnsignedIntToBytes(decode_sequence_number_);
- for (int i = 0; i < decode_sequence_number_bytes.size(); i++) {
- byteBuffer.Append(static_cast<size_t>(1), decode_sequence_number_bytes[i]);
- }
-
- // Append encode key.
- byteBuffer = ByteBuffer::Concat(byteBuffer, encode_key_.data());
-
- // Append decode key.
- byteBuffer = ByteBuffer::Concat(byteBuffer, decode_key_.data());
-
- return std::unique_ptr<std::string>(new std::string(byteBuffer.String()));
-}
-
-// static.
-std::unique_ptr<D2DConnectionContextV1>
-D2DConnectionContextV1::FromSavedSession(const std::string& savedSessionInfo) {
- ByteBuffer byteBuffer = ByteBuffer(savedSessionInfo);
-
- if (byteBuffer.size() != kSavedSessionLength) {
- return nullptr;
- }
-
- uint32_t encode_sequence_number = BytesToUnsignedInt(
- byteBuffer.SubArray(kEncodeSequenceStart, kEncodeSequenceEnd)->Vector());
- uint32_t decode_sequence_number = BytesToUnsignedInt(
- byteBuffer.SubArray(kDecodeSequenceStart, kDecodeSequenceEnd)->Vector());
-
- const CryptoOps::SecretKey encode_key =
- CryptoOps::SecretKey(*byteBuffer.SubArray(kEncodeKeyStart, kAesKeyLength),
- CryptoOps::KeyAlgorithm::AES_256_KEY);
- const CryptoOps::SecretKey decode_key =
- CryptoOps::SecretKey(*byteBuffer.SubArray(kDecodeKeyStart, kAesKeyLength),
- CryptoOps::KeyAlgorithm::AES_256_KEY);
-
- return std::unique_ptr<D2DConnectionContextV1>(new D2DConnectionContextV1(
- encode_key, decode_key, encode_sequence_number, decode_sequence_number));
-}
-
-} // namespace securegcm
diff --git a/src/main/cpp/src/securegcm/d2d_crypto_ops.cc b/src/main/cpp/src/securegcm/d2d_crypto_ops.cc
deleted file mode 100644
index 49f0b85..0000000
--- a/src/main/cpp/src/securegcm/d2d_crypto_ops.cc
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 2020 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
-//
-// https://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.
-
-#include "securegcm/d2d_crypto_ops.h"
-
-#include <sstream>
-
-#include "securemessage/secure_message_builder.h"
-#include "securemessage/secure_message_parser.h"
-#include "securemessage/util.h"
-
-namespace securegcm {
-
-using securemessage::CryptoOps;
-using securemessage::HeaderAndBody;
-using securemessage::SecureMessage;
-using securemessage::SecureMessageBuilder;
-using securemessage::SecureMessageParser;
-using securemessage::Util;
-
-namespace {
-
-// The current protocol version.
-const int kSecureGcmProtocolVersion = 1;
-
-// The number of bytes in an expected AES256 key.
-const int kAes256KeyLength = 32;
-}
-
-// static.
-const uint8_t D2DCryptoOps::kSalt[] = {
- 0x82, 0xAA, 0x55, 0xA0, 0xD3, 0x97, 0xF8, 0x83, 0x46, 0xCA, 0x1C,
- 0xEE, 0x8D, 0x39, 0x09, 0xB9, 0x5F, 0x13, 0xFA, 0x7D, 0xEB, 0x1D,
- 0x4A, 0xB3, 0x83, 0x76, 0xB8, 0x25, 0x6D, 0xA8, 0x55, 0x10};
-
-// static.
-const size_t D2DCryptoOps::kSaltLength = sizeof(D2DCryptoOps::kSalt);
-
-D2DCryptoOps::Payload::Payload(Type type, const string& message)
- : type_(type), message_(message) {}
-
-D2DCryptoOps::D2DCryptoOps() {}
-
-// static.
-std::unique_ptr<string> D2DCryptoOps::SigncryptPayload(
- const Payload& payload, const CryptoOps::SecretKey& secret_key) {
- GcmMetadata gcm_metadata;
- gcm_metadata.set_type(payload.type());
- gcm_metadata.set_version(kSecureGcmProtocolVersion);
-
- SecureMessageBuilder builder;
- builder.SetPublicMetadata(gcm_metadata.SerializeAsString());
-
- std::unique_ptr<SecureMessage> secure_message =
- builder.BuildSignCryptedMessage(secret_key, CryptoOps::HMAC_SHA256,
- secret_key, CryptoOps::AES_256_CBC,
- payload.message());
- if (!secure_message) {
- Util::LogError("Unable to encrypt payload.");
- return nullptr;
- }
-
- return std::unique_ptr<string>(
- new string(secure_message->SerializeAsString()));
-}
-
-// static.
-std::unique_ptr<D2DCryptoOps::Payload> D2DCryptoOps::VerifyDecryptPayload(
- const string& signcrypted_message, const CryptoOps::SecretKey& secret_key) {
- SecureMessage secure_message;
- if (!secure_message.ParseFromString(signcrypted_message)) {
- Util::LogError("VerifyDecryptPayload: error parsing SecureMessage.");
- return nullptr;
- }
-
- std::unique_ptr<HeaderAndBody> header_and_body =
- SecureMessageParser::ParseSignCryptedMessage(
- secure_message, secret_key, CryptoOps::HMAC_SHA256, secret_key,
- CryptoOps::AES_256_CBC, string() /* associated_data */);
- if (!header_and_body) {
- Util::LogError("VerifyDecryptPayload: error verifying SecureMessage.");
- return nullptr;
- }
-
- if (!header_and_body->header().has_public_metadata()) {
- Util::LogError("VerifyDecryptPayload: no public metadata in header.");
- return nullptr;
- }
-
- GcmMetadata metadata;
- if (!metadata.ParseFromString(header_and_body->header().public_metadata())) {
- Util::LogError("VerifyDecryptPayload: Failed to parse GcmMetadata.");
- return nullptr;
- }
-
- if (metadata.version() != kSecureGcmProtocolVersion) {
- std::ostringstream stream;
- stream << "VerifyDecryptPayload: Unsupported protocol version "
- << metadata.version();
- Util::LogError(stream.str());
- return nullptr;
- }
-
- return std::unique_ptr<Payload>(
- new Payload(metadata.type(), header_and_body->body()));
-}
-
-// static.
-std::unique_ptr<CryptoOps::SecretKey> D2DCryptoOps::DeriveNewKeyForPurpose(
- const securemessage::CryptoOps::SecretKey& master_key,
- const string& purpose) {
- if (master_key.data().size() != kAes256KeyLength) {
- Util::LogError("DeriveNewKeyForPurpose: Invalid master_key length.");
- return nullptr;
- }
-
- if (purpose.empty()) {
- Util::LogError("DeriveNewKeyForPurpose: purpose is empty.");
- return nullptr;
- }
-
- std::unique_ptr<string> raw_derived_key = CryptoOps::Hkdf(
- master_key.data().String(),
- string(reinterpret_cast<const char *>(kSalt), kSaltLength),
- purpose);
- if (!raw_derived_key) {
- Util::LogError("DeriveNewKeyForPurpose: hkdf failed.");
- return nullptr;
- }
-
- if (raw_derived_key->size() != kAes256KeyLength) {
- Util::LogError("DeriveNewKeyForPurpose: Unexpected size of derived key.");
- return nullptr;
- }
-
- return std::unique_ptr<CryptoOps::SecretKey>(
- new CryptoOps::SecretKey(*raw_derived_key, CryptoOps::AES_256_KEY));
-}
-
-} // namespace securegcm
diff --git a/src/main/cpp/src/securegcm/java_util.cc b/src/main/cpp/src/securegcm/java_util.cc
deleted file mode 100644
index 1ce4d7b..0000000
--- a/src/main/cpp/src/securegcm/java_util.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2020 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
-//
-// https://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.
-
-#include "securegcm/java_util.h"
-
-#include <cstring>
-
-namespace securegcm {
-namespace java_util {
-
-namespace {
-
-// Returns the lower 32-bits of a int64_t |value| as an int32_t.
-int32_t Lower32Bits(int64_t value) {
- const uint32_t lower_bits = static_cast<uint32_t>(value & 0xFFFFFFFF);
- int32_t return_value;
- std::memcpy(&return_value, &lower_bits, sizeof(uint32_t));
- return return_value;
-}
-
-} // namespace
-
-int32_t JavaMultiply(int32_t lhs, int32_t rhs) {
- // Multiplication guaranteed to fit in int64_t, range from [2^63, 2^63 - 1].
- // Minimum value is (-2^31)^2 = 2^62.
- const int64_t result = static_cast<int64_t>(lhs) * static_cast<int64_t>(rhs);
- return Lower32Bits(result);
-}
-
-int32_t JavaAdd(int32_t lhs, int32_t rhs) {
- const int64_t result = static_cast<int64_t>(lhs) + static_cast<int64_t>(rhs);
- return Lower32Bits(result);
-}
-
-int32_t JavaHashCode(const securemessage::ByteBuffer& byte_buffer) {
- const string bytes = byte_buffer.String();
- int32_t hash_code = 1;
- for (const int8_t byte : bytes) {
- int32_t int_value = static_cast<int32_t>(byte);
- // Java relies on the overflow/underflow behaviour of arithmetic operations,
- // which is undefined in C++, so we call our own Java-compatible versions of
- // + and * here.
- hash_code = JavaAdd(JavaMultiply(31, hash_code), int_value);
- }
- return hash_code;
-}
-
-} // namespace java_util
-} // namespace securegcm
diff --git a/src/main/cpp/src/securegcm/ukey2_handshake.cc b/src/main/cpp/src/securegcm/ukey2_handshake.cc
deleted file mode 100644
index dc5c131..0000000
--- a/src/main/cpp/src/securegcm/ukey2_handshake.cc
+++ /dev/null
@@ -1,715 +0,0 @@
-// Copyright 2020 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
-//
-// https://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.
-
-#include "securegcm/ukey2_handshake.h"
-
-#include <sstream>
-
-#include "securegcm/d2d_crypto_ops.h"
-#include "securemessage/public_key_proto_util.h"
-
-namespace securegcm {
-
-using securemessage::ByteBuffer;
-using securemessage::CryptoOps;
-using securemessage::GenericPublicKey;
-using securemessage::PublicKeyProtoUtil;
-
-namespace {
-
-// Salt value used to derive client and server keys for next protocol.
-const char kUkey2HkdfSalt[] = "UKEY2 v1 next";
-
-// Salt value used to derive verification string.
-const char kUkey2VerificationStringSalt[] = "UKEY2 v1 auth";
-
-// Maximum version of the handshake supported by this class.
-const uint32_t kVersion = 1;
-
-// Random nonce is fixed at 32 bytes (as per go/ukey2).
-const uint32_t kNonceLengthInBytes = 32;
-
-// Currently, we only support one next protocol.
-const char kNextProtocol[] = "AES_256_CBC-HMAC_SHA256";
-
-// Creates the appropriate KeyPair for |cipher|.
-std::unique_ptr<CryptoOps::KeyPair> GenerateKeyPair(
- UKey2Handshake::HandshakeCipher cipher) {
- switch (cipher) {
- case UKey2Handshake::HandshakeCipher::P256_SHA512:
- return CryptoOps::GenerateEcP256KeyPair();
- default:
- return nullptr;
- }
-}
-
-// Parses a CryptoOps::PublicKey from a serialized GenericPublicKey.
-std::unique_ptr<securemessage::CryptoOps::PublicKey> ParsePublicKey(
- const string& serialized_generic_public_key) {
- GenericPublicKey generic_public_key;
- if (!generic_public_key.ParseFromString(serialized_generic_public_key)) {
- return nullptr;
- }
- return PublicKeyProtoUtil::ParsePublicKey(generic_public_key);
-}
-
-} // namespace
-
-// static.
-std::unique_ptr<UKey2Handshake> UKey2Handshake::ForInitiator(
- HandshakeCipher cipher) {
- return std::unique_ptr<UKey2Handshake>(
- new UKey2Handshake(InternalState::CLIENT_START, cipher));
-}
-
-// static.
-std::unique_ptr<UKey2Handshake> UKey2Handshake::ForResponder(
- HandshakeCipher cipher) {
- return std::unique_ptr<UKey2Handshake>(
- new UKey2Handshake(InternalState::SERVER_START, cipher));
-}
-
-UKey2Handshake::UKey2Handshake(InternalState state, HandshakeCipher cipher)
- : handshake_state_(state),
- handshake_cipher_(cipher),
- handshake_role_(state == InternalState::CLIENT_START
- ? HandshakeRole::CLIENT
- : HandshakeRole::SERVER),
- our_key_pair_(GenerateKeyPair(cipher)) {}
-
-UKey2Handshake::State UKey2Handshake::GetHandshakeState() const {
- switch (handshake_state_) {
- case InternalState::CLIENT_START:
- case InternalState::CLIENT_WAITING_FOR_SERVER_INIT:
- case InternalState::CLIENT_AFTER_SERVER_INIT:
- case InternalState::SERVER_START:
- case InternalState::SERVER_AFTER_CLIENT_INIT:
- case InternalState::SERVER_WAITING_FOR_CLIENT_FINISHED:
- // Fallthrough intended -- these are all in-progress states.
- return State::kInProgress;
- case InternalState::HANDSHAKE_VERIFICATION_NEEDED:
- return State::kVerificationNeeded;
- case InternalState::HANDSHAKE_VERIFICATION_IN_PROGRESS:
- return State::kVerificationInProgress;
- case InternalState::HANDSHAKE_FINISHED:
- return State::kFinished;
- case InternalState::HANDSHAKE_ALREADY_USED:
- return State::kAlreadyUsed;
- case InternalState::HANDSHAKE_ERROR:
- return State::kError;
- default:
- // Unreachable.
- return State::kError;
- }
-}
-
-const string& UKey2Handshake::GetLastError() const {
- return last_error_;
-}
-
-std::unique_ptr<string> UKey2Handshake::GetNextHandshakeMessage() {
- switch (handshake_state_) {
- case InternalState::CLIENT_START: {
- std::unique_ptr<string> client_init = MakeClientInitUkey2Message();
- if (!client_init) {
- // |last_error_| is already set.
- return nullptr;
- }
-
- wrapped_client_init_ = *client_init;
- handshake_state_ = InternalState::CLIENT_WAITING_FOR_SERVER_INIT;
- return client_init;
- }
-
- case InternalState::SERVER_AFTER_CLIENT_INIT: {
- std::unique_ptr<string> server_init = MakeServerInitUkey2Message();
- if (!server_init) {
- // |last_error_| is already set.
- return nullptr;
- }
-
- wrapped_server_init_ = *server_init;
- handshake_state_ = InternalState::SERVER_WAITING_FOR_CLIENT_FINISHED;
- return server_init;
- }
-
- case InternalState::CLIENT_AFTER_SERVER_INIT: {
- // Make sure we have a message 3 for the chosen cipher.
- if (raw_message3_map_.count(handshake_cipher_) == 0) {
- std::ostringstream stream;
- stream << "Client state is CLIENT_AFTER_SERVER_INIT, and cipher is "
- << static_cast<int>(handshake_cipher_)
- << ", but no corresponding raw "
- << "[Client Finished] message has been generated.";
- SetError(stream.str());
- return nullptr;
- }
- handshake_state_ = InternalState::HANDSHAKE_VERIFICATION_NEEDED;
- return std::unique_ptr<string>(
- new string(raw_message3_map_[handshake_cipher_]));
- }
-
- default: {
- std::ostringstream stream;
- stream << "Cannot get next message in state "
- << static_cast<int>(handshake_state_);
- SetError(stream.str());
- return nullptr;
- }
- }
-}
-
-UKey2Handshake::ParseResult
-UKey2Handshake::ParseHandshakeMessage(const string& handshake_message) {
- switch (handshake_state_) {
- case InternalState::SERVER_START:
- return ParseClientInitUkey2Message(handshake_message);
- case InternalState::CLIENT_WAITING_FOR_SERVER_INIT:
- return ParseServerInitUkey2Message(handshake_message);
- case InternalState::SERVER_WAITING_FOR_CLIENT_FINISHED:
- return ParseClientFinishUkey2Message(handshake_message);
- default:
- std::ostringstream stream;
- stream << "Cannot parse message in state "
- << static_cast<int>(handshake_state_);
- SetError(stream.str());
- return {false, nullptr};
- }
-}
-
-std::unique_ptr<string> UKey2Handshake::GetVerificationString(int byte_length) {
- if (byte_length < 1 || byte_length > 32) {
- SetError("Minimum length is 1 byte, max is 32 bytes.");
- return nullptr;
- }
-
- if (handshake_state_ != InternalState::HANDSHAKE_VERIFICATION_NEEDED) {
- std::ostringstream stream;
- stream << "Unexpected state: " << static_cast<int>(handshake_state_);
- SetError(stream.str());
- return nullptr;
- }
-
- if (!our_key_pair_ || !our_key_pair_->private_key || !their_public_key_) {
- SetError("One of our private key or their public key is null.");
- return nullptr;
- }
-
- switch (handshake_cipher_) {
- case HandshakeCipher::P256_SHA512:
- derived_secret_key_ = CryptoOps::KeyAgreementSha256(
- *(our_key_pair_->private_key), *their_public_key_);
- break;
- default:
- // Unreachable.
- return nullptr;
- }
-
- if (!derived_secret_key_) {
- SetError("Failed to derive shared secret key.");
- return nullptr;
- }
-
- std::unique_ptr<string> auth_string = CryptoOps::Hkdf(
- derived_secret_key_->data().String(),
- string(kUkey2VerificationStringSalt, sizeof(kUkey2VerificationStringSalt)),
- wrapped_client_init_ + wrapped_server_init_);
-
- handshake_state_ = InternalState::HANDSHAKE_VERIFICATION_IN_PROGRESS;
- return auth_string;
-}
-
-bool UKey2Handshake::VerifyHandshake() {
- if (handshake_state_ != InternalState::HANDSHAKE_VERIFICATION_IN_PROGRESS) {
- std::ostringstream stream;
- stream << "Unexpected state: " << static_cast<int>(handshake_state_);
- SetError(stream.str());
- return false;
- }
-
- handshake_state_ = InternalState::HANDSHAKE_FINISHED;
- return true;
-}
-
-std::unique_ptr<D2DConnectionContextV1> UKey2Handshake::ToConnectionContext() {
- if (InternalState::HANDSHAKE_FINISHED != handshake_state_) {
- std::ostringstream stream;
- stream << "ToConnectionContext can only be called when handshake is "
- << "completed, but current state is "
- << static_cast<int>(handshake_state_);
- SetError(stream.str());
- return nullptr;
- }
-
- if (!derived_secret_key_) {
- SetError("Derived key is null.");
- return nullptr;
- }
-
- string info = wrapped_client_init_ + wrapped_server_init_;
- std::unique_ptr<string> master_key_data = CryptoOps::Hkdf(
- derived_secret_key_->data().String(), kUkey2HkdfSalt, info);
-
- if (!master_key_data) {
- SetError("Failed to create master key.");
- return nullptr;
- }
-
- // Derive separate encode keys for both client and server.
- CryptoOps::SecretKey master_key(*master_key_data, CryptoOps::AES_256_KEY);
- std::unique_ptr<CryptoOps::SecretKey> client_key =
- D2DCryptoOps::DeriveNewKeyForPurpose(master_key, "client");
- std::unique_ptr<CryptoOps::SecretKey> server_key =
- D2DCryptoOps::DeriveNewKeyForPurpose(master_key, "server");
- if (!client_key || !server_key) {
- SetError("Failed to derive client or server key.");
- return nullptr;
- }
-
- handshake_state_ = InternalState::HANDSHAKE_ALREADY_USED;
-
- return std::unique_ptr<D2DConnectionContextV1>(new D2DConnectionContextV1(
- handshake_role_ == HandshakeRole::CLIENT ? *client_key : *server_key,
- handshake_role_ == HandshakeRole::CLIENT ? *server_key : *client_key,
- 0 /* initial encode sequence number */,
- 0 /* initial decode sequence number */));
-}
-
-UKey2Handshake::ParseResult UKey2Handshake::ParseClientInitUkey2Message(
- const string& handshake_message) {
- // Deserialize the protobuf.
- Ukey2Message message;
- if (!message.ParseFromString(handshake_message)) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_MESSAGE,
- "Can't parse message 1.");
- }
-
- // Verify that message_type == CLIENT_INIT.
- if (!message.has_message_type() ||
- message.message_type() != Ukey2Message::CLIENT_INIT) {
- return CreateFailedResultWithAlert(
- Ukey2Alert::BAD_MESSAGE,
- "Expected, but did not find ClientInit message type.");
- }
-
- // Derserialize message_data as a ClientInit message.
- if (!message.has_message_data()) {
- return CreateFailedResultWithAlert(
- Ukey2Alert::BAD_MESSAGE_DATA,
- "Expected message data, but did not find it.");
- }
-
- Ukey2ClientInit client_init;
- if (!client_init.ParseFromString(message.message_data())) {
- return CreateFailedResultWithAlert(
- Ukey2Alert::BAD_MESSAGE_DATA,
- "Can't parse message data into ClientInit.");
- }
-
- // Check that version == VERSION.
- if (!client_init.has_version()) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_VERSION,
- "ClientInit missing version.");
- }
- if (client_init.version() != kVersion) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_VERSION,
- "ClientInit version mismatch.");
- }
-
- // Check that random is exactly kNonceLengthInBytes.
- if (!client_init.has_random()) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_RANDOM,
- "ClientInit missing random.");
- }
- if (client_init.random().length() != kNonceLengthInBytes) {
- return CreateFailedResultWithAlert(
- Ukey2Alert::BAD_RANDOM, "ClientInit has incorrect nonce length.");
- }
-
- // Check to see if any of the handshake_cipher in handshake_cipher_commitment
- // are acceptable. Servers should select the first ahdnshake_cipher that it
- // finds acceptable to support clients signalling deprecated but supported
- // HandshakeCiphers. If no handshake_cipher is acceptable (or there are no
- // HandshakeCiphers in the message), the server sends a BAD_HANDSHAKE_CIPHER
- // alert message.
- if (client_init.cipher_commitments_size() == 0) {
- return CreateFailedResultWithAlert(
- Ukey2Alert::BAD_HANDSHAKE_CIPHER,
- "ClientInit is missing cipher commitments.");
- }
-
- for (const Ukey2ClientInit::CipherCommitment& commitment :
- client_init.cipher_commitments()) {
- if (!commitment.has_handshake_cipher() || !commitment.has_commitment() ||
- commitment.commitment().empty()) {
- return CreateFailedResultWithAlert(
- Ukey2Alert::BAD_HANDSHAKE_CIPHER,
- "ClientInit has improperly formatted cipher commitment.");
- }
-
- // TODO(aczeskis): for now we only support one cipher, eventually support
- // more.
- if (commitment.handshake_cipher() == static_cast<int>(handshake_cipher_)) {
- peer_commitment_ = commitment.commitment();
- }
- }
-
- if (peer_commitment_.empty()) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_HANDSHAKE_CIPHER,
- "No acceptable commitments found");
- }
-
- // Checks that next_protocol contains a protocol that the server supports. We
- // currently only support one protocol.
- if (!client_init.has_next_protocol() ||
- client_init.next_protocol() != kNextProtocol) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_NEXT_PROTOCOL,
- "Incorrect next protocol.");
- }
-
- // Store raw message for AUTH_STRING computation.
- wrapped_client_init_ = handshake_message;
- handshake_state_ = InternalState::SERVER_AFTER_CLIENT_INIT;
- return CreateSuccessResult();
-}
-
-UKey2Handshake::ParseResult UKey2Handshake::ParseServerInitUkey2Message(
- const string& handshake_message) {
- // Deserialize the protobuf.
- Ukey2Message message;
- if (!message.ParseFromString(handshake_message)) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_MESSAGE,
- "Can't parse message 2.");
- }
-
- // Verify that message_type == SERVER_INIT.
- if (!message.has_message_type() ||
- message.message_type() != Ukey2Message::SERVER_INIT) {
- return CreateFailedResultWithAlert(
- Ukey2Alert::BAD_MESSAGE,
- "Expected, but did not find SERVER_INIT message type.");
- }
-
- // Derserialize message_data as a ServerInit message.
- if (!message.has_message_data()) {
- return CreateFailedResultWithAlert(
- Ukey2Alert::BAD_MESSAGE_DATA,
- "Expected message data, but did not find it.");
- }
-
- Ukey2ServerInit server_init;
- if (!server_init.ParseFromString(message.message_data())) {
- return CreateFailedResultWithAlert(
- Ukey2Alert::BAD_MESSAGE_DATA,
- "Can't parse message data into ServerInit.");
- }
-
- // Check that version == VERSION.
- if (!server_init.has_version()) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_VERSION,
- "ServerInit missing version.");
- }
- if (server_init.version() != kVersion) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_VERSION,
- "ServerInit version mismatch.");
- }
-
- // Check that random is exactly kNonceLengthInBytes.
- if (!server_init.has_random()) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_RANDOM,
- "ServerInit missing random.");
- }
- if (server_init.random().length() != kNonceLengthInBytes) {
- return CreateFailedResultWithAlert(
- Ukey2Alert::BAD_RANDOM, "ServerInit has incorrect nonce length.");
- }
-
- // Check that the handshake_cipher matches a handshake cipher that was sent in
- // ClientInit::cipher_commitments().
- if (!server_init.has_handshake_cipher()) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_HANDSHAKE_CIPHER,
- "No handshake cipher found.");
- }
-
- Ukey2HandshakeCipher cipher = server_init.handshake_cipher();
- HandshakeCipher server_cipher;
- switch (static_cast<HandshakeCipher>(cipher)) {
- case HandshakeCipher::P256_SHA512:
- server_cipher = static_cast<HandshakeCipher>(cipher);
- break;
- default:
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_HANDSHAKE_CIPHER,
- "No acceptable handshake found.");
- }
-
- // Check that public_key parses into a correct public key structure.
- if (!server_init.has_public_key()) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_PUBLIC_KEY,
- "No public key found in ServerInit.");
- }
-
- their_public_key_ = ParsePublicKey(server_init.public_key());
- if (!their_public_key_) {
- return CreateFailedResultWithAlert(Ukey2Alert::BAD_PUBLIC_KEY,
- "Failed to parse public key.");
- }
-
- // Store raw message for AUTH_STRING computation.
- wrapped_server_init_ = handshake_message;
- handshake_state_ = InternalState::CLIENT_AFTER_SERVER_INIT;
- return CreateSuccessResult();
-}
-
-UKey2Handshake::ParseResult UKey2Handshake::ParseClientFinishUkey2Message(
- const string& handshake_message) {
- // Deserialize the protobuf.
- Ukey2Message message;
- if (!message.ParseFromString(handshake_message)) {
- return CreateFailedResultWithoutAlert("Can't parse message 3.");
- }
-
- // Verify that message_type == CLIENT_FINISH.
- if (!message.has_message_type() ||
- message.message_type() != Ukey2Message::CLIENT_FINISH) {
- return CreateFailedResultWithoutAlert(
- "Expected, but did not find CLIENT_FINISH message type.");
- }
-
- // Verify that the hash of the CLientFinished message matches the expected
- // commitment from ClientInit.
- if (!VerifyCommitment(handshake_message)) {
- return CreateFailedResultWithoutAlert(last_error_);
- }
-
- // Deserialize message_data as a ClientFinished message.
- if (!message.has_message_data()) {
- return CreateFailedResultWithoutAlert(
- "Expected message data, but didn't find it.");
- }
-
- Ukey2ClientFinished client_finished;
- if (!client_finished.ParseFromString(message.message_data())) {
- return CreateFailedResultWithoutAlert("Failed to parse ClientFinished.");
- }
-
- // Check that public_key parses into a correct public key structure.
- if (!client_finished.has_public_key()) {
- return CreateFailedResultWithoutAlert(
- "No public key found in ClientFinished.");
- }
-
- their_public_key_ = ParsePublicKey(client_finished.public_key());
- if (!their_public_key_) {
- return CreateFailedResultWithoutAlert("Failed to parse public key.");
- }
-
- handshake_state_ = InternalState::HANDSHAKE_VERIFICATION_NEEDED;
- return CreateSuccessResult();
-}
-
-UKey2Handshake::ParseResult UKey2Handshake::CreateFailedResultWithAlert(
- Ukey2Alert::AlertType alert_type, const string& error_message) {
- if (!Ukey2Alert_AlertType_IsValid(alert_type)) {
- std::ostringstream stream;
- stream << "Unknown alert type: " << static_cast<int>(alert_type);
- SetError(stream.str());
- return {false, nullptr};
- }
-
- Ukey2Alert alert;
- alert.set_type(alert_type);
- if (!error_message.empty()) {
- alert.set_error_message(error_message);
- }
-
- std::unique_ptr<string> alert_message =
- MakeUkey2Message(Ukey2Message::ALERT, alert.SerializeAsString());
-
- SetError(error_message);
- ParseResult result{false, std::move(alert_message)};
- return result;
-}
-
-UKey2Handshake::ParseResult
-UKey2Handshake::CreateFailedResultWithoutAlert(const string& error_message) {
- SetError(error_message);
- return {false, nullptr};
-}
-
-UKey2Handshake::ParseResult UKey2Handshake::CreateSuccessResult() {
- return {true, nullptr};
-}
-
-bool UKey2Handshake::VerifyCommitment(const string& handshake_message) {
- std::unique_ptr<ByteBuffer> actual_client_finish_hash;
- switch (handshake_cipher_) {
- case HandshakeCipher::P256_SHA512:
- actual_client_finish_hash =
- CryptoOps::Sha512(ByteBuffer(handshake_message));
- break;
- default:
- // Unreachable.
- return false;
- }
-
- if (!actual_client_finish_hash) {
- SetError("Failed to hash ClientFinish message.");
- return false;
- }
-
- // Note: Equals() is a time constant comparison operation.
- if (!actual_client_finish_hash->Equals(peer_commitment_)) {
- SetError("Failed to verify commitment.");
- return false;
- }
-
- return true;
-}
-
-std::unique_ptr<Ukey2ClientInit::CipherCommitment>
-UKey2Handshake::GenerateP256Sha512Commitment() {
- // Generate the corresponding ClientFinished message if it's not done yet.
- if (raw_message3_map_.count(HandshakeCipher::P256_SHA512) == 0) {
- if (!our_key_pair_ || !our_key_pair_->public_key) {
- SetError("Invalid public key.");
- return nullptr;
- }
-
- std::unique_ptr<GenericPublicKey> generic_public_key =
- PublicKeyProtoUtil::EncodePublicKey(*(our_key_pair_->public_key));
- if (!generic_public_key) {
- SetError("Failed to encode generic public key.");
- return nullptr;
- }
-
- Ukey2ClientFinished client_finished;
- client_finished.set_public_key(generic_public_key->SerializeAsString());
- std::unique_ptr<string> serialized_ukey2_message = MakeUkey2Message(
- Ukey2Message::CLIENT_FINISH, client_finished.SerializeAsString());
- if (!serialized_ukey2_message) {
- SetError("Failed to serialized Ukey2Message.");
- return nullptr;
- }
-
- raw_message3_map_[HandshakeCipher::P256_SHA512] = *serialized_ukey2_message;
- }
-
- // Create the SHA512 commitment from raw message 3.
- std::unique_ptr<ByteBuffer> commitment = CryptoOps::Sha512(
- ByteBuffer(raw_message3_map_[HandshakeCipher::P256_SHA512]));
- if (!commitment) {
- SetError("Failed to hash message for commitment.");
- return nullptr;
- }
-
- // Wrap the commitment in a proto.
- std::unique_ptr<Ukey2ClientInit::CipherCommitment>
- handshake_cipher_commitment(new Ukey2ClientInit::CipherCommitment());
- handshake_cipher_commitment->set_handshake_cipher(P256_SHA512);
- handshake_cipher_commitment->set_commitment(commitment->String());
-
- return handshake_cipher_commitment;
-}
-
-std::unique_ptr<string> UKey2Handshake::MakeClientInitUkey2Message() {
- std::unique_ptr<ByteBuffer> nonce =
- CryptoOps::SecureRandom(kNonceLengthInBytes);
- if (!nonce) {
- SetError("Failed to generate nonce.");
- return nullptr;
- }
-
- Ukey2ClientInit client_init;
- client_init.set_version(kVersion);
- client_init.set_random(nonce->String());
- client_init.set_next_protocol(kNextProtocol);
-
- // At the moment, we only support one cipher.
- std::unique_ptr<Ukey2ClientInit::CipherCommitment>
- handshake_cipher_commitment = GenerateP256Sha512Commitment();
- if (!handshake_cipher_commitment) {
- // |last_error_| already set.
- return nullptr;
- }
- *(client_init.add_cipher_commitments()) = *handshake_cipher_commitment;
-
- return MakeUkey2Message(Ukey2Message::CLIENT_INIT,
- client_init.SerializeAsString());
-}
-
-std::unique_ptr<string> UKey2Handshake::MakeServerInitUkey2Message() {
- std::unique_ptr<ByteBuffer> nonce =
- CryptoOps::SecureRandom(kNonceLengthInBytes);
- if (!nonce) {
- SetError("Failed to generate nonce.");
- return nullptr;
- }
-
- if (!our_key_pair_ || !our_key_pair_->public_key) {
- SetError("Invalid key pair.");
- return nullptr;
- }
-
- std::unique_ptr<GenericPublicKey> public_key =
- PublicKeyProtoUtil::EncodePublicKey(*(our_key_pair_->public_key));
- if (!public_key) {
- SetError("Failed to encode public key.");
- return nullptr;
- }
-
- Ukey2ServerInit server_init;
- server_init.set_version(kVersion);
- server_init.set_random(nonce->String());
- server_init.set_handshake_cipher(
- static_cast<Ukey2HandshakeCipher>(handshake_cipher_));
- server_init.set_public_key(public_key->SerializeAsString());
-
- return MakeUkey2Message(Ukey2Message::SERVER_INIT,
- server_init.SerializeAsString());
-}
-
-// Generates the serialized representation of a Ukey2Message based on the
-// provided |type| and |data|. On error, returns nullptr and writes error
-// message to |out_error|.
-std::unique_ptr<string> UKey2Handshake::MakeUkey2Message(
- Ukey2Message::Type type, const string& data) {
- Ukey2Message message;
- if (!Ukey2Message::Type_IsValid(type)) {
- std::ostringstream stream;
- stream << "Invalid message type: " << type;
- SetError(stream.str());
- return nullptr;
- }
- message.set_message_type(type);
-
- // Only ALERT messages can have a blank data field.
- if (type != Ukey2Message::ALERT) {
- if (data.length() == 0) {
- SetError("Cannot send empty message data for non-alert messages");
- return nullptr;
- }
- }
- message.set_message_data(data);
-
- std::unique_ptr<string> serialized(new string());
- message.SerializeToString(serialized.get());
- return serialized;
-}
-
-void UKey2Handshake::SetError(const string& error_message) {
- handshake_state_ = InternalState::HANDSHAKE_ERROR;
- last_error_ = error_message;
-}
-
-} // namespace securegcm
diff --git a/src/main/cpp/src/securegcm/ukey2_shell.cc b/src/main/cpp/src/securegcm/ukey2_shell.cc
deleted file mode 100644
index 99a35a8..0000000
--- a/src/main/cpp/src/securegcm/ukey2_shell.cc
+++ /dev/null
@@ -1,297 +0,0 @@
-// Copyright 2020 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
-//
-// https://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.
-
-// The ukey2_shell binary is a command-line based wrapper, exercising the
-// UKey2Handshake class. Its main use is to be run in a Java test, testing the
-// compatibility of the Java and C++ implementations.
-//
-// This program can be run in two modes, initiator or responder (default is
-// initiator):
-// ukey2_shell --mode=initiator --verification_string_length=32
-// ukey2_shell --mode=responder --verification_string_length=32
-//
-// In initiator mode, the program performs the initiator handshake, and in
-// responder mode, it performs the responder handshake.
-//
-// After the handshake is done, the program establishes a secure connection and
-// enters a loop in which it processes the following commands:
-// * encrypt <payload>: encrypts the payload and prints it.
-// * decrypt <message>: decrypts the message and prints the payload.
-// * session_unique: prints the session unique value.
-//
-// IO is performed on stdin and stdout. To provide frame control, all frames
-// will have the following simple format:
-// [ length | bytes ]
-// where |length| is a 4 byte big-endian encoded unsigned integer.
-#include <cassert>
-#include <cstdio>
-#include <iostream>
-#include <memory>
-
-#include "securegcm/ukey2_handshake.h"
-#include "absl/container/fixed_array.h"
-#include "absl/flags/flag.h"
-#include "absl/flags/parse.h"
-
-#define LOG(ERROR) std::cerr
-#define CHECK_EQ(a, b) do { if ((a) != (b)) abort(); } while(0)
-
-ABSL_FLAG(
- int, verification_string_length, 32,
- "The length in bytes of the verification string. Must be a value between 1"
- "and 32.");
-ABSL_FLAG(string, mode, "initiator",
- "The mode to run as: one of [initiator, responder]");
-
-namespace securegcm {
-
-namespace {
-
-// Writes |message| to stdout in the frame format.
-void WriteFrame(const string& message) {
- // Write length of |message| in little-endian.
- const uint32_t length = message.length();
- fputc((length >> (3 * 8)) & 0xFF, stdout);
- fputc((length >> (2 * 8)) & 0xFF, stdout);
- fputc((length >> (1 * 8)) & 0xFF, stdout);
- fputc((length >> (0 * 8)) & 0xFF, stdout);
-
- // Write message to stdout.
- CHECK_EQ(message.length(),
- fwrite(message.c_str(), 1, message.length(), stdout));
- CHECK_EQ(0, fflush(stdout));
-}
-
-// Returns a message read from stdin after parsing it from the frame format.
-string ReadFrame() {
- // Read length of the frame from the stream.
- uint8_t length_data[sizeof(uint32_t)];
- CHECK_EQ(sizeof(uint32_t), fread(&length_data, 1, sizeof(uint32_t), stdin));
-
- uint32_t length = 0;
- length |= static_cast<uint32_t>(length_data[0]) << (3 * 8);
- length |= static_cast<uint32_t>(length_data[1]) << (2 * 8);
- length |= static_cast<uint32_t>(length_data[2]) << (1 * 8);
- length |= static_cast<uint32_t>(length_data[3]) << (0 * 8);
-
- // Read |length| bytes from the stream.
- absl::FixedArray<char> buffer(length);
- CHECK_EQ(length, fread(buffer.data(), 1, length, stdin));
-
- return string(buffer.data(), length);
-}
-
-} // namespace
-
-// Handles the runtime of the program in initiator or responder mode.
-class UKey2Shell {
- public:
- explicit UKey2Shell(int verification_string_length);
- ~UKey2Shell();
-
- // Runs the shell, performing the initiator handshake for authentication.
- bool RunAsInitiator();
-
- // Runs the shell, performing the responder handshake for authentication.
- bool RunAsResponder();
-
- private:
- // Writes the next handshake message obtained from |ukey2_handshake_| to
- // stdout.
- // If an error occurs, |tag| is logged.
- bool WriteNextHandshakeMessage(const string& tag);
-
- // Reads the next handshake message from stdin and parses it using
- // |ukey2_handshake_|.
- // If an error occurs, |tag| is logged.
- bool ReadNextHandshakeMessage(const string& tag);
-
- // Writes the verification string to stdout and waits for a confirmation from
- // stdin.
- bool ConfirmVerificationString();
-
- // After authentication is completed, this function runs the loop handing the
- // secure connection.
- bool RunSecureConnectionLoop();
-
- std::unique_ptr<UKey2Handshake> ukey2_handshake_;
- const int verification_string_length_;
-};
-
-UKey2Shell::UKey2Shell(int verification_string_length)
- : verification_string_length_(verification_string_length) {}
-
-UKey2Shell::~UKey2Shell() {}
-
-bool UKey2Shell::WriteNextHandshakeMessage(const string& tag) {
- const std::unique_ptr<string> message =
- ukey2_handshake_->GetNextHandshakeMessage();
- if (!message) {
- LOG(ERROR) << "Failed to create [" << tag
- << "] message: " << ukey2_handshake_->GetLastError();
- return false;
- }
- WriteFrame(*message);
- return true;
-}
-
-bool UKey2Shell::ReadNextHandshakeMessage(const string& tag) {
- const string message = ReadFrame();
- const UKey2Handshake::ParseResult result =
- ukey2_handshake_->ParseHandshakeMessage(message);
- if (!result.success) {
- LOG(ERROR) << "Failed to parse [" << tag
- << "] message: " << ukey2_handshake_->GetLastError();
- if (result.alert_to_send) {
- WriteFrame(*result.alert_to_send);
- }
- return false;
- }
- return true;
-}
-
-bool UKey2Shell::ConfirmVerificationString() {
- const std::unique_ptr<string> auth_string =
- ukey2_handshake_->GetVerificationString(verification_string_length_);
- if (!auth_string) {
- LOG(ERROR) << "Failed to get verification string: "
- << ukey2_handshake_->GetLastError();
- return false;
- }
- WriteFrame(*auth_string);
-
- // Wait for ack message.
- const string message = ReadFrame();
- if (message != "ok") {
- LOG(ERROR) << "Expected string 'ok'";
- return false;
- }
- ukey2_handshake_->VerifyHandshake();
- return true;
-}
-
-bool UKey2Shell::RunSecureConnectionLoop() {
- const std::unique_ptr<D2DConnectionContextV1> connection_context =
- ukey2_handshake_->ToConnectionContext();
- if (!connection_context) {
- LOG(ERROR) << "Failed to create connection context: "
- << ukey2_handshake_->GetLastError();
- return false;
- }
-
- for (;;) {
- // Parse the next expression.
- const string expression = ReadFrame();
- const size_t pos = expression.find(" ");
- if (pos == std::string::npos) {
- LOG(ERROR) << "Invalid command in connection loop.";
- return false;
- }
- const string command = expression.substr(0, pos);
-
- if (command == "encrypt") {
- const string payload = expression.substr(pos + 1, expression.length());
- std::unique_ptr<string> encoded_message =
- connection_context->EncodeMessageToPeer(payload);
- if (!encoded_message) {
- LOG(ERROR) << "Failed to encode payload of size " << payload.length();
- return false;
- }
- WriteFrame(*encoded_message);
- } else if (command == "decrypt") {
- const string message = expression.substr(pos + 1, expression.length());
- std::unique_ptr<string> decoded_payload =
- connection_context->DecodeMessageFromPeer(message);
- if (!decoded_payload) {
- LOG(ERROR) << "Failed to decode message of size " << message.length();
- return false;
- }
- WriteFrame(*decoded_payload);
- } else if (command == "session_unique") {
- std::unique_ptr<string> session_unique =
- connection_context->GetSessionUnique();
- if (!session_unique) {
- LOG(ERROR) << "Failed to get session unique.";
- return false;
- }
- WriteFrame(*session_unique);
- } else {
- LOG(ERROR) << "Unrecognized command: " << command;
- return false;
- }
- }
-}
-
-bool UKey2Shell::RunAsInitiator() {
- ukey2_handshake_ = UKey2Handshake::ForInitiator(
- UKey2Handshake::HandshakeCipher::P256_SHA512);
- if (!ukey2_handshake_) {
- LOG(ERROR) << "Unable to create UKey2Handshake";
- return false;
- }
-
- // Perform handshake.
- if (!WriteNextHandshakeMessage("Initiator Init")) return false;
- if (!ReadNextHandshakeMessage("Responder Init")) return false;
- if (!WriteNextHandshakeMessage("Initiator Finish")) return false;
- if (!ConfirmVerificationString()) return false;
-
- // Create a connection context.
- return RunSecureConnectionLoop();
-}
-
-bool UKey2Shell::RunAsResponder() {
- ukey2_handshake_ = UKey2Handshake::ForResponder(
- UKey2Handshake::HandshakeCipher::P256_SHA512);
- if (!ukey2_handshake_) {
- LOG(ERROR) << "Unable to create UKey2Handshake";
- return false;
- }
-
- // Perform handshake.
- if (!ReadNextHandshakeMessage("Initiator Init")) return false;
- if (!WriteNextHandshakeMessage("Responder Init")) return false;
- if (!ReadNextHandshakeMessage("Initiator Finish")) return false;
- if (!ConfirmVerificationString()) return false;
-
- // Create a connection context.
- return RunSecureConnectionLoop();
-}
-
-} // namespace securegcm
-
-int main(int argc, char** argv) {
- absl::ParseCommandLine(argc, argv);
-
- const int verification_string_length =
- absl::GetFlag(FLAGS_verification_string_length);
- if (verification_string_length < 1 || verification_string_length > 32) {
- LOG(ERROR) << "Invalid flag value, verification_string_length: "
- << verification_string_length;
- return 1;
- }
-
- securegcm::UKey2Shell shell(verification_string_length);
- int exit_code = 0;
- const string mode = absl::GetFlag(FLAGS_mode);
- if (mode == "initiator") {
- exit_code = !shell.RunAsInitiator();
- } else if (mode == "responder") {
- exit_code = !shell.RunAsResponder();
- } else {
- LOG(ERROR) << "Invalid flag value, mode: " << mode;
- exit_code = 1;
- }
- return exit_code;
-}
diff --git a/src/main/cpp/test/securegcm/CMakeLists.txt b/src/main/cpp/test/securegcm/CMakeLists.txt
deleted file mode 100644
index 272f919..0000000
--- a/src/main/cpp/test/securegcm/CMakeLists.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright 2020 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
-#
-# https://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.
-
-add_executable(ukey2_test
- d2d_connection_context_v1_test.cc
- d2d_crypto_ops_test.cc
- java_util_test.cc
-)
-
-target_link_libraries(ukey2_test
- PUBLIC
- ukey2
- gtest
- gtest_main
-)
-
-add_test(
- NAME ukey2_test
- COMMAND ukey2_test
-)
diff --git a/src/main/cpp/test/securegcm/d2d_connection_context_v1_test.cc b/src/main/cpp/test/securegcm/d2d_connection_context_v1_test.cc
deleted file mode 100644
index daf69d1..0000000
--- a/src/main/cpp/test/securegcm/d2d_connection_context_v1_test.cc
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2020 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
-//
-// https://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.
-
-#include "securegcm/d2d_connection_context_v1.h"
-
-#include "securegcm/d2d_crypto_ops.h"
-#include "gtest/gtest.h"
-
-namespace securegcm {
-
-using securemessage::CryptoOps;
-
-namespace {
-
-// The encode and decode keys should be 32 bytes.
-const char kEncodeKeyData[] = "initiator_encode_key_for_aes_256";
-const char kDecodeKeyData[] = "initiator_decode_key_for_aes_256";
-
-} // namespace
-
-// A friend to access the private variables of D2DConnectionContextV1.
-class D2DConnectionContextV1Peer {
- public:
- explicit D2DConnectionContextV1Peer(const std::string& savedSessionInfo) {
- context_ = D2DConnectionContextV1::FromSavedSession(savedSessionInfo);
- }
-
- D2DConnectionContextV1* GetContext() { return context_.get(); }
-
- uint32_t GetEncodeSequenceNumber() {
- return context_->encode_sequence_number_;
- }
-
- uint32_t GetDecodeSequenceNumber() {
- return context_->decode_sequence_number_;
- }
-
- private:
- std::unique_ptr<D2DConnectionContextV1> context_;
-};
-
-TEST(D2DConnectionContextionV1Test, SaveSession) {
- CryptoOps::SecretKey encodeKey = CryptoOps::SecretKey(
- kEncodeKeyData, CryptoOps::KeyAlgorithm::AES_256_KEY);
- CryptoOps::SecretKey decodeKey = CryptoOps::SecretKey(
- kDecodeKeyData, CryptoOps::KeyAlgorithm::AES_256_KEY);
-
- D2DConnectionContextV1 initiator =
- D2DConnectionContextV1(encodeKey, decodeKey, 0, 1);
- D2DConnectionContextV1 responder =
- D2DConnectionContextV1(decodeKey, encodeKey, 1, 0);
-
- std::unique_ptr<std::string> initiatorSavedSessionState =
- initiator.SaveSession();
- std::unique_ptr<std::string> responderSavedSessionState =
- responder.SaveSession();
-
- D2DConnectionContextV1Peer restoredInitiator =
- D2DConnectionContextV1Peer(*initiatorSavedSessionState);
- D2DConnectionContextV1Peer restoredResponder =
- D2DConnectionContextV1Peer(*responderSavedSessionState);
-
- // Verify internal state matches initialization.
- EXPECT_EQ(0, restoredInitiator.GetEncodeSequenceNumber());
- EXPECT_EQ(1, restoredInitiator.GetDecodeSequenceNumber());
- EXPECT_EQ(1, restoredResponder.GetEncodeSequenceNumber());
- EXPECT_EQ(0, restoredResponder.GetDecodeSequenceNumber());
-
- EXPECT_EQ(*restoredInitiator.GetContext()->GetSessionUnique(),
- *restoredResponder.GetContext()->GetSessionUnique());
-
- const std::string message = "ping";
-
- // Ensure that they can still talk to one another.
- std::string encodedMessage =
- *restoredInitiator.GetContext()->EncodeMessageToPeer(message);
- std::string decodedMessage =
- *restoredResponder.GetContext()->DecodeMessageFromPeer(encodedMessage);
-
- EXPECT_EQ(message, decodedMessage);
-
- encodedMessage =
- *restoredResponder.GetContext()->EncodeMessageToPeer(message);
- decodedMessage =
- *restoredInitiator.GetContext()->DecodeMessageFromPeer(encodedMessage);
-
- EXPECT_EQ(message, decodedMessage);
-}
-
-TEST(D2DConnectionContextionV1Test, SaveSession_TooShort) {
- CryptoOps::SecretKey encodeKey = CryptoOps::SecretKey(
- kEncodeKeyData, CryptoOps::KeyAlgorithm::AES_256_KEY);
- CryptoOps::SecretKey decodeKey = CryptoOps::SecretKey(
- kDecodeKeyData, CryptoOps::KeyAlgorithm::AES_256_KEY);
-
- D2DConnectionContextV1 initiator =
- D2DConnectionContextV1(encodeKey, decodeKey, 0, 1);
-
- std::unique_ptr<std::string> initiatorSavedSessionState =
- initiator.SaveSession();
-
- // Try to rebuild the context with a shorter session state.
- std::string shortSessionState = initiatorSavedSessionState->substr(
- 0, initiatorSavedSessionState->size() - 1);
-
- D2DConnectionContextV1Peer restoredInitiator =
- D2DConnectionContextV1Peer(shortSessionState);
-
- // nullptr is returned on error. It should not crash.
- EXPECT_EQ(restoredInitiator.GetContext(), nullptr);
-}
-
-} // namespace securegcm
diff --git a/src/main/cpp/test/securegcm/d2d_crypto_ops_test.cc b/src/main/cpp/test/securegcm/d2d_crypto_ops_test.cc
deleted file mode 100644
index 5acbb89..0000000
--- a/src/main/cpp/test/securegcm/d2d_crypto_ops_test.cc
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2020 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
-//
-// https://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.
-
-#include "securegcm/d2d_crypto_ops.h"
-
-#include "gtest/gtest.h"
-#include "securemessage/crypto_ops.h"
-#include "securemessage/secure_message_builder.h"
-
-namespace securegcm {
-
-using securemessage::CryptoOps;
-using securemessage::HeaderAndBody;
-using securemessage::SecureMessage;
-
-namespace {
-
-const char kPayloadData[] = "Test payload";
-const char kSecretKeyData[] = "secret key must be 32 bytes long";
-const char kOtherSecretKeyData[] = "other secret key****************";
-const char kInvalidSigncryptedMessage[] = "Not a protobuf";
-const int kSupportedProtocolVersion = 1;
-const int kUnsupportedProtocolVersion = 2;
-
-} // namespace
-
-class D2DCryptoOpsTest : public testing::Test {
- public:
- D2DCryptoOpsTest()
- : payload_(DEVICE_TO_DEVICE_MESSAGE, kPayloadData),
- secret_key_(kSecretKeyData, CryptoOps::AES_256_KEY) {}
-
- protected:
- const D2DCryptoOps::Payload payload_;
- const CryptoOps::SecretKey secret_key_;
-};
-
-TEST_F(D2DCryptoOpsTest, Signcrypt_EmptyPayload) {
- // Signcrypting an empty payload should fail.
- D2DCryptoOps::Payload empty_payload(DEVICE_TO_DEVICE_MESSAGE, string());
- EXPECT_FALSE(D2DCryptoOps::SigncryptPayload(empty_payload, secret_key_));
-}
-
-TEST_F(D2DCryptoOpsTest, VerifyDecrypt_InvalidMessage) {
- // VerifyDecrypting an invalid payload should fail.
- EXPECT_FALSE(D2DCryptoOps::VerifyDecryptPayload(kInvalidSigncryptedMessage,
- secret_key_));
-}
-
-TEST_F(D2DCryptoOpsTest, VerifyDecrypt_NoPublicMetadata) {
- std::unique_ptr<string> signcrypted_message =
- D2DCryptoOps::SigncryptPayload(payload_, secret_key_);
-
- // Clear metadata field in header.
- SecureMessage secure_message;
- ASSERT_TRUE(secure_message.ParseFromString(*signcrypted_message));
- HeaderAndBody header_and_body;
- ASSERT_TRUE(
- header_and_body.ParseFromString(secure_message.header_and_body()));
- header_and_body.mutable_header()->clear_public_metadata();
- secure_message.set_header_and_body(header_and_body.SerializeAsString());
-
- // Decrypting the message should now fail.
- EXPECT_FALSE(D2DCryptoOps::VerifyDecryptPayload(
- secure_message.SerializeAsString(), secret_key_));
-}
-
-TEST_F(D2DCryptoOpsTest, VerifyDecrypt_ProtocolVersionNotSupported) {
- std::unique_ptr<string> signcrypted_message =
- D2DCryptoOps::SigncryptPayload(payload_, secret_key_);
-
- // Change version in metadata field in header to an unsupported version.
- SecureMessage secure_message;
- ASSERT_TRUE(secure_message.ParseFromString(*signcrypted_message));
- HeaderAndBody header_and_body;
- ASSERT_TRUE(
- header_and_body.ParseFromString(secure_message.header_and_body()));
- GcmMetadata metadata;
- ASSERT_TRUE(
- metadata.ParseFromString(header_and_body.header().public_metadata()));
- EXPECT_EQ(kSupportedProtocolVersion, metadata.version());
- metadata.set_version(kUnsupportedProtocolVersion);
- header_and_body.mutable_header()->set_public_metadata(
- metadata.SerializeAsString());
- secure_message.set_header_and_body(header_and_body.SerializeAsString());
-
- // Decrypting the message should now fail.
- EXPECT_FALSE(D2DCryptoOps::VerifyDecryptPayload(
- secure_message.SerializeAsString(), secret_key_));
-}
-
-TEST_F(D2DCryptoOpsTest, SigncryptThenVerifyDecrypt_SuccessWithSameKey) {
- // Signcrypt the payload.
- std::unique_ptr<string> signcrypted_message =
- D2DCryptoOps::SigncryptPayload(payload_, secret_key_);
- ASSERT_TRUE(signcrypted_message);
-
- // Decrypt the signcrypted message.
- std::unique_ptr<D2DCryptoOps::Payload> decrypted_payload =
- D2DCryptoOps::VerifyDecryptPayload(*signcrypted_message, secret_key_);
- ASSERT_TRUE(decrypted_payload);
-
- // Check that decrypted payload is the same.
- EXPECT_EQ(payload_.type(), decrypted_payload->type());
- EXPECT_EQ(payload_.message(), decrypted_payload->message());
-}
-
-TEST_F(D2DCryptoOpsTest, SigncryptThenVerifyDecrypt_FailsWithDifferentKey) {
- CryptoOps::SecretKey other_secret_key(kOtherSecretKeyData,
- CryptoOps::AES_256_KEY);
-
- // Signcrypt the payload with first secret key.
- std::unique_ptr<string> signcrypted_message =
- D2DCryptoOps::SigncryptPayload(payload_, secret_key_);
- ASSERT_TRUE(signcrypted_message);
-
- // Decrypting the signcrypted message with the other secret key should fail.
- EXPECT_FALSE(D2DCryptoOps::VerifyDecryptPayload(*signcrypted_message,
- other_secret_key));
-}
-
-TEST_F(D2DCryptoOpsTest, DeriveNewKeyForPurpose_ClientServer) {
- CryptoOps::SecretKey master_key(kSecretKeyData, CryptoOps::AES_256_KEY);
-
- std::unique_ptr<CryptoOps::SecretKey> derived_key1 =
- D2DCryptoOps::DeriveNewKeyForPurpose(master_key, "client");
- std::unique_ptr<CryptoOps::SecretKey> derived_key2 =
- D2DCryptoOps::DeriveNewKeyForPurpose(master_key, "server");
-
- ASSERT_TRUE(derived_key1);
- ASSERT_TRUE(derived_key2);
- EXPECT_EQ(CryptoOps::AES_256_KEY, derived_key1->algorithm());
- EXPECT_EQ(CryptoOps::AES_256_KEY, derived_key2->algorithm());
- EXPECT_NE(derived_key1->data().String(), derived_key2->data().String());
-}
-
-TEST_F(D2DCryptoOpsTest, DeriveNewKeyForPurpose_InvalidMasterKeySize) {
- CryptoOps::SecretKey master_key("Invalid Size", CryptoOps::AES_256_KEY);
- EXPECT_FALSE(D2DCryptoOps::DeriveNewKeyForPurpose(master_key, "purpose"));
-}
-
-TEST_F(D2DCryptoOpsTest, DeriveNewKeyForPurpose_PurposeEmpty) {
- CryptoOps::SecretKey master_key(kSecretKeyData, CryptoOps::AES_256_KEY);
- EXPECT_FALSE(D2DCryptoOps::DeriveNewKeyForPurpose(master_key, string()));
-}
-
-} // namespace securegcm
diff --git a/src/main/cpp/test/securegcm/java_util_test.cc b/src/main/cpp/test/securegcm/java_util_test.cc
deleted file mode 100644
index 20928fa..0000000
--- a/src/main/cpp/test/securegcm/java_util_test.cc
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2020 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
-//
-// https://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.
-
-#include "securegcm/java_util.h"
-
-#include <limits>
-
-#include "gtest/gtest.h"
-
-namespace securegcm {
-
-using securemessage::ByteBuffer;
-
-namespace {
-
-int32_t kMinInt32 = std::numeric_limits<int32_t>::min();
-int32_t kMaxInt32 = std::numeric_limits<int32_t>::max();
-
-} // namespace
-
-
-TEST(JavaUtilTest, TestJavaAdd_InRange) {
- EXPECT_EQ(2, java_util::JavaAdd(1, 1));
-}
-
-TEST(JavaUtilTest, TestJavaAdd_Underflow) {
- EXPECT_EQ(kMaxInt32, java_util::JavaAdd(kMinInt32, -1));
- EXPECT_EQ(kMaxInt32 - 1, java_util::JavaAdd(kMinInt32, -2));
- EXPECT_EQ(1, java_util::JavaAdd(kMinInt32, -kMaxInt32));
-}
-
-TEST(JavaUtilTest, TestJavaAdd_Overflow) {
- EXPECT_EQ(kMinInt32, java_util::JavaAdd(kMaxInt32, 1));
- EXPECT_EQ(kMinInt32 + 1, java_util::JavaAdd(kMaxInt32, 2));
- EXPECT_EQ(-2, java_util::JavaAdd(kMaxInt32, kMaxInt32));
-}
-
-TEST(JavaUtilTest, TestJavaMultiply_InRange) {
- EXPECT_EQ(4, java_util::JavaAdd(2, 2));
-}
-
-TEST(JavaUtilTest, TestJavaMultiply_Underflow) {
- EXPECT_EQ(0, java_util::JavaMultiply(kMinInt32, 2));
- EXPECT_EQ(-(kMinInt32 / 2), java_util::JavaMultiply(kMinInt32 / 2, 3));
- EXPECT_EQ(kMinInt32, java_util::JavaMultiply(kMinInt32, kMaxInt32));
-}
-
-TEST(JavaUtilTest, TestJavaMultiply_Overflow) {
- EXPECT_EQ(-2, java_util::JavaMultiply(kMaxInt32, 2));
- EXPECT_EQ(kMaxInt32 - 2, java_util::JavaMultiply(kMaxInt32, 3));
- EXPECT_EQ(1, java_util::JavaMultiply(kMaxInt32, kMaxInt32));
-}
-
-TEST(JavaUtilTest, TestJavaHashCode_EmptyBytes) {
- EXPECT_EQ(1, java_util::JavaHashCode(ByteBuffer()));
-}
-
-TEST(JavaUtilTest, TestJavaHashCode_LongByteArray) {
- const uint8_t kBytes[] = {
- 0x93, 0x75, 0xE1, 0x2E, 0x26, 0x28, 0x54, 0x8C, 0xD9, 0x5C, 0x48, 0x7A,
- 0x07, 0x53, 0x4E, 0xED, 0x28, 0x52, 0x5D, 0x41, 0xE3, 0x18, 0x84, 0x84,
- 0x5F, 0xF6, 0x89, 0x98, 0x25, 0x1E, 0xD9, 0x6C, 0x85, 0xF3, 0x5A, 0x83,
- 0x39, 0x37, 0x4E, 0x77, 0x95, 0xB5, 0x58, 0x7C, 0xD2, 0x55, 0xA0, 0x86,
- 0x13, 0x3F, 0xBF, 0x85, 0xD3, 0xE0, 0x28, 0x90, 0x17, 0x3D, 0x2E, 0xD4,
- 0x4D, 0x95, 0x9C, 0xAE, 0xAD, 0x8A, 0x05, 0x91, 0x5D, 0xC6, 0x4B, 0x09,
- 0xB2, 0xD9, 0x34, 0x64, 0x07, 0x7B, 0x07, 0x8C, 0xA6, 0xC7, 0x1C, 0x10,
- 0x34, 0xD4, 0x30, 0x80, 0x03, 0x4F, 0x2C, 0x70};
- const int32_t kExpectedHashCode = 1983685004;
- EXPECT_EQ(kExpectedHashCode,
- java_util::JavaHashCode(ByteBuffer(kBytes, sizeof(kBytes))));
-}
-
-} // namespace securegcm
diff --git a/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContext.java b/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContext.java
index fb4af63..09023a5 100644
--- a/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContext.java
+++ b/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContext.java
@@ -1,17 +1,17 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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 com.google.common.annotations.VisibleForTesting;
diff --git a/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextV0.java b/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextV0.java
index d0efa44..92aa02d 100644
--- a/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextV0.java
+++ b/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextV0.java
@@ -1,17 +1,17 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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 java.io.ByteArrayOutputStream;
diff --git a/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextV1.java b/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextV1.java
index 1566849..4b5fb5e 100644
--- a/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextV1.java
+++ b/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextV1.java
@@ -1,17 +1,17 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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 java.io.ByteArrayOutputStream;
diff --git a/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DCryptoOps.java b/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DCryptoOps.java
index a7203d1..48dc52f 100644
--- a/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DCryptoOps.java
+++ b/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DCryptoOps.java
@@ -1,17 +1,17 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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 com.google.common.annotations.VisibleForTesting;
@@ -55,11 +55,6 @@ class D2DCryptoOps {
(byte) 0x6D, (byte) 0xA8, (byte) 0x55, (byte) 0x10
};
- // Data passed to hkdf to create the key used by the initiator to encode messages.
- static final String INITIATOR_PURPOSE = "initiator";
- // Data passed to hkdf to create the key used by the responder to encode messages.
- static final String RESPONDER_PURPOSE = "responder";
-
// Don't instantiate
private D2DCryptoOps() { }
@@ -201,7 +196,7 @@ class D2DCryptoOps {
/**
* Used to derive a distinct key for each initiator and responder.
- *
+ *
* @param masterKey the source key used to derive the new key.
* @param purpose a string to make the new key different for each purpose.
* @return the derived {@link SecretKey}.
diff --git a/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DDiffieHellmanKeyExchangeHandshake.java b/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DDiffieHellmanKeyExchangeHandshake.java
index f929a3a..ae43d90 100644
--- a/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DDiffieHellmanKeyExchangeHandshake.java
+++ b/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DDiffieHellmanKeyExchangeHandshake.java
@@ -1,17 +1,17 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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 com.google.protobuf.InvalidProtocolBufferException;
@@ -64,6 +64,11 @@ import javax.crypto.SecretKey;
* </pre>
*/
public class D2DDiffieHellmanKeyExchangeHandshake implements D2DHandshakeContext {
+ // Data passed to hkdf to create the key used by the initiator to encode messages.
+ private static final String INITIATOR_PURPOSE = "initiator";
+ // Data passed to hkdf to create the key used by the responder to encode messages.
+ private static final String RESPONDER_PURPOSE = "responder";
+
private KeyPair ourKeyPair;
private PublicKey theirPublicKey;
private SecretKey initiatorEncodeKey;
@@ -171,10 +176,8 @@ public class D2DDiffieHellmanKeyExchangeHandshake implements D2DHandshakeContext
responderEncodeKey = masterKey;
break;
case D2DConnectionContextV1.PROTOCOL_VERSION:
- initiatorEncodeKey = D2DCryptoOps.deriveNewKeyForPurpose(masterKey,
- D2DCryptoOps.INITIATOR_PURPOSE);
- responderEncodeKey = D2DCryptoOps.deriveNewKeyForPurpose(masterKey,
- D2DCryptoOps.RESPONDER_PURPOSE);
+ initiatorEncodeKey = D2DCryptoOps.deriveNewKeyForPurpose(masterKey, INITIATOR_PURPOSE);
+ responderEncodeKey = D2DCryptoOps.deriveNewKeyForPurpose(masterKey, RESPONDER_PURPOSE);
break;
default:
throw new IllegalStateException("Unexpected protocol version: " + protocolVersionToUse);
@@ -235,10 +238,8 @@ public class D2DDiffieHellmanKeyExchangeHandshake implements D2DHandshakeContext
initiatorEncodeKey = masterKey;
responderEncodeKey = masterKey;
} else {
- initiatorEncodeKey = D2DCryptoOps.deriveNewKeyForPurpose(masterKey,
- D2DCryptoOps.INITIATOR_PURPOSE);
- responderEncodeKey = D2DCryptoOps.deriveNewKeyForPurpose(masterKey,
- D2DCryptoOps.RESPONDER_PURPOSE);
+ initiatorEncodeKey = D2DCryptoOps.deriveNewKeyForPurpose(masterKey, INITIATOR_PURPOSE);
+ responderEncodeKey = D2DCryptoOps.deriveNewKeyForPurpose(masterKey, RESPONDER_PURPOSE);
}
DeviceToDeviceMessage message =
diff --git a/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DHandshakeContext.java b/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DHandshakeContext.java
index 5fc1d7b..8c35d22 100644
--- a/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DHandshakeContext.java
+++ b/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DHandshakeContext.java
@@ -1,17 +1,17 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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;
/**
diff --git a/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DSpakeEd25519Handshake.java b/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DSpakeEd25519Handshake.java
new file mode 100644
index 0000000..da5abf1
--- /dev/null
+++ b/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DSpakeEd25519Handshake.java
@@ -0,0 +1,648 @@
+/* Copyright 2018 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
+ *
+ * https://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 com.google.common.annotations.VisibleForTesting;
+import com.google.protobuf.ByteString;
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.security.cryptauth.lib.securegcm.DeviceToDeviceMessagesProto.DeviceToDeviceMessage;
+import com.google.security.cryptauth.lib.securegcm.DeviceToDeviceMessagesProto.EcPoint;
+import com.google.security.cryptauth.lib.securegcm.DeviceToDeviceMessagesProto.SpakeHandshakeMessage;
+import com.google.security.cryptauth.lib.securegcm.Ed25519.Ed25519Exception;
+import com.google.security.cryptauth.lib.securegcm.TransportCryptoOps.Payload;
+import com.google.security.cryptauth.lib.securegcm.TransportCryptoOps.PayloadType;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.SignatureException;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Implements a {@link D2DHandshakeContext} by using SPAKE2 (Simple Password-Based Encrypted Key
+ * Exchange Protocol) on top of the Ed25519 curve.
+ * SPAKE2: http://www.di.ens.fr/~mabdalla/papers/AbPo05a-letter.pdf
+ * Ed25519: http://ed25519.cr.yp.to/
+ *
+ * <p>Usage:
+ * {@code
+ * // initiator:
+ * D2DHandshakeContext initiatorHandshakeContext =
+ * D2DSpakeEd25519Handshake.forInitiator(PASSWORD);
+ * byte[] initiatorMsg = initiatorHandshakeContext.getNextHandshakeMessage();
+ * // (send initiatorMsg to responder)
+ *
+ * // responder:
+ * D2DHandshakeContext responderHandshakeContext =
+ * D2DSpakeEd25519Handshake.forResponder(PASSWORD);
+ * responderHandshakeContext.parseHandshakeMessage(initiatorMsg);
+ * byte[] responderMsg = responderHandshakeContext.getNextHandshakeMessage();
+ * // (send responderMsg to initiator)
+ *
+ * // initiator:
+ * initiatorHandshakeContext.parseHandshakeMessage(responderMsg);
+ * initiatorMsg = initiatorHandshakeContext.getNextHandshakeMessage();
+ * // (send initiatorMsg to responder)
+ *
+ * // responder:
+ * responderHandshakeContext.parseHandshakeMessage(initiatorMsg);
+ * responderMsg = responderHandshakeContext.getNextHandshakeMessage();
+ * D2DConnectionContext responderCtx = responderHandshakeContext.toConnectionContext();
+ * // (send responderMsg to initiator)
+ *
+ * // initiator:
+ * initiatorHandshakeContext.parseHandshakeMessage(responderMsg);
+ * D2DConnectionContext initiatorCtx = initiatorHandshakeContext.toConnectionContext();
+ * }
+ *
+ * <p>The initial computation is:
+ * Initiator Responder
+ * has KM (pre-agreed point) has KM (pre-agreed point)
+ * has KN (pre-agreed point) has KN (pre-agreed point)
+ * has Password (pre-agreed) has Password (pre-agreed)
+ * picks random scalar Xi (private key) picks random scalar Xr (private key)
+ * computes the public key Pxi = G*Xi computes the public key Pxr = G*Xr
+ * computes commitment: computes commitment:
+ * Ci = KM * password + Pxi Cr = KN * password + Pxr
+ *
+ * <p>The flow is:
+ * Initiator Responder
+ * ----- Ci --------------------------------->
+ * <--------------------------------- Cr -----
+ * computes shared key K: computes shared key K:
+ * (Cr - KN*password) * Xi (Ci - KM*password) * Xr
+ * computes hash: computes hash:
+ * Hi = sha256(0|Cr|Ci|K) Hr = sha256(1|Ci|Cr|K)
+ * ----- Hi --------------------------------->
+ * Verify Hi
+ * <-------------- Hr (optional payload) -----
+ * Verify Hr
+ */
+public class D2DSpakeEd25519Handshake implements D2DHandshakeContext {
+ // Minimum length password that is acceptable for the handshake
+ public static final int MIN_PASSWORD_LENGTH = 4;
+ /**
+ * Creates a new SPAKE handshake object for the initiator.
+ *
+ * @param password the password that should be used in the handshake. Note that this should be
+ * at least {@value #MIN_PASSWORD_LENGTH} bytes long
+ */
+ public static D2DSpakeEd25519Handshake forInitiator(byte[] password) throws HandshakeException {
+ return new D2DSpakeEd25519Handshake(State.INITIATOR_START, password);
+ }
+
+ /**
+ * Creates a new SPAKE handshake object for the responder.
+ *
+ * @param password the password that should be used in the handshake. Note that this should be
+ * at least {@value #MIN_PASSWORD_LENGTH} bytes long
+ */
+ public static D2DSpakeEd25519Handshake forResponder(byte[] password) throws HandshakeException {
+ return new D2DSpakeEd25519Handshake(State.RESPONDER_START, password);
+ }
+
+ //
+ // The protocol requires two verifiable, randomly generated group point. They were generated
+ // using the python code below. The algorithm is to first pick a random y in the group and solve
+ // the elliptic curve equation for a value of x, if possible. We then use (x, y) as the random
+ // point.
+ // Source of ed25519 is here: http://ed25519.cr.yp.to/python/ed25519.py
+ // import ed25519
+ // import hashlib
+ //
+ // # Seeds
+ // seed1 = 'D2D Ed25519 point generation seed (M)'
+ // seed2 = 'D2D Ed25519 point generation seed (N)'
+ //
+ // def find_seed(seed):
+ // # generate a random scalar for the y coordinate
+ // y = hashlib.sha256(seed).hexdigest()
+ //
+ // P = ed25519.scalarmult(ed25519.B, int(y, 16) % ed25519.q)
+ // if (not ed25519.isoncurve(P)):
+ // print 'Wat? P should be on curve!'
+ //
+ // print ' x: ' + hex(P[0])
+ // print ' y: ' + hex(P[1])
+ // print
+ //
+ // find_seed(seed1)
+ // find_seed(seed2)
+ //
+ // Output is:
+ // x: 0x1981fb43f103290ecf9772022db8b19bfaf389057ed91e8486eb368763435925L
+ // y: 0xa714c34f3b588aac92fd2587884a20964fd351a1f147d5c4bbf5c2f37a77c36L
+ //
+ // x: 0x201a184f47d9a7973891d148e3d1c864d8084547131c2c1cefb7eebd26c63567L
+ // y: 0x6da2d3b18ec4f9aa3b08e39c997cd8bf6e9948ffd4feffecaf8dd0b3d648b7e8L
+ //
+ // To get extended representation X, Y, Z, T, do: Z = 1, T = X*Y mod P
+ @VisibleForTesting
+ static final BigInteger[] KM = new BigInteger[] {
+ new BigInteger(new byte[] {(byte) 0x19, (byte) 0x81, (byte) 0xFB, (byte) 0x43,
+ (byte) 0xF1, (byte) 0x03, (byte) 0x29, (byte) 0x0E, (byte) 0xCF, (byte) 0x97,
+ (byte) 0x72, (byte) 0x02, (byte) 0x2D, (byte) 0xB8, (byte) 0xB1, (byte) 0x9B,
+ (byte) 0xFA, (byte) 0xF3, (byte) 0x89, (byte) 0x05, (byte) 0x7E, (byte) 0xD9,
+ (byte) 0x1E, (byte) 0x84, (byte) 0x86, (byte) 0xEB, (byte) 0x36, (byte) 0x87,
+ (byte) 0x63, (byte) 0x43, (byte) 0x59, (byte) 0x25}),
+ new BigInteger(new byte[] {(byte) 0x0A, (byte) 0x71, (byte) 0x4C, (byte) 0x34,
+ (byte) 0xF3, (byte) 0xB5, (byte) 0x88, (byte) 0xAA, (byte) 0xC9, (byte) 0x2F,
+ (byte) 0xD2, (byte) 0x58, (byte) 0x78, (byte) 0x84, (byte) 0xA2, (byte) 0x09,
+ (byte) 0x64, (byte) 0xFD, (byte) 0x35, (byte) 0x1A, (byte) 0x1F, (byte) 0x14,
+ (byte) 0x7D, (byte) 0x5C, (byte) 0x4B, (byte) 0xBF, (byte) 0x5C, (byte) 0x2F,
+ (byte) 0x37, (byte) 0xA7, (byte) 0x7C, (byte) 0x36}),
+ BigInteger.ONE,
+ new BigInteger(new byte[] {(byte) 0x04, (byte) 0x8F, (byte) 0xC1, (byte) 0xCE,
+ (byte) 0xE5, (byte) 0x83, (byte) 0x99, (byte) 0x25, (byte) 0xE5, (byte) 0x9B,
+ (byte) 0x80, (byte) 0xEA, (byte) 0xAD, (byte) 0x82, (byte) 0xAC, (byte) 0x0A,
+ (byte) 0x3C, (byte) 0xFE, (byte) 0xC5, (byte) 0x60, (byte) 0x93, (byte) 0x59,
+ (byte) 0x8B, (byte) 0x48, (byte) 0x44, (byte) 0xDD, (byte) 0x2A, (byte) 0x3E,
+ (byte) 0x24, (byte) 0x5D, (byte) 0x88, (byte) 0x33})};
+
+ @VisibleForTesting
+ static final BigInteger[] KN = new BigInteger[] {
+ new BigInteger(new byte[] {(byte) 0x20, (byte) 0x1A, (byte) 0x18, (byte) 0x4F,
+ (byte) 0x47, (byte) 0xD9, (byte) 0xA7, (byte) 0x97, (byte) 0x38, (byte) 0x91,
+ (byte) 0xD1, (byte) 0x48, (byte) 0xE3, (byte) 0xD1, (byte) 0xC8, (byte) 0x64,
+ (byte) 0xD8, (byte) 0x08, (byte) 0x45, (byte) 0x47, (byte) 0x13, (byte) 0x1C,
+ (byte) 0x2C, (byte) 0x1C, (byte) 0xEF, (byte) 0xB7, (byte) 0xEE, (byte) 0xBD,
+ (byte) 0x26, (byte) 0xC6, (byte) 0x35, (byte) 0x67}),
+ new BigInteger(new byte[] {(byte) 0x6D, (byte) 0xA2, (byte) 0xD3, (byte) 0xB1,
+ (byte) 0x8E, (byte) 0xC4, (byte) 0xF9, (byte) 0xAA, (byte) 0x3B, (byte) 0x08,
+ (byte) 0xE3, (byte) 0x9C, (byte) 0x99, (byte) 0x7C, (byte) 0xD8, (byte) 0xBF,
+ (byte) 0x6E, (byte) 0x99, (byte) 0x48, (byte) 0xFF, (byte) 0xD4, (byte) 0xFE,
+ (byte) 0xFF, (byte) 0xEC, (byte) 0xAF, (byte) 0x8D, (byte) 0xD0, (byte) 0xB3,
+ (byte) 0xD6, (byte) 0x48, (byte) 0xB7, (byte) 0xE8}),
+ BigInteger.ONE,
+ new BigInteger(new byte[] {(byte) 0x16, (byte) 0x40, (byte) 0xED, (byte) 0x5A,
+ (byte) 0x54, (byte) 0xFA, (byte) 0x0B, (byte) 0x07, (byte) 0x22, (byte) 0x86,
+ (byte) 0xE9, (byte) 0xD2, (byte) 0x2F, (byte) 0x46, (byte) 0x47, (byte) 0x63,
+ (byte) 0xFB, (byte) 0xF6, (byte) 0x0D, (byte) 0x79, (byte) 0x1D, (byte) 0x37,
+ (byte) 0xB9, (byte) 0x09, (byte) 0x3B, (byte) 0x58, (byte) 0x4D, (byte) 0xF4,
+ (byte) 0xC9, (byte) 0x95, (byte) 0xF7, (byte) 0x81})};
+
+ // Base point B as per ed25519.cr.yp.to
+ @VisibleForTesting
+ /* package */ static final BigInteger[] B = new BigInteger[] {
+ new BigInteger(new byte[] {(byte) 0x21, (byte) 0x69, (byte) 0x36, (byte) 0xD3,
+ (byte) 0xCD, (byte) 0x6E, (byte) 0x53, (byte) 0xFE, (byte) 0xC0, (byte) 0xA4,
+ (byte) 0xE2, (byte) 0x31, (byte) 0xFD, (byte) 0xD6, (byte) 0xDC, (byte) 0x5C,
+ (byte) 0x69, (byte) 0x2C, (byte) 0xC7, (byte) 0x60, (byte) 0x95, (byte) 0x25,
+ (byte) 0xA7, (byte) 0xB2, (byte) 0xC9, (byte) 0x56, (byte) 0x2D, (byte) 0x60,
+ (byte) 0x8F, (byte) 0x25, (byte) 0xD5, (byte) 0x1A}),
+ new BigInteger(new byte[] {(byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66,
+ (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66,
+ (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66,
+ (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66,
+ (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66,
+ (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x58}),
+ BigInteger.ONE,
+ new BigInteger(new byte[] {(byte) 0x67, (byte) 0x87, (byte) 0x5F, (byte) 0x0F,
+ (byte) 0xD7, (byte) 0x8B, (byte) 0x76, (byte) 0x65, (byte) 0x66, (byte) 0xEA,
+ (byte) 0x4E, (byte) 0x8E, (byte) 0x64, (byte) 0xAB, (byte) 0xE3, (byte) 0x7D,
+ (byte) 0x20, (byte) 0xF0, (byte) 0x9F, (byte) 0x80, (byte) 0x77, (byte) 0x51,
+ (byte) 0x52, (byte) 0xF5, (byte) 0x6D, (byte) 0xDE, (byte) 0x8A, (byte) 0xB3,
+ (byte) 0xA5, (byte) 0xB7, (byte) 0xDD, (byte) 0xA3})};
+
+ // Number of bits needed to represent a point
+ private static final int POINT_SIZE_BITS = 256;
+
+ // Java Message Digest name for SHA 256
+ private static final String SHA256 = "SHA-256";
+
+ // Pre-shared password hash represented as an integer
+ private BigInteger passwordHash;
+
+ // Current state of the handshake
+ private State handshakeState;
+
+ // Derived shared key
+ private byte[] sharedKey;
+
+ // Private key (random scalar)
+ private BigInteger valueX;
+
+ // Public key (random point, in extended notation, based on valueX)
+ private BigInteger[] pointX;
+
+ // Commitment we've received from the other party (their password-authenticated public key)
+ private BigInteger[] theirCommitmentPointAffine;
+ private BigInteger[] theirCommitmentPointExtended;
+
+ // Commitment we've sent to the other party (our password-authenticated public key)
+ private BigInteger[] ourCommitmentPointAffine;
+ private BigInteger[] ourCommitmentPointExtended;
+
+ private enum State {
+ // Initiator state
+ INITIATOR_START,
+ INITIATOR_WAITING_FOR_RESPONDER_COMMITMENT,
+ INITIATOR_AFTER_RESPONDER_COMMITMENT,
+ INITIATOR_WAITING_FOR_RESPONDER_HASH,
+
+ // Responder state
+ RESPONDER_START,
+ RESPONDER_AFTER_INITIATOR_COMMITMENT,
+ RESPONDER_WAITING_FOR_INITIATOR_HASH,
+ RESPONDER_AFTER_INITIATOR_HASH,
+
+ // Common completion state
+ HANDSHAKE_FINISHED,
+ HANDSHAKE_ALREADY_USED
+ }
+
+ @VisibleForTesting
+ D2DSpakeEd25519Handshake(State state, byte[] password) throws HandshakeException {
+ if (password == null || password.length < MIN_PASSWORD_LENGTH) {
+ throw new HandshakeException("Passwords must be at least " + MIN_PASSWORD_LENGTH + " bytes");
+ }
+
+ handshakeState = state;
+ passwordHash = new BigInteger(1 /* positive */, hash(password));
+
+ do {
+ valueX = new BigInteger(POINT_SIZE_BITS, new SecureRandom());
+ } while (valueX.equals(BigInteger.ZERO));
+
+ try {
+ pointX = Ed25519.scalarMultiplyExtendedPoint(B, valueX);
+ } catch (Ed25519Exception e) {
+ throw new HandshakeException("Could not make public key point", e);
+ }
+ }
+
+ @Override
+ public boolean isHandshakeComplete() {
+ switch (handshakeState) {
+ case HANDSHAKE_FINISHED:
+ // fall-through intentional
+ case HANDSHAKE_ALREADY_USED:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public byte[] getNextHandshakeMessage() throws HandshakeException {
+ byte[] nextMessage;
+
+ switch(handshakeState) {
+ case INITIATOR_START:
+ nextMessage = makeCommitmentPointMessage(true /* is initiator */);
+ handshakeState = State.INITIATOR_WAITING_FOR_RESPONDER_COMMITMENT;
+ break;
+
+ case RESPONDER_AFTER_INITIATOR_COMMITMENT:
+ nextMessage = makeCommitmentPointMessage(false /* is initiator */);
+ handshakeState = State.RESPONDER_WAITING_FOR_INITIATOR_HASH;
+ break;
+
+ case INITIATOR_AFTER_RESPONDER_COMMITMENT:
+ nextMessage = makeSharedKeyHashMessage(true /* is initiator */, null /* no payload */);
+ handshakeState = State.INITIATOR_WAITING_FOR_RESPONDER_HASH;
+ break;
+
+ case RESPONDER_AFTER_INITIATOR_HASH:
+ nextMessage = makeSharedKeyHashMessage(false /* is initiator */, null /* no payload */);
+ handshakeState = State.HANDSHAKE_FINISHED;
+ break;
+
+ default:
+ throw new HandshakeException("Cannot get next message in state: " + handshakeState);
+ }
+
+ return nextMessage;
+ }
+
+ @Override
+ public byte[] getNextHandshakeMessage(byte[] payload) throws HandshakeException {
+ byte[] nextMessage;
+
+ switch (handshakeState) {
+ case RESPONDER_AFTER_INITIATOR_HASH:
+ nextMessage = makeSharedKeyHashMessage(false /* is initiator */, payload);
+ handshakeState = State.HANDSHAKE_FINISHED;
+ break;
+
+ default:
+ throw new HandshakeException(
+ "Cannot send handshake message with payload in state: " + handshakeState);
+ }
+
+ return nextMessage;
+ }
+
+ private byte[] makeCommitmentPointMessage(boolean isInitiator) throws HandshakeException {
+ try {
+ ourCommitmentPointExtended =
+ Ed25519.scalarMultiplyExtendedPoint(isInitiator ? KM : KN, passwordHash);
+ ourCommitmentPointExtended = Ed25519.addExtendedPoints(ourCommitmentPointExtended, pointX);
+ ourCommitmentPointAffine = Ed25519.toAffine(ourCommitmentPointExtended);
+
+ return SpakeHandshakeMessage.newBuilder()
+ .setEcPoint(
+ EcPoint.newBuilder()
+ .setCurve(DeviceToDeviceMessagesProto.Curve.ED_25519)
+ .setX(ByteString.copyFrom(ourCommitmentPointAffine[0].toByteArray()))
+ .setY(ByteString.copyFrom(ourCommitmentPointAffine[1].toByteArray()))
+ .build())
+ .setFlowNumber(isInitiator ? 1 : 2 /* first or second message */)
+ .build()
+ .toByteArray();
+ } catch (Ed25519Exception e) {
+ throw new HandshakeException("Could not make commitment point message", e);
+ }
+ }
+
+ private void makeSharedKey(boolean isInitiator) throws HandshakeException {
+
+ if (handshakeState != State.RESPONDER_START
+ && handshakeState != State.INITIATOR_WAITING_FOR_RESPONDER_COMMITMENT) {
+ throw new HandshakeException("Cannot make shared key in state: " + handshakeState);
+ }
+
+ try {
+ BigInteger[] kNMP = Ed25519.scalarMultiplyExtendedPoint(isInitiator ? KN : KM, passwordHash);
+
+ // TheirPublicKey = TheirCommitment - kNMP = (TheirPublicKey + kNMP) - kNMP
+ BigInteger[] theirPublicKey =
+ Ed25519.subtractExtendedPoints(theirCommitmentPointExtended, kNMP);
+
+ BigInteger[] sharedKeyPoint = Ed25519.scalarMultiplyExtendedPoint(theirPublicKey, valueX);
+ sharedKey = hash(pointToByteArray(Ed25519.toAffine(sharedKeyPoint)));
+ } catch (Ed25519Exception e) {
+ throw new HandshakeException("Error computing shared key", e);
+ }
+ }
+
+ private byte[] makeSharedKeyHashMessage(boolean isInitiator, byte[] payload)
+ throws HandshakeException {
+ SpakeHandshakeMessage.Builder handshakeMessage = SpakeHandshakeMessage.newBuilder()
+ .setHashValue(ByteString.copyFrom(computeOurKeyHash(isInitiator)))
+ .setFlowNumber(isInitiator ? 3 : 4 /* third or fourth message */);
+
+ if (canSendPayloadInHandshakeMessage() && payload != null) {
+ DeviceToDeviceMessage deviceToDeviceMessage =
+ D2DConnectionContext.createDeviceToDeviceMessage(payload, 1 /* sequence number */);
+ try {
+ handshakeMessage.setPayload(ByteString.copyFrom(
+ D2DCryptoOps.signcryptPayload(
+ new Payload(PayloadType.DEVICE_TO_DEVICE_RESPONDER_HELLO_PAYLOAD,
+ deviceToDeviceMessage.toByteArray()),
+ new SecretKeySpec(sharedKey, "AES"))));
+ } catch (InvalidKeyException | NoSuchAlgorithmException e) {
+ throw new HandshakeException("Cannot set payload", e);
+ }
+ }
+
+ return handshakeMessage.build().toByteArray();
+ }
+
+ private byte[] computeOurKeyHash(boolean isInitiator) throws HandshakeException {
+ return hash(concat(
+ new byte[] { (byte) (isInitiator ? 0 : 1) },
+ pointToByteArray(theirCommitmentPointAffine),
+ pointToByteArray(ourCommitmentPointAffine),
+ sharedKey));
+ }
+
+ private byte[] computeTheirKeyHash(boolean isInitiator) throws HandshakeException {
+ return hash(concat(
+ new byte[] { (byte) (isInitiator ? 1 : 0) },
+ pointToByteArray(ourCommitmentPointAffine),
+ pointToByteArray(theirCommitmentPointAffine),
+ sharedKey));
+ }
+
+ private byte[] pointToByteArray(BigInteger[] p) {
+ return concat(p[0].toByteArray(), p[1].toByteArray());
+ }
+
+ @Override
+ public boolean canSendPayloadInHandshakeMessage() {
+ return handshakeState == State.RESPONDER_AFTER_INITIATOR_HASH;
+ }
+
+ @Override
+ public byte[] parseHandshakeMessage(byte[] handshakeMessage) throws HandshakeException {
+ if (handshakeMessage == null || handshakeMessage.length == 0) {
+ throw new HandshakeException("Handshake message too short");
+ }
+
+ byte[] payload = new byte[0];
+
+ switch(handshakeState) {
+ case RESPONDER_START:
+ // no payload can be sent in this message
+ parseCommitmentMessage(handshakeMessage, false /* is initiator */);
+ makeSharedKey(false /* is initiator */);
+ handshakeState = State.RESPONDER_AFTER_INITIATOR_COMMITMENT;
+ break;
+
+ case INITIATOR_WAITING_FOR_RESPONDER_COMMITMENT:
+ // no payload can be sent in this message
+ parseCommitmentMessage(handshakeMessage, true /* is initiator */);
+ makeSharedKey(true /* is initiator */);
+ handshakeState = State.INITIATOR_AFTER_RESPONDER_COMMITMENT;
+ break;
+
+ case RESPONDER_WAITING_FOR_INITIATOR_HASH:
+ // no payload can be sent in this message
+ parseHashMessage(handshakeMessage, false /* is initiator */);
+ handshakeState = State.RESPONDER_AFTER_INITIATOR_HASH;
+ break;
+
+ case INITIATOR_WAITING_FOR_RESPONDER_HASH:
+ payload = parseHashMessage(handshakeMessage, true /* is initiator */);
+ handshakeState = State.HANDSHAKE_FINISHED;
+ break;
+
+ default:
+ throw new HandshakeException("Cannot parse message in state: " + handshakeState);
+ }
+
+ return payload;
+ }
+
+ private byte[] parseHashMessage(byte[] handshakeMessage, boolean isInitiator)
+ throws HandshakeException {
+ SpakeHandshakeMessage hashMessage;
+
+ // Parse the message
+ try {
+ hashMessage = SpakeHandshakeMessage.parseFrom(handshakeMessage);
+ } catch (InvalidProtocolBufferException e) {
+ throw new HandshakeException("Could not parse hash message", e);
+ }
+
+ // Check flow number
+ if (!hashMessage.hasFlowNumber()) {
+ throw new HandshakeException("Hash message missing flow number");
+ }
+ int expectedFlowNumber = isInitiator ? 4 : 3;
+ int actualFlowNumber = hashMessage.getFlowNumber();
+ if (actualFlowNumber != expectedFlowNumber) {
+ throw new HandshakeException("Hash message has flow number " + actualFlowNumber
+ + ", but expected flow number " + expectedFlowNumber);
+ }
+
+ // Check and extract hash
+ if (!hashMessage.hasHashValue()) {
+ throw new HandshakeException("Hash message missing hash value");
+ }
+
+ byte[] theirHash = hashMessage.getHashValue().toByteArray();
+ byte[] theirCorrectHash = computeTheirKeyHash(isInitiator);
+
+ if (!constantTimeArrayEquals(theirCorrectHash, theirHash)) {
+ throw new HandshakeException("Hash message had incorrect hash value");
+ }
+
+ if (isInitiator && hashMessage.hasPayload()) {
+ try {
+ DeviceToDeviceMessage message = D2DCryptoOps.decryptResponderHelloMessage(
+ new SecretKeySpec(sharedKey, "AES"),
+ hashMessage.getPayload().toByteArray());
+
+ if (message.getSequenceNumber() != 1) {
+ throw new HandshakeException("Incorrect sequence number in responder hello");
+ }
+
+ return message.getMessage().toByteArray();
+
+ } catch (SignatureException e) {
+ throw new HandshakeException("Error recovering payload from hash message", e);
+ }
+ }
+
+ // empty/no payload
+ return new byte[0];
+ }
+
+ private void parseCommitmentMessage(byte[] handshakeMessage, boolean isInitiator)
+ throws HandshakeException {
+ SpakeHandshakeMessage commitmentMessage;
+
+ // Parse the message
+ try {
+ commitmentMessage = SpakeHandshakeMessage.parseFrom(handshakeMessage);
+ } catch (InvalidProtocolBufferException e) {
+ throw new HandshakeException("Could not parse commitment message", e);
+ }
+
+ // Check flow number
+ if (!commitmentMessage.hasFlowNumber()) {
+ throw new HandshakeException("Commitment message missing flow number");
+ }
+ if (commitmentMessage.getFlowNumber() != (isInitiator ? 2 : 1)) {
+ throw new HandshakeException("Commitment message has wrong flow number");
+ }
+
+ // Check point and curve; and extract point
+ if (!commitmentMessage.hasEcPoint()) {
+ throw new HandshakeException("Commitment message missing point");
+ }
+ EcPoint commitmentPoint = commitmentMessage.getEcPoint();
+ if (!commitmentPoint.hasCurve()
+ || commitmentPoint.getCurve() != DeviceToDeviceMessagesProto.Curve.ED_25519) {
+ throw new HandshakeException("Commitment message has wrong curve");
+ }
+
+ if (!commitmentPoint.hasX()) {
+ throw new HandshakeException("Commitment point missing x coordinate");
+ }
+
+ if (!commitmentPoint.hasY()) {
+ throw new HandshakeException("Commitment point missing y coordinate");
+ }
+
+ // Build the point
+ theirCommitmentPointAffine = new BigInteger[] {
+ new BigInteger(commitmentPoint.getX().toByteArray()),
+ new BigInteger(commitmentPoint.getY().toByteArray())
+ };
+
+ // Validate the point to be sure
+ try {
+ Ed25519.validateAffinePoint(theirCommitmentPointAffine);
+ theirCommitmentPointExtended = Ed25519.toExtended(theirCommitmentPointAffine);
+ } catch (Ed25519Exception e) {
+ throw new HandshakeException("Error validating their commitment point", e);
+ }
+ }
+
+ @Override
+ public D2DConnectionContext toConnectionContext() throws HandshakeException {
+ if (handshakeState == State.HANDSHAKE_ALREADY_USED) {
+ throw new HandshakeException("Cannot reuse handshake context; is has already been used");
+ }
+
+ if (!isHandshakeComplete()) {
+ throw new HandshakeException("Handshake is not complete; cannot create connection context");
+ }
+
+ handshakeState = State.HANDSHAKE_ALREADY_USED;
+
+ // Both sides start with an initial sequence number of 1 because the last message of the
+ // handshake had an optional payload with sequence number 1. D2DConnectionContext remembers
+ // the last sequence number used.
+ return new D2DConnectionContextV0(
+ new SecretKeySpec(sharedKey, "AES"), 1 /* initialSequenceNumber */);
+ }
+
+ /**
+ * Implementation of byte array concatenation copied from Guava.
+ */
+ private static byte[] concat(byte[]... arrays) {
+ int length = 0;
+ for (byte[] array : arrays) {
+ length += array.length;
+ }
+
+ byte[] result = new byte[length];
+ int pos = 0;
+ for (byte[] array : arrays) {
+ System.arraycopy(array, 0, result, pos, array.length);
+ pos += array.length;
+ }
+
+ return result;
+ }
+
+ private static byte[] hash(byte[] message) throws HandshakeException {
+ try {
+ return MessageDigest.getInstance(SHA256).digest(message);
+ } catch (NoSuchAlgorithmException e) {
+ throw new HandshakeException("Error performing hash", e);
+ }
+ }
+
+ private static boolean constantTimeArrayEquals(byte[] a, byte[] b) {
+ if (a == null || b == null) {
+ return (a == b);
+ }
+ if (a.length != b.length) {
+ return false;
+ }
+ byte result = 0;
+ for (int i = 0; i < b.length; i++) {
+ result = (byte) (result | (a[i] ^ b[i]));
+ }
+ return (result == 0);
+ }
+}
diff --git a/src/main/java/com/google/security/cryptauth/lib/securegcm/Ed25519.java b/src/main/java/com/google/security/cryptauth/lib/securegcm/Ed25519.java
index 454b942..2ea2563 100644
--- a/src/main/java/com/google/security/cryptauth/lib/securegcm/Ed25519.java
+++ b/src/main/java/com/google/security/cryptauth/lib/securegcm/Ed25519.java
@@ -1,23 +1,24 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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 static java.math.BigInteger.ONE;
import static java.math.BigInteger.ZERO;
import com.google.common.annotations.VisibleForTesting;
+
import java.math.BigInteger;
/**
@@ -207,20 +208,20 @@ public class Ed25519 {
};
}
- /** Converts a point in affine representation to extended representation */
- // TODO(b/120887495): This @VisibleForTesting annotation was being ignored by prod code.
- // Please check that removing it is correct, and remove this comment along with it.
- // @VisibleForTesting
+ /**
+ * Converts a point in affine representation to extended representation
+ */
+ @VisibleForTesting
static BigInteger[] toExtended(BigInteger[] p) throws Ed25519Exception {
checkPointIsInAffineRepresentation(p);
return new BigInteger[] {p[X], p[Y], ONE, p[X].multiply(p[Y]).mod(Ed25519_P)}; // x, y, 1, x*y
}
- /** Converts a point in extended representation to affine representation */
- // TODO(b/120887495): This @VisibleForTesting annotation was being ignored by prod code.
- // Please check that removing it is correct, and remove this comment along with it.
- // @VisibleForTesting
+ /**
+ * Converts a point in extended representation to affine representation
+ */
+ @VisibleForTesting
static BigInteger[] toAffine(BigInteger[] p) throws Ed25519Exception {
checkPointIsInExtendedRepresentation(p);
diff --git a/src/main/java/com/google/security/cryptauth/lib/securegcm/EnrollmentCryptoOps.java b/src/main/java/com/google/security/cryptauth/lib/securegcm/EnrollmentCryptoOps.java
index 450c806..328cb53 100644
--- a/src/main/java/com/google/security/cryptauth/lib/securegcm/EnrollmentCryptoOps.java
+++ b/src/main/java/com/google/security/cryptauth/lib/securegcm/EnrollmentCryptoOps.java
@@ -1,17 +1,17 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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 com.google.protobuf.InvalidProtocolBufferException;
diff --git a/src/main/java/com/google/security/cryptauth/lib/securegcm/HandshakeException.java b/src/main/java/com/google/security/cryptauth/lib/securegcm/HandshakeException.java
index b717eb6..bb5fffc 100644
--- a/src/main/java/com/google/security/cryptauth/lib/securegcm/HandshakeException.java
+++ b/src/main/java/com/google/security/cryptauth/lib/securegcm/HandshakeException.java
@@ -1,17 +1,17 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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;
/**
diff --git a/src/main/java/com/google/security/cryptauth/lib/securegcm/KeyEncoding.java b/src/main/java/com/google/security/cryptauth/lib/securegcm/KeyEncoding.java
index 67e4ace..9690a89 100644
--- a/src/main/java/com/google/security/cryptauth/lib/securegcm/KeyEncoding.java
+++ b/src/main/java/com/google/security/cryptauth/lib/securegcm/KeyEncoding.java
@@ -1,17 +1,17 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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 com.google.protobuf.InvalidProtocolBufferException;
@@ -58,10 +58,6 @@ public class KeyEncoding {
return sk.getEncoded();
}
- public static byte[] encodeDeviceSyncGroupPublicKey(PublicKey pk) {
- return PublicKeyProtoUtil.encodePaddedEcPublicKey(pk).toByteArray();
- }
-
public static PrivateKey parseUserPrivateKey(byte[] encodedPrivateKey, boolean isLegacy)
throws InvalidKeySpecException {
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
@@ -75,11 +71,6 @@ public class KeyEncoding {
return parsePublicKey(keyBytes);
}
- public static PublicKey parseDeviceSyncGroupPublicKey(byte[] keyBytes)
- throws InvalidKeySpecException {
- return parsePublicKey(keyBytes);
- }
-
public static byte[] encodeKeyAgreementPublicKey(PublicKey pk) {
return encodePublicKey(pk);
}
diff --git a/src/main/java/com/google/security/cryptauth/lib/securegcm/SecureGcmConstants.java b/src/main/java/com/google/security/cryptauth/lib/securegcm/SecureGcmConstants.java
index a69431f..8482628 100644
--- a/src/main/java/com/google/security/cryptauth/lib/securegcm/SecureGcmConstants.java
+++ b/src/main/java/com/google/security/cryptauth/lib/securegcm/SecureGcmConstants.java
@@ -1,17 +1,17 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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;
/**
diff --git a/src/main/java/com/google/security/cryptauth/lib/securegcm/TransportCryptoOps.java b/src/main/java/com/google/security/cryptauth/lib/securegcm/TransportCryptoOps.java
index b053bf4..a22edc4 100644
--- a/src/main/java/com/google/security/cryptauth/lib/securegcm/TransportCryptoOps.java
+++ b/src/main/java/com/google/security/cryptauth/lib/securegcm/TransportCryptoOps.java
@@ -1,17 +1,17 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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 com.google.protobuf.InvalidProtocolBufferException;
diff --git a/src/main/java/com/google/security/cryptauth/lib/securegcm/Ukey2Handshake.java b/src/main/java/com/google/security/cryptauth/lib/securegcm/Ukey2Handshake.java
index 8e00ea9..0d01c9a 100644
--- a/src/main/java/com/google/security/cryptauth/lib/securegcm/Ukey2Handshake.java
+++ b/src/main/java/com/google/security/cryptauth/lib/securegcm/Ukey2Handshake.java
@@ -1,17 +1,17 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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 com.google.protobuf.ByteString;
diff --git a/src/main/java/com/google/security/cryptauth/lib/securemessage/CryptoOps.java b/src/main/java/com/google/security/cryptauth/lib/securemessage/CryptoOps.java
index 876bd93..1e3b196 100644
--- a/src/main/java/com/google/security/cryptauth/lib/securemessage/CryptoOps.java
+++ b/src/main/java/com/google/security/cryptauth/lib/securemessage/CryptoOps.java
@@ -1,17 +1,17 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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.securemessage;
import com.google.security.annotations.SuppressInsecureCipherModeCheckerReviewed;
@@ -132,6 +132,7 @@ public class CryptoOps {
* A salt value specific to this library, generated as SHA-256("SecureMessage")
*/
private static final byte[] SALT = sha256("SecureMessage");
+ private static final byte[] CONSTANT_01 = { 0x01 }; // For convenience
/**
* Signs {@code data} using the algorithm specified by {@code sigType} with {@code signingKey}.
@@ -411,31 +412,10 @@ public class CryptoOps {
*/
public static byte[] hkdf(SecretKey inputKeyMaterial, byte[] salt, byte[] info)
throws NoSuchAlgorithmException, InvalidKeyException {
- return hkdf(inputKeyMaterial, salt, info, /* length= */ 32);
- }
-
- /**
- * Implements HKDF (RFC 5869) with the SHA-256 hash.
- *
- * <p>Please make sure to select a salt that is fixed and unique for your codebase, and use the
- * {@code info} parameter to specify any additional bits that should influence the derived key.
- *
- * @param inputKeyMaterial master key from which to derive sub-keys
- * @param salt a (public) randomly generated 256-bit input that can be re-used
- * @param info arbitrary information that is bound to the derived key (i.e., used in its creation)
- * @param length length of returned key material
- * @return raw derived key bytes = HKDF-SHA256(inputKeyMaterial, salt, info)
- * @throws InvalidKeyException if the encoded form of {@code inputKeyMaterial} cannot be accessed
- */
- public static byte[] hkdf(SecretKey inputKeyMaterial, byte[] salt, byte[] info, int length)
- throws NoSuchAlgorithmException, InvalidKeyException {
if ((inputKeyMaterial == null) || (salt == null) || (info == null)) {
throw new NullPointerException();
}
- if (length < 0) {
- throw new IllegalArgumentException("Length must be positive");
- }
- return hkdfSha256Expand(hkdfSha256Extract(inputKeyMaterial, salt), info, length);
+ return hkdfSha256Expand(hkdfSha256Extract(inputKeyMaterial, salt), info);
}
/**
@@ -484,12 +464,12 @@ public class CryptoOps {
}
/**
- * The HKDF (RFC 5869) extraction function, using the SHA-256 hash function. This function is used
- * to pre-process the inputKeyMaterial and mix it with the salt, producing output suitable for use
- * with HKDF expansion function (which produces the actual derived key).
+ * The HKDF (RFC 5869) extraction function, using the SHA-256 hash function. This function is
+ * used to pre-process the inputKeyMaterial and mix it with the salt, producing output suitable
+ * for use with HKDF expansion function (which produces the actual derived key).
*
- * @see #hkdfSha256Expand(byte[], byte[], int)
- * @return HMAC-SHA256(salt, inputKeyMaterial) (salt is the "key" for the HMAC)
+ * @see #hkdfSha256Expand(byte[], byte[])
+ * @return HMAC-SHA256(salt, inputKeyMaterial) (salt is the "key" for the HMAC)
* @throws InvalidKeyException if the encoded form of {@code inputKeyMaterial} cannot be accessed
* @throws NoSuchAlgorithmException if the HmacSHA256 or AES algorithms are unavailable
*/
@@ -511,15 +491,15 @@ public class CryptoOps {
}
/**
- * HKDF (RFC 5869) expansion function, using the SHA-256 hash function.
+ * Special case of HKDF (RFC 5869) expansion function, using the SHA-256 hash function and
+ * allowing for a maximum output length of 256 bits.
*
- * @param pseudoRandomKey should be generated by {@link #hkdfSha256Extract(SecretKey, byte[])}
+ * @param pseudoRandomKey should be generated by {@link #hkdfSha256Expand(byte[], byte[])}
* @param info arbitrary information the derived key should be bound to
- * @param length length of the output key material in bytes
* @return raw derived key bytes = HMAC-SHA256(pseudoRandomKey, info | 0x01)
* @throws NoSuchAlgorithmException if the HmacSHA256 or AES algorithms are unavailable
*/
- private static byte[] hkdfSha256Expand(byte[] pseudoRandomKey, byte[] info, int length)
+ private static byte[] hkdfSha256Expand(byte[] pseudoRandomKey, byte[] info)
throws NoSuchAlgorithmException {
Mac macScheme = Mac.getInstance("HmacSHA256");
try {
@@ -527,38 +507,11 @@ public class CryptoOps {
} catch (InvalidKeyException e) {
throw new AssertionError(e); // This should never happen
}
-
- // Number of blocks N = ceil(hash length / output length).
- int blocks = length / 32;
- if (length % 32 > 0) {
- blocks += 1;
- }
-
- // The counter used to generate the blocks according to the RFC is only one byte long,
- // which puts a limit on the number of blocks possible.
- if (blocks > 0xFF) {
- throw new IllegalArgumentException("Maximum HKDF output length exceeded.");
- }
-
- byte[] outputBlock = new byte[32];
- byte[] counter = new byte[1];
- byte[] output = new byte[32 * blocks];
- for (int i = 0; i < blocks; ++i) {
- macScheme.reset();
- if (i > 0) {
- // Previous block
- macScheme.update(outputBlock);
- }
- // Arbitrary info
- macScheme.update(info);
- // Counter
- counter[0] = (byte) (i + 1);
- outputBlock = macScheme.doFinal(counter);
-
- System.arraycopy(outputBlock, 0, output, 32 * i, 32);
- }
-
- return subarray(output, 0, length);
+ // Arbitrary "info" to be included in the MAC.
+ macScheme.update(info);
+ // Note that RFC 5869 computes number of blocks N = ceil(hash length / output length), but
+ // here we only deal with a 256 bit hash up to a 256 bit output, yielding N=1.
+ return macScheme.doFinal(CONSTANT_01);
}
}
diff --git a/src/main/java/com/google/security/cryptauth/lib/securemessage/PublicKeyProtoUtil.java b/src/main/java/com/google/security/cryptauth/lib/securemessage/PublicKeyProtoUtil.java
index 0c593fe..ab97cca 100644
--- a/src/main/java/com/google/security/cryptauth/lib/securemessage/PublicKeyProtoUtil.java
+++ b/src/main/java/com/google/security/cryptauth/lib/securemessage/PublicKeyProtoUtil.java
@@ -1,20 +1,19 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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.securemessage;
-import com.google.common.collect.Lists;
import com.google.protobuf.ByteString;
import com.google.security.annotations.SuppressInsecureCipherModeCheckerPendingReview;
import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.DhPublicKey;
@@ -195,37 +194,6 @@ public class PublicKeyProtoUtil {
}
/**
- * Encodes an {@link ECPublicKey} to an {@link GenericPublicKey} proto message. The returned key
- * has a null-byte padded to the front in order to match the C++ implementation.
- */
- public static GenericPublicKey encodePaddedEcPublicKey(PublicKey pk) {
- if (pk == null) {
- throw new NullPointerException();
- }
- if (!(pk instanceof ECPublicKey)) {
- throw new IllegalArgumentException("Expected ECPublicKey PublicKey type");
- }
-
- ECPublicKey epk = pkToECPublicKey(pk);
- ByteString nullByteString = ByteString.copyFrom(new byte[] {0});
- ByteString xByteString = extractX(epk);
- if (xByteString.size() < MAX_P256_ENCODING_BYTES) {
- xByteString = ByteString.copyFrom(Lists.newArrayList(nullByteString, xByteString));
- }
- ByteString yByteString = extractY(epk);
- if (yByteString.size() < MAX_P256_ENCODING_BYTES) {
- yByteString = ByteString.copyFrom(Lists.newArrayList(nullByteString, yByteString));
- }
- EcP256PublicKey newKey =
- EcP256PublicKey.newBuilder().setX(xByteString).setY(yByteString).build();
-
- return GenericPublicKey.newBuilder()
- .setType(SecureMessageProto.PublicKeyType.EC_P256)
- .setEcP256PublicKey(newKey)
- .build();
- }
-
- /**
* Encodes an {@link ECPublicKey} to an {@link EcP256PublicKey} proto message.
*/
public static EcP256PublicKey encodeEcPublicKey(PublicKey pk) {
diff --git a/src/main/java/com/google/security/cryptauth/lib/securemessage/SecureMessageBuilder.java b/src/main/java/com/google/security/cryptauth/lib/securemessage/SecureMessageBuilder.java
index f1a9464..f59ce4e 100644
--- a/src/main/java/com/google/security/cryptauth/lib/securemessage/SecureMessageBuilder.java
+++ b/src/main/java/com/google/security/cryptauth/lib/securemessage/SecureMessageBuilder.java
@@ -1,17 +1,17 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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.securemessage;
import com.google.protobuf.ByteString;
diff --git a/src/main/java/com/google/security/cryptauth/lib/securemessage/SecureMessageParser.java b/src/main/java/com/google/security/cryptauth/lib/securemessage/SecureMessageParser.java
index d634d40..e1c60e3 100644
--- a/src/main/java/com/google/security/cryptauth/lib/securemessage/SecureMessageParser.java
+++ b/src/main/java/com/google/security/cryptauth/lib/securemessage/SecureMessageParser.java
@@ -1,17 +1,17 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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.securemessage;
import com.google.protobuf.ByteString;
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextTest.java
deleted file mode 100644
index e671e8c..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextTest.java
+++ /dev/null
@@ -1,568 +0,0 @@
-// Copyright 2020 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
-//
-// https://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 static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.security.SignatureException;
-import java.util.Arrays;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/**
- * Base class for Android compatible tests for {@link D2DConnectionContext} subclasses.
- * Note: We would use a Parameterized test runner to test different versions, but this
- * functionality is not supported by Android tests.
- */
-@RunWith(JUnit4.class)
-public class D2DConnectionContextTest {
- private static final String PING = "ping";
- private static final String PONG = "pong";
-
- // Key is: "initiator_encode_key_for_aes_256"
- private static final SecretKey INITIATOR_ENCODE_KEY = new SecretKeySpec(
- new byte[] {
- (byte) 0x69, (byte) 0x6e, (byte) 0x69, (byte) 0x74, (byte) 0x69, (byte) 0x61, (byte) 0x74,
- (byte) 0x6f, (byte) 0x72, (byte) 0x5f, (byte) 0x65, (byte) 0x6e, (byte) 0x63, (byte) 0x6f,
- (byte) 0x64, (byte) 0x65, (byte) 0x5f, (byte) 0x6b, (byte) 0x65, (byte) 0x79, (byte) 0x5f,
- (byte) 0x66, (byte) 0x6f, (byte) 0x72, (byte) 0x5f, (byte) 0x61, (byte) 0x65, (byte) 0x73,
- (byte) 0x5f, (byte) 0x32, (byte) 0x35, (byte) 0x36
- },
- "AES");
-
- // Key is: "initiator_decode_key_for_aes_256"
- private static final SecretKey INITIATOR_DECODE_KEY = new SecretKeySpec(
- new byte[] {
- (byte) 0x69, (byte) 0x6e, (byte) 0x69, (byte) 0x74, (byte) 0x69, (byte) 0x61, (byte) 0x74,
- (byte) 0x6f, (byte) 0x72, (byte) 0x5f, (byte) 0x64, (byte) 0x65, (byte) 0x63, (byte) 0x6f,
- (byte) 0x64, (byte) 0x65, (byte) 0x5f, (byte) 0x6b, (byte) 0x65, (byte) 0x79, (byte) 0x5f,
- (byte) 0x66, (byte) 0x6f, (byte) 0x72, (byte) 0x5f, (byte) 0x61, (byte) 0x65, (byte) 0x73,
- (byte) 0x5f, (byte) 0x32, (byte) 0x35, (byte) 0x36
- },
- "AES");
-
- private D2DConnectionContext initiatorCtx;
- private D2DConnectionContext responderCtx;
-
- @Before
- public void setUp() throws Exception {
- KeyEncodingTest.installSunEcSecurityProviderIfNecessary();
- }
-
- protected void testPeerToPeerProtocol(int protocolVersion) throws Exception {
-
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- initiatorCtx = createConnectionContext(protocolVersion, true /** isInitiator */);
- responderCtx = createConnectionContext(protocolVersion, false /** isInitiator */);
-
- byte[] pingMessage = initiatorCtx.encodeMessageToPeer(PING);
- // (send message to responder)
-
- // responder
- String messageStr = responderCtx.decodeMessageFromPeerAsString(pingMessage);
- assertEquals(PING, messageStr);
-
- byte[] pongMessage = responderCtx.encodeMessageToPeer(PONG);
- // (send message to initiator)
-
- // initiator
- messageStr = initiatorCtx.decodeMessageFromPeerAsString(pongMessage);
- assertEquals(PONG, messageStr);
-
- // let's make sure there is actually some crypto involved.
- pingMessage = initiatorCtx.encodeMessageToPeer("can you see this?");
- pingMessage[2] = (byte) (pingMessage[2] + 1); // twiddle with the message
- try {
- responderCtx.decodeMessageFromPeerAsString(pingMessage);
- fail("expected exception, but didn't get it");
- } catch (SignatureException expected) {
- assertTrue(expected.getMessage().contains("failed verification"));
- }
-
- // Try and replay the previous encoded message to the initiator (replays should not work).
- try {
- initiatorCtx.decodeMessageFromPeerAsString(pongMessage);
- fail("expected exception, but didn't get it");
- } catch (SignatureException expected) {
- assertTrue(expected.getMessage().contains("sequence"));
- }
-
- assertEquals(protocolVersion, initiatorCtx.getProtocolVersion());
- assertEquals(protocolVersion, responderCtx.getProtocolVersion());
- }
-
- @Test
- public void testPeerToPeerProtocol_V0() throws Exception {
- testPeerToPeerProtocol(D2DConnectionContextV0.PROTOCOL_VERSION);
- }
-
- @Test
- public void testPeerToPeerProtocol_V1() throws Exception {
- testPeerToPeerProtocol(D2DConnectionContextV1.PROTOCOL_VERSION);
- }
-
- protected void testResponderSendsFirst(int protocolVersion) throws Exception {
-
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- initiatorCtx = createConnectionContext(protocolVersion, true /** isInitiator */);
- responderCtx = createConnectionContext(protocolVersion, false /** isInitiator */);
-
- byte[] pongMessage = responderCtx.encodeMessageToPeer(PONG);
- assertEquals(PONG, initiatorCtx.decodeMessageFromPeerAsString(pongMessage));
-
- pongMessage = responderCtx.encodeMessageToPeer(PONG);
- assertEquals(PONG, initiatorCtx.decodeMessageFromPeerAsString(pongMessage));
-
- // for good measure, if the initiator now responds, it should also work:
- byte[] pingMessage = initiatorCtx.encodeMessageToPeer(PING);
- assertEquals(PING, responderCtx.decodeMessageFromPeerAsString(pingMessage));
-
- pingMessage = initiatorCtx.encodeMessageToPeer(PING);
- assertEquals(PING, responderCtx.decodeMessageFromPeerAsString(pingMessage));
-
- pingMessage = initiatorCtx.encodeMessageToPeer(PING);
- assertEquals(PING, responderCtx.decodeMessageFromPeerAsString(pingMessage));
- }
-
- @Test
- public void testResponderSendsFirst_V0() throws Exception {
- testResponderSendsFirst(D2DConnectionContextV0.PROTOCOL_VERSION);
- }
-
- @Test
- public void testResponderSendsFirst_V1() throws Exception {
- testResponderSendsFirst(D2DConnectionContextV1.PROTOCOL_VERSION);
- }
-
- protected void testAssymmetricFlows(int protocolVersion) throws Exception {
-
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- initiatorCtx = createConnectionContext(protocolVersion, true /** isInitiator */);
- responderCtx = createConnectionContext(protocolVersion, false /** isInitiator */);
-
- // Let's test that this still works if one side sends a few messages in a row.
- byte[] pingMessage = initiatorCtx.encodeMessageToPeer(PING);
- assertEquals(PING, responderCtx.decodeMessageFromPeerAsString(pingMessage));
-
- pingMessage = initiatorCtx.encodeMessageToPeer(PING);
- assertEquals(PING, responderCtx.decodeMessageFromPeerAsString(pingMessage));
-
- pingMessage = initiatorCtx.encodeMessageToPeer(PING);
- assertEquals(PING, responderCtx.decodeMessageFromPeerAsString(pingMessage));
-
-
- byte[] pongMessage = responderCtx.encodeMessageToPeer(PONG);
- assertEquals(PONG, initiatorCtx.decodeMessageFromPeerAsString(pongMessage));
-
- pongMessage = responderCtx.encodeMessageToPeer(PONG);
- assertEquals(PONG, initiatorCtx.decodeMessageFromPeerAsString(pongMessage));
- }
-
- @Test
- public void testAssymmetricFlows_V0() throws Exception {
- testAssymmetricFlows(D2DConnectionContextV0.PROTOCOL_VERSION);
- }
-
- @Test
- public void testAssymmetricFlows_V1() throws Exception {
- testAssymmetricFlows(D2DConnectionContextV1.PROTOCOL_VERSION);
- }
-
- public void testErrorWhenResponderResendsMessage(int protocolVersion) throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- initiatorCtx = createConnectionContext(protocolVersion, true /** isInitiator */);
- responderCtx = createConnectionContext(protocolVersion, false /** isInitiator */);
-
- byte[] pongMessage = responderCtx.encodeMessageToPeer(PONG);
- assertEquals(PONG, initiatorCtx.decodeMessageFromPeerAsString(pongMessage));
-
- try {
- // send pongMessage again to the initiator
- initiatorCtx.decodeMessageFromPeerAsString(pongMessage);
- fail("expected exception, but didn't get it");
- } catch (SignatureException expected) {
- assertTrue(expected.getMessage().contains("sequence"));
- }
- }
-
- @Test
- public void testErrorWhenResponderResendsMessage_V0() throws Exception {
- testErrorWhenResponderResendsMessage(D2DConnectionContextV0.PROTOCOL_VERSION);
- }
-
- @Test
- public void testErrorWhenResponderResendsMessage_V1() throws Exception {
- testErrorWhenResponderResendsMessage(D2DConnectionContextV1.PROTOCOL_VERSION);
- }
-
- protected void testErrorWhenResponderEchoesInitiatorMessage(
- int protocolVersion) throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- return;
- }
-
- initiatorCtx = createConnectionContext(protocolVersion, true /** isInitiator */);
- responderCtx = createConnectionContext(protocolVersion, false /** isInitiator */);
-
- byte[] pingMessage = initiatorCtx.encodeMessageToPeer(PING);
- assertEquals(PING, responderCtx.decodeMessageFromPeerAsString(pingMessage));
-
- try {
- initiatorCtx.decodeMessageFromPeerAsString(pingMessage);
- fail("expected exception, but didn't get it");
- } catch (SignatureException expected) {
- }
- }
-
- @Test
- public void testErrorWhenResponderEchoesInitiatorMessage_V0() throws Exception {
- testErrorWhenResponderEchoesInitiatorMessage(D2DConnectionContextV0.PROTOCOL_VERSION);
- }
-
- @Test
- public void testErrorWhenResponderEchoesInitiatorMessage_V1() throws Exception {
- testErrorWhenResponderEchoesInitiatorMessage(D2DConnectionContextV1.PROTOCOL_VERSION);
- }
-
- @Test
- public void testErrorUsingV1InitiatorWithV0Responder() throws SignatureException {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- initiatorCtx = new D2DConnectionContextV1(INITIATOR_ENCODE_KEY, INITIATOR_DECODE_KEY, 1, 1);
- responderCtx = new D2DConnectionContextV0(INITIATOR_DECODE_KEY, 1);
-
- // Decoding the responder's message should succeed, because the decode key and sequence numbers
- // match.
- initiatorCtx.decodeMessageFromPeer(responderCtx.encodeMessageToPeer(PING));
-
- // Responder fails to decodes initiator's encoded message because keys do not match.
- try {
- responderCtx.decodeMessageFromPeer(initiatorCtx.encodeMessageToPeer(PONG));
- fail("Expected verification to fail.");
- } catch (SignatureException e) {
- // Exception expected.
- }
- }
-
- @Test
- public void testErrorWithV0InitiatorV1Responder() throws SignatureException {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- initiatorCtx = new D2DConnectionContextV0(INITIATOR_ENCODE_KEY, 1);
- responderCtx = new D2DConnectionContextV1(INITIATOR_DECODE_KEY, INITIATOR_ENCODE_KEY, 1, 1);
-
- // Decoding the initiator's message should succeed, because the decode key and sequence numbers
- // match.
- responderCtx.decodeMessageFromPeer(initiatorCtx.encodeMessageToPeer(PING));
-
- // Initiator fails to decodes responder's encoded message because keys do not match.
- try {
- initiatorCtx.decodeMessageFromPeer(responderCtx.encodeMessageToPeer(PONG));
- fail("Expected verification to fail.");
- } catch (SignatureException e) {
- // Exception expected.
- }
- }
-
- protected void testSessionUnique(int protocolVersion) throws Exception {
- // Should be the same (we set them up with the same key and sequence number)
- initiatorCtx = createConnectionContext(protocolVersion, true /** isInitiator */);
- responderCtx = createConnectionContext(protocolVersion, false /** isInitiator */);
- Assert.assertArrayEquals(initiatorCtx.getSessionUnique(), responderCtx.getSessionUnique());
-
- // Change just the key (should not match)
- SecretKey wrongKey = new SecretKeySpec("wrong".getBytes("UTF8"), "AES");
- responderCtx = createConnectionContext(protocolVersion, false, wrongKey, wrongKey, 0, 1);
- assertFalse(Arrays.equals(initiatorCtx.getSessionUnique(), responderCtx.getSessionUnique()));
-
- // Change just the sequence number (should still match)
- responderCtx = createConnectionContext(
- protocolVersion, false, INITIATOR_ENCODE_KEY, INITIATOR_DECODE_KEY, 2, 2);
- Assert.assertArrayEquals(initiatorCtx.getSessionUnique(), responderCtx.getSessionUnique());
- }
-
- @Test
- public void testSessionUnique_V0() throws Exception {
- testSessionUnique(D2DConnectionContextV0.PROTOCOL_VERSION);
- }
-
- @Test
- public void testSessionUnique_V1() throws Exception {
- testSessionUnique(D2DConnectionContextV1.PROTOCOL_VERSION);
- }
-
- @Test
- public void testSessionUniqueValues_V0() throws Exception {
- // The key and the session unique value should match ones in the equivalent test in
- // @link {cs/Nearby/D2DCrypto/Tests/D2DConnectionContextTest.m}
- byte[] key =
- new byte[] {
- (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07,
- (byte) 0x08, (byte) 0x09, (byte) 0x0a, (byte) 0x0b, (byte) 0x0c, (byte) 0x0d, (byte) 0x0e,
- (byte) 0x0f, (byte) 0x10, (byte) 0x11, (byte) 0x12, (byte) 0x13, (byte) 0x14, (byte) 0x15,
- (byte) 0x16, (byte) 0x17, (byte) 0x18, (byte) 0x19, (byte) 0x1a, (byte) 0x1b, (byte) 0x1c,
- (byte) 0x1d, (byte) 0x1e, (byte) 0x1f, (byte) 0x20
- };
- byte[] sessionUnique =
- new byte[] {
- (byte) 0x70, (byte) 0x7a, (byte) 0x17, (byte) 0x27, (byte) 0xa3, (byte) 0x0e, (byte) 0x68,
- (byte) 0x63, (byte) 0x38, (byte) 0xdf, (byte) 0x72, (byte) 0x62, (byte) 0xf4, (byte) 0xb0,
- (byte) 0x41, (byte) 0xac, (byte) 0x75, (byte) 0x8b, (byte) 0xca, (byte) 0x3b, (byte) 0x11,
- (byte) 0xd4, (byte) 0x09, (byte) 0x64, (byte) 0x96, (byte) 0x54, (byte) 0xb4, (byte) 0x9b,
- (byte) 0x43, (byte) 0xe6, (byte) 0x9b, (byte) 0xce
- };
-
- SecretKey secretKey = new SecretKeySpec(key, "AES");
- D2DConnectionContext context = new D2DConnectionContextV0(secretKey, 1);
-
- Assert.assertArrayEquals(context.getSessionUnique(), sessionUnique);
- }
-
- @Test
- public void testSessionUniqueValues_V1_Initiator() throws Exception {
- // The key and the session unique value should match ones in the equivalent test in
- // @link {cs/Nearby/D2DCrypto/Tests/D2DConnectionContextTest.m}
- byte[] sessionUnique =
- new byte[] {
- (byte) 0x91, (byte) 0xc7, (byte) 0xc9, (byte) 0x26, (byte) 0x2c, (byte) 0x17, (byte) 0x8a,
- (byte) 0xa0, (byte) 0x36, (byte) 0x9f, (byte) 0xf2, (byte) 0x05, (byte) 0x20, (byte) 0x98,
- (byte) 0x38, (byte) 0x53, (byte) 0xa5, (byte) 0x46, (byte) 0xab, (byte) 0x3a, (byte) 0x21,
- (byte) 0x3b, (byte) 0x76, (byte) 0x58, (byte) 0x59, (byte) 0x4e, (byte) 0xe7, (byte) 0xe3,
- (byte) 0xc1, (byte) 0x69, (byte) 0x87, (byte) 0xfa
- };
-
- D2DConnectionContext initiatorContext = new D2DConnectionContextV1(
- INITIATOR_ENCODE_KEY, INITIATOR_DECODE_KEY, 0, 1);
- D2DConnectionContext responderContext = new D2DConnectionContextV1(
- INITIATOR_DECODE_KEY, INITIATOR_ENCODE_KEY, 1, 0);
-
- // Both the initiator and responder must be the same.
- Assert.assertArrayEquals(initiatorContext.getSessionUnique(), sessionUnique);
- Assert.assertArrayEquals(responderContext.getSessionUnique(), sessionUnique);
- }
-
- @Test
- public void testSaveSessionV0() throws Exception {
- D2DConnectionContext initiatorCtx = new D2DConnectionContextV0(INITIATOR_ENCODE_KEY, 1);
- D2DConnectionContext responderCtx = new D2DConnectionContextV0(INITIATOR_ENCODE_KEY, 1);
-
- // Save the state
- byte[] initiatorSavedSessionState = initiatorCtx.saveSession();
- byte[] responderSavedSessionState = responderCtx.saveSession();
-
- // Try to rebuild the context
- initiatorCtx = D2DConnectionContext.fromSavedSession(initiatorSavedSessionState);
- responderCtx = D2DConnectionContext.fromSavedSession(responderSavedSessionState);
-
- // Sanity check
- assertEquals(1, initiatorCtx.getSequenceNumberForDecoding());
- assertEquals(1, responderCtx.getSequenceNumberForDecoding());
- Assert.assertArrayEquals(initiatorCtx.getSessionUnique(), responderCtx.getSessionUnique());
-
- // Make sure they can still talk to one another
- assertEquals(PING,
- responderCtx.decodeMessageFromPeerAsString(initiatorCtx.encodeMessageToPeer(PING)));
- assertEquals(PONG,
- initiatorCtx.decodeMessageFromPeerAsString(responderCtx.encodeMessageToPeer(PONG)));
- }
-
- @Test
- public void testSaveSessionV0_negativeSeqNumber() throws Exception {
- D2DConnectionContext initiatorCtx = new D2DConnectionContextV0(INITIATOR_ENCODE_KEY, -5);
-
- // Save the state
- byte[] initiatorSavedSessionState = initiatorCtx.saveSession();
-
- // Try to rebuild the context
- initiatorCtx = D2DConnectionContext.fromSavedSession(initiatorSavedSessionState);
-
- // Sanity check
- assertEquals(-5, initiatorCtx.getSequenceNumberForDecoding());
- }
-
- @Test
- public void testSaveSessionV0_shortKey() throws Exception {
- D2DConnectionContext initiatorCtx = new D2DConnectionContextV0(INITIATOR_ENCODE_KEY, -5);
-
- // Save the state
- byte[] initiatorSavedSessionState = initiatorCtx.saveSession();
-
- // Try to rebuild the context
- try {
- D2DConnectionContext.fromSavedSession(Arrays.copyOf(initiatorSavedSessionState,
- initiatorSavedSessionState.length - 1));
- fail("Expected failure as key is too short");
- } catch (IllegalArgumentException e) {
- // expected
- }
- }
-
- @Test
- public void testSaveSession_unknownProtocolVersion() throws Exception {
- D2DConnectionContext initiatorCtx = new D2DConnectionContextV0(INITIATOR_ENCODE_KEY, -5);
-
- // Save the state
- byte[] initiatorSavedSessionState = initiatorCtx.saveSession();
-
- // Mess with the protocol version
- initiatorSavedSessionState[0] = (byte) 0xff;
-
- // Try to rebuild the context
- try {
- D2DConnectionContext.fromSavedSession(initiatorSavedSessionState);
- fail("Expected failure as 0xff is not a valid protocol version");
- } catch (IllegalArgumentException e) {
- // expected
- }
-
- // Mess with the protocol version in the other direction
- initiatorSavedSessionState[0] = 2;
-
- // Try to rebuild the context
- try {
- D2DConnectionContext.fromSavedSession(initiatorSavedSessionState);
- fail("Expected failure as 2 is not a valid protocol version");
- } catch (IllegalArgumentException e) {
- // expected
- }
- }
-
- @Test
- public void testSaveSessionV1() throws Exception {
- D2DConnectionContext initiatorCtx = new D2DConnectionContextV1(INITIATOR_ENCODE_KEY,
- INITIATOR_DECODE_KEY, 0, 1);
- D2DConnectionContext responderCtx = new D2DConnectionContextV1(INITIATOR_DECODE_KEY,
- INITIATOR_ENCODE_KEY, 1, 0);
-
- // Save the state
- byte[] initiatorSavedSessionState = initiatorCtx.saveSession();
- byte[] responderSavedSessionState = responderCtx.saveSession();
-
- // Try to rebuild the context
- initiatorCtx = D2DConnectionContext.fromSavedSession(initiatorSavedSessionState);
- responderCtx = D2DConnectionContext.fromSavedSession(responderSavedSessionState);
-
- // Sanity check
- assertEquals(1, initiatorCtx.getSequenceNumberForDecoding());
- assertEquals(0, initiatorCtx.getSequenceNumberForEncoding());
- assertEquals(0, responderCtx.getSequenceNumberForDecoding());
- assertEquals(1, responderCtx.getSequenceNumberForEncoding());
- Assert.assertArrayEquals(initiatorCtx.getSessionUnique(), responderCtx.getSessionUnique());
-
- // Make sure they can still talk to one another
- assertEquals(PING,
- responderCtx.decodeMessageFromPeerAsString(initiatorCtx.encodeMessageToPeer(PING)));
- assertEquals(PONG,
- initiatorCtx.decodeMessageFromPeerAsString(responderCtx.encodeMessageToPeer(PONG)));
- }
-
- @Test
- public void testSaveSessionV1_negativeSeqNumbers() throws Exception {
- D2DConnectionContext initiatorCtx = new D2DConnectionContextV1(INITIATOR_ENCODE_KEY,
- INITIATOR_DECODE_KEY, -8, -10);
-
- // Save the state
- byte[] initiatorSavedSessionState = initiatorCtx.saveSession();
-
- // Try to rebuild the context
- initiatorCtx = D2DConnectionContext.fromSavedSession(initiatorSavedSessionState);
-
- // Sanity check
- assertEquals(-10, initiatorCtx.getSequenceNumberForDecoding());
- assertEquals(-8, initiatorCtx.getSequenceNumberForEncoding());
- }
-
- @Test
- public void testSaveSessionV1_tooShort() throws Exception {
- D2DConnectionContext initiatorCtx = new D2DConnectionContextV1(INITIATOR_ENCODE_KEY,
- INITIATOR_DECODE_KEY, -8, -10);
-
- // Save the state
- byte[] initiatorSavedSessionState = initiatorCtx.saveSession();
-
- // Try to rebuild the context
- try {
- D2DConnectionContext.fromSavedSession(
- Arrays.copyOf(initiatorSavedSessionState, initiatorSavedSessionState.length - 1));
- fail("Expected error as saved session is too short");
- } catch (IllegalArgumentException e) {
- // expected
- }
-
- // Sanity check
- assertEquals(-10, initiatorCtx.getSequenceNumberForDecoding());
- assertEquals(-8, initiatorCtx.getSequenceNumberForEncoding());
- }
-
- D2DConnectionContext createConnectionContext(int protocolVersion, boolean isInitiator) {
- return createConnectionContext(
- protocolVersion, isInitiator, INITIATOR_ENCODE_KEY, INITIATOR_DECODE_KEY, 0, 1);
- }
-
- D2DConnectionContext createConnectionContext(
- int protocolVersion, boolean isInitiator,
- SecretKey initiatorEncodeKey, SecretKey initiatorDecodeKey,
- int initiatorSequenceNumber, int responderSequenceNumber) {
- if (protocolVersion == D2DConnectionContextV0.PROTOCOL_VERSION) {
- return new D2DConnectionContextV0(initiatorEncodeKey, responderSequenceNumber);
- } else if (protocolVersion == D2DConnectionContextV1.PROTOCOL_VERSION) {
- return isInitiator
- ? new D2DConnectionContextV1(
- initiatorEncodeKey, initiatorDecodeKey,
- initiatorSequenceNumber, responderSequenceNumber)
- : new D2DConnectionContextV1(
- initiatorDecodeKey, initiatorEncodeKey,
- responderSequenceNumber, initiatorSequenceNumber);
- } else {
- throw new IllegalArgumentException("Unknown version: " + protocolVersion);
- }
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/D2DDiffieHellmanKeyExchangeHandshakeTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securegcm/D2DDiffieHellmanKeyExchangeHandshakeTest.java
deleted file mode 100644
index 4de794a..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/D2DDiffieHellmanKeyExchangeHandshakeTest.java
+++ /dev/null
@@ -1,432 +0,0 @@
-// Copyright 2020 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
-//
-// https://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 com.google.security.cryptauth.lib.securegcm.DeviceToDeviceMessagesProto.InitiatorHello;
-import com.google.security.cryptauth.lib.securegcm.DeviceToDeviceMessagesProto.ResponderHello;
-import com.google.security.cryptauth.lib.securegcm.TransportCryptoOps.Payload;
-import com.google.security.cryptauth.lib.securegcm.TransportCryptoOps.PayloadType;
-import com.google.security.cryptauth.lib.securemessage.PublicKeyProtoUtil;
-import java.nio.charset.Charset;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.security.SignatureException;
-import javax.crypto.SecretKey;
-import junit.framework.TestCase;
-import org.junit.Assert;
-
-/**
- * Android compatible tests for the {@link D2DDiffieHellmanKeyExchangeHandshake} class.
- */
-public class D2DDiffieHellmanKeyExchangeHandshakeTest extends TestCase {
-
- private static final byte[] RESPONDER_HELLO_MESSAGE =
- "first payload".getBytes(Charset.forName("UTF-8"));
-
- private static final String PING = "ping";
-
- @Override
- protected void setUp() throws Exception {
- KeyEncodingTest.installSunEcSecurityProviderIfNecessary();
- super.setUp();
- }
-
- public void testHandshakeWithPayload() throws Exception {
-
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // initiator:
- D2DHandshakeContext initiatorHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forInitiator();
- assertFalse(initiatorHandshakeContext.canSendPayloadInHandshakeMessage());
- assertFalse(initiatorHandshakeContext.isHandshakeComplete());
- byte[] initiatorHello = initiatorHandshakeContext.getNextHandshakeMessage();
- assertFalse(initiatorHandshakeContext.isHandshakeComplete());
- // (send initiatorHello to responder)
-
- // responder:
- D2DHandshakeContext responderHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forResponder();
- byte[] payload = responderHandshakeContext.parseHandshakeMessage(initiatorHello);
- assertEquals(0, payload.length);
- assertTrue(responderHandshakeContext.canSendPayloadInHandshakeMessage());
- assertFalse(responderHandshakeContext.isHandshakeComplete());
- byte[] responderHelloAndPayload = responderHandshakeContext.getNextHandshakeMessage(
- RESPONDER_HELLO_MESSAGE);
- assertTrue(responderHandshakeContext.isHandshakeComplete());
- D2DConnectionContext responderCtx = responderHandshakeContext.toConnectionContext();
- // (send responderHelloAndPayload to initiator)
-
- // initiator
- byte[] messageFromPayload =
- initiatorHandshakeContext.parseHandshakeMessage(responderHelloAndPayload);
- Assert.assertArrayEquals(RESPONDER_HELLO_MESSAGE, messageFromPayload);
- assertTrue(initiatorHandshakeContext.isHandshakeComplete());
- D2DConnectionContextV1 initiatorCtx =
- (D2DConnectionContextV1) initiatorHandshakeContext.toConnectionContext();
-
- // Test that that initiator and responder contexts are initialized correctly.
- checkInitializedConnectionContexts(initiatorCtx, responderCtx);
- }
-
- public void testHandshakeWithoutPayload() throws Exception {
-
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // initiator:
- D2DHandshakeContext initiatorHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forInitiator();
- byte[] initiatorHello = initiatorHandshakeContext.getNextHandshakeMessage();
- // (send initiatorHello to responder)
-
- // responder:
- D2DHandshakeContext responderHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forResponder();
- responderHandshakeContext.parseHandshakeMessage(initiatorHello);
- byte[] responderHelloAndPayload = responderHandshakeContext.getNextHandshakeMessage();
- assertTrue(responderHandshakeContext.isHandshakeComplete());
- D2DConnectionContext responderCtx = responderHandshakeContext.toConnectionContext();
- // (send responderHelloAndPayload to initiator)
-
- // initiator
- byte[] messageFromPayload =
- initiatorHandshakeContext.parseHandshakeMessage(responderHelloAndPayload);
- assertEquals(0, messageFromPayload.length);
- assertTrue(initiatorHandshakeContext.isHandshakeComplete());
- D2DConnectionContext initiatorCtx = initiatorHandshakeContext.toConnectionContext();
-
- // Test that that initiator and responder contexts are initialized correctly.
- checkInitializedConnectionContexts(initiatorCtx, responderCtx);
- }
-
- public void testErrorWhenInitiatorOrResponderSendTwice() throws Exception {
-
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // initiator:
- D2DHandshakeContext initiatorHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forInitiator();
- byte[] initiatorHello = initiatorHandshakeContext.getNextHandshakeMessage();
- try {
- initiatorHandshakeContext.getNextHandshakeMessage();
- fail("Expected error as initiator has no more initiator messages to send");
- } catch (HandshakeException expected) {
- assertTrue(expected.getMessage().contains("Cannot get next message"));
- }
- // (send initiatorHello to responder)
-
- // responder:
- D2DHandshakeContext responderHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forResponder();
- responderHandshakeContext.parseHandshakeMessage(initiatorHello);
- responderHandshakeContext.getNextHandshakeMessage();
- try {
- responderHandshakeContext.getNextHandshakeMessage();
- fail("Expected error as initiator has no more responder messages to send");
- } catch (HandshakeException expected) {
- assertTrue(expected.getMessage().contains("Cannot get"));
- }
- }
-
- public void testInitiatorOrResponderFailOnEmptyMessage() throws Exception {
-
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- D2DHandshakeContext handshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forInitiator();
- try {
- handshakeContext.parseHandshakeMessage(null);
- fail("Expected to crash on null message");
- } catch (HandshakeException expected) {
- assertTrue(expected.getMessage().contains("short"));
- }
- try {
- handshakeContext.parseHandshakeMessage(new byte[0]);
- fail("Expected to crash on empty message");
- } catch (HandshakeException expected) {
- assertTrue(expected.getMessage().contains("short"));
- }
- }
-
- public void testPrematureConversionToConnection() throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // initiator:
- D2DHandshakeContext initiatorHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forInitiator();
- try {
- initiatorHandshakeContext.toConnectionContext();
- fail("Expected to crash: initiator hasn't done anything to deserve full connection");
- } catch (HandshakeException expected) {
- assertTrue(expected.getMessage().contains("not complete"));
- }
-
- byte[] initiatorHello = initiatorHandshakeContext.getNextHandshakeMessage();
- try {
- initiatorHandshakeContext.toConnectionContext();
- fail("Expected to crash: initiator hasn't yet received responder's key");
- } catch (HandshakeException expected) {
- assertTrue(expected.getMessage().contains("not complete"));
- }
- // (send initiatorHello to responder)
-
- // responder:
- D2DHandshakeContext responderHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forResponder();
- responderHandshakeContext.parseHandshakeMessage(initiatorHello);
- try {
- initiatorHandshakeContext.toConnectionContext();
- fail("Expected to crash: responder hasn't yet send their key");
- } catch (HandshakeException expected) {
- assertTrue(expected.getMessage().contains("not complete"));
- }
- }
-
- public void testCannotReuseHandshakeContext() throws Exception {
-
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // initiator:
- D2DHandshakeContext initiatorHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forInitiator();
- byte[] initiatorHello = initiatorHandshakeContext.getNextHandshakeMessage();
- // (send initiatorHello to responder)
-
- // responder:
- D2DHandshakeContext responderHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forResponder();
- responderHandshakeContext.parseHandshakeMessage(initiatorHello);
- byte[] responderHelloAndPayload = responderHandshakeContext.getNextHandshakeMessage();
- D2DConnectionContext responderCtx = responderHandshakeContext.toConnectionContext();
- // (send responderHelloAndPayload to initiator)
-
- // initiator
- initiatorHandshakeContext.parseHandshakeMessage(responderHelloAndPayload);
- D2DConnectionContext initiatorCtx = initiatorHandshakeContext.toConnectionContext();
-
- // Test that that initiator and responder contexts are initialized correctly.
- checkInitializedConnectionContexts(initiatorCtx, responderCtx);
-
- // Try to get another full context
- try {
- initiatorHandshakeContext.toConnectionContext();
- fail("Expected crash: initiator context has already been used");
- } catch (HandshakeException expected) {
- assertTrue(expected.getMessage().contains("used"));
- }
- try {
- responderHandshakeContext.toConnectionContext();
- fail("Expected crash: responder context has already been used");
- } catch (HandshakeException expected) {
- assertTrue(expected.getMessage().contains("used"));
- }
- }
-
- public void testErrorWhenInitiatorEchosResponderHello() throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // Initiator echoing back responder's first packet:
- D2DDiffieHellmanKeyExchangeHandshake partialInitiatorContext =
- D2DDiffieHellmanKeyExchangeHandshake.forInitiator();
- byte[] initiatorHello = partialInitiatorContext.getNextHandshakeMessage();
-
- D2DDiffieHellmanKeyExchangeHandshake partialResponderCtx =
- D2DDiffieHellmanKeyExchangeHandshake.forResponder();
- partialResponderCtx.parseHandshakeMessage(initiatorHello);
- byte[] responderHelloAndPayload =
- partialResponderCtx.getNextHandshakeMessage(RESPONDER_HELLO_MESSAGE);
- D2DConnectionContext responderCtx = partialResponderCtx.toConnectionContext();
-
- try {
- // initiator sends responderHelloAndPayload to responder
- responderCtx.decodeMessageFromPeerAsString(responderHelloAndPayload);
- fail("expected exception, but didn't get it");
- } catch (SignatureException expected) {
- assertTrue(expected.getMessage().contains("Signature failed verification"));
- }
- }
-
- public void testErrorWhenInitiatorResendsMessage() throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // Initiator repeating the same packet twice
- D2DDiffieHellmanKeyExchangeHandshake partialInitiatorContext =
- D2DDiffieHellmanKeyExchangeHandshake.forInitiator();
- byte[] initiatorHello = partialInitiatorContext.getNextHandshakeMessage();
-
- D2DDiffieHellmanKeyExchangeHandshake partialResponderCtx =
- D2DDiffieHellmanKeyExchangeHandshake.forResponder();
- partialResponderCtx.parseHandshakeMessage(initiatorHello);
- byte[] responderHelloAndPayload =
- partialResponderCtx.getNextHandshakeMessage(RESPONDER_HELLO_MESSAGE);
- D2DConnectionContext responderCtx = partialResponderCtx.toConnectionContext();
-
- partialInitiatorContext.parseHandshakeMessage(responderHelloAndPayload);
- D2DConnectionContext initiatorCtx = partialInitiatorContext.toConnectionContext();
-
- byte[] pingMessage = initiatorCtx.encodeMessageToPeer(PING);
- assertEquals(PING, responderCtx.decodeMessageFromPeerAsString(pingMessage));
-
- try {
- // send pingMessage to responder again
- responderCtx.decodeMessageFromPeerAsString(pingMessage);
- fail("expected exception, but didn't get it");
- } catch (SignatureException expected) {
- assertTrue(expected.getMessage().contains("sequence"));
- }
- }
-
- public void testErrorWhenResponderResendsFirstMessage() throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- D2DDiffieHellmanKeyExchangeHandshake partialInitiatorContext =
- D2DDiffieHellmanKeyExchangeHandshake.forInitiator();
- byte[] initiatorHello = partialInitiatorContext.getNextHandshakeMessage();
-
- D2DDiffieHellmanKeyExchangeHandshake partialResponderCtx =
- D2DDiffieHellmanKeyExchangeHandshake.forResponder();
- partialResponderCtx.parseHandshakeMessage(initiatorHello);
- byte[] responderHelloAndPayload =
- partialResponderCtx.getNextHandshakeMessage(RESPONDER_HELLO_MESSAGE);
-
- partialInitiatorContext.parseHandshakeMessage(responderHelloAndPayload);
- D2DConnectionContext initiatorCtx = partialInitiatorContext.toConnectionContext();
-
- try {
- // Send the responderHelloAndPayload again. This time, the initiator will
- // process it as a normal message.
- initiatorCtx.decodeMessageFromPeerAsString(responderHelloAndPayload);
- fail("expected exception, but didn't get it");
- } catch (SignatureException expected) {
- assertTrue(expected.getMessage().contains("wrong message type"));
- }
- }
-
- public void testHandshakeWithInitiatorV1AndResponderV0() throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // Initialize initiator side.
- D2DHandshakeContext initiatorHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forInitiator();
- byte[] initiatorHello = initiatorHandshakeContext.getNextHandshakeMessage();
-
- // Set up keys used by the responder.
- PublicKey initiatorPublicKey = PublicKeyProtoUtil.parsePublicKey(
- InitiatorHello.parseFrom(initiatorHello).getPublicDhKey());
- KeyPair responderKeyPair = PublicKeyProtoUtil.generateEcP256KeyPair();
- SecretKey sharedKey =
- EnrollmentCryptoOps.doKeyAgreement(responderKeyPair.getPrivate(), initiatorPublicKey);
-
- // Construct a responder hello message without the version field, whose payload is encrypted
- // with the shared key.
- byte[] responderHello = D2DCryptoOps.signcryptPayload(
- new Payload(
- PayloadType.DEVICE_TO_DEVICE_RESPONDER_HELLO_PAYLOAD,
- D2DConnectionContext.createDeviceToDeviceMessage(new byte[] {}, 1).toByteArray()),
- sharedKey,
- ResponderHello.newBuilder()
- .setPublicDhKey(
- PublicKeyProtoUtil.encodePublicKey(responderKeyPair.getPublic()))
- .build().toByteArray());
-
- // Handle V0 responder hello message.
- initiatorHandshakeContext.parseHandshakeMessage(responderHello);
- D2DConnectionContext initiatorCtx = initiatorHandshakeContext.toConnectionContext();
-
- assertEquals(D2DConnectionContextV0.PROTOCOL_VERSION, initiatorCtx.getProtocolVersion());
- assertEquals(1, initiatorCtx.getSequenceNumberForEncoding());
- assertEquals(1, initiatorCtx.getSequenceNumberForDecoding());
- }
-
- public void testHandshakeWithInitiatorV0AndResponderV1() throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // Construct an initiator hello message without the version field.
- byte[] initiatorHello = InitiatorHello.newBuilder()
- .setPublicDhKey(PublicKeyProtoUtil.encodePublicKey(
- PublicKeyProtoUtil.generateEcP256KeyPair().getPublic()))
- .build()
- .toByteArray();
-
- // Handle V0 initiator hello message.
- D2DHandshakeContext responderHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forResponder();
- responderHandshakeContext.parseHandshakeMessage(initiatorHello);
- responderHandshakeContext.getNextHandshakeMessage();
- D2DConnectionContext responderCtx = responderHandshakeContext.toConnectionContext();
-
- assertEquals(D2DConnectionContextV0.PROTOCOL_VERSION, responderCtx.getProtocolVersion());
- assertEquals(1, responderCtx.getSequenceNumberForEncoding());
- assertEquals(1, responderCtx.getSequenceNumberForDecoding());
- }
-
- private void checkInitializedConnectionContexts(
- D2DConnectionContext initiatorCtx, D2DConnectionContext responderCtx) {
- assertNotNull(initiatorCtx);
- assertNotNull(responderCtx);
- assertEquals(D2DConnectionContextV1.PROTOCOL_VERSION, initiatorCtx.getProtocolVersion());
- assertEquals(D2DConnectionContextV1.PROTOCOL_VERSION, responderCtx.getProtocolVersion());
- assertEquals(initiatorCtx.getEncodeKey(), responderCtx.getDecodeKey());
- assertEquals(initiatorCtx.getDecodeKey(), responderCtx.getEncodeKey());
- assertEquals(0, initiatorCtx.getSequenceNumberForEncoding());
- assertEquals(1, initiatorCtx.getSequenceNumberForDecoding());
- assertEquals(1, responderCtx.getSequenceNumberForEncoding());
- assertEquals(0, responderCtx.getSequenceNumberForDecoding());
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ed25519Test.java b/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ed25519Test.java
deleted file mode 100644
index 6ae95d8..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ed25519Test.java
+++ /dev/null
@@ -1,195 +0,0 @@
-// Copyright 2020 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
-//
-// https://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 static org.hamcrest.CoreMatchers.containsString;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertThat;
-
-import com.google.security.cryptauth.lib.securegcm.Ed25519.Ed25519Exception;
-import java.math.BigInteger;
-import junit.framework.TestCase;
-
-/**
- * Android compatible tests for the {@link Ed25519} class.
- */
-public class Ed25519Test extends TestCase {
-
- // Points on the curve
- private static final int HEX_RADIX = 16;
- private static final BigInteger[] KM = new BigInteger[] {
- new BigInteger("1981FB43F103290ECF9772022DB8B19BFAF389057ED91E8486EB368763435925", HEX_RADIX),
- new BigInteger("A714C34F3B588AAC92FD2587884A20964FD351A1F147D5C4BBF5C2F37A77C36", HEX_RADIX)};
- private static final BigInteger[] KN = new BigInteger[] {
- new BigInteger("201A184F47D9A7973891D148E3D1C864D8084547131C2C1CEFB7EEBD26C63567", HEX_RADIX),
- new BigInteger("6DA2D3B18EC4F9AA3B08E39C997CD8BF6E9948FFD4FEFFECAF8DD0B3D648B7E8", HEX_RADIX)};
-
- // Curve prime P
- private static final BigInteger P =
- new BigInteger("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED", HEX_RADIX);
-
- // Test vectors obtain by multiplying KM by k by manually using the official implementation
- // see: http://ed25519.cr.yp.to/python/ed25519.py
- // k = 2
- private static final BigInteger[] KM_2 = new BigInteger[] {
- new BigInteger("718079972e63c2d62caf0ee93ec6f00337ceaff4e283181c04c4082b1d5e1ecf", HEX_RADIX),
- new BigInteger("143d18d393a8058c8614335bf36bf59364cc7c451db74726b322ce9d0b826d51", HEX_RADIX)
- };
- // k = 3
- private static final BigInteger[] KM_3 = new BigInteger[] {
- new BigInteger("39DA3C92EFC0577586B4D58F4A5C0BF65A6CC8F6BF358F38D70B2E6C28A31E8E", HEX_RADIX),
- new BigInteger("6D194F054B3FC2BE217F6A360BBEC747D2937FCEBD74B67FC3B20ED638ADD670", HEX_RADIX)
- };
- // k = 317698
- private static final BigInteger[] KM_317698 = new BigInteger[] {
- new BigInteger("7945D0ADEB568B16495476E81ADF281F4515439AE835914FBF6CEEAFEB9CD7E8", HEX_RADIX),
- new BigInteger("3631503DCDEBC0BF9BB1FFC3984A8CB52A34FFC2E77E9C19FD896DC6EE64A530", HEX_RADIX)
- };
- // k = P
- private static final BigInteger[] KM_HUGE = new BigInteger[] {
- new BigInteger("530162B05F440E00E219DFD3188524821C860C41FD87B9AC6AF2A283FDD585A1", HEX_RADIX),
- new BigInteger("48385A7D2BB858F3DB7F72E7CDFE218B9CA84DDA8BD64C3775AA43551D974F60", HEX_RADIX)
- };
- // k = P + 10000
- private static final BigInteger[] KM_XRAHUGE = new BigInteger[] {
- new BigInteger("16377E9F5EE2C0F4C70E17AC298EF670700A7CB186EEB0DA10CDD59635000AF8", HEX_RADIX),
- new BigInteger("5BD7921EEE662ACBAC3A96D8B6039D2356F154859FAF41FD2F0D99DF06CD2EAE", HEX_RADIX)
- };
-
- // Helpful constants
- private static final BigInteger ONE = BigInteger.ONE;
- private static final BigInteger ZERO = BigInteger.ZERO;
-
- // Identity element of the group (the zero) in affine and extended representations
- private static final BigInteger[] ID = new BigInteger[] {ZERO, ONE};
- private static final BigInteger[] ID_EX = new BigInteger[] {ZERO, ONE, ONE, ZERO};
-
- public void testValidPoints() throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // We've got a couple of valid points
- Ed25519.validateAffinePoint(KM);
- Ed25519.validateAffinePoint(KN);
-
- // And a bunch of invalid ones
- try {
- Ed25519.validateAffinePoint(new BigInteger[] {ZERO, ONE});
- fail("Validate point not catching zero x coordinates");
- } catch (Ed25519Exception e) {
- assertThat(e.getMessage(), containsString("positive"));
- }
-
- try {
- Ed25519.validateAffinePoint(new BigInteger[] {ONE, ZERO});
- fail("Validate point not catching zero y coordinates");
- } catch (Ed25519Exception e) {
- assertThat(e.getMessage(), containsString("positive"));
- }
-
- try {
- Ed25519.validateAffinePoint(new BigInteger[] {new BigInteger("-1"), ONE});
- fail("Validate point not catching negative x coordinates");
- } catch (Ed25519Exception e) {
- assertThat(e.getMessage(), containsString("positive"));
- }
-
- try {
- Ed25519.validateAffinePoint(new BigInteger[] {ONE, new BigInteger("-1")});
- fail("Validate point not catching negative y coordinates");
- } catch (Ed25519Exception e) {
- assertThat(e.getMessage(), containsString("positive"));
- }
-
- try {
- Ed25519.validateAffinePoint(new BigInteger[] {ONE, ONE});
- fail("Validate point not catching points that are not on curve");
- } catch (Ed25519Exception e) {
- assertThat(e.getMessage(), containsString("expected curve"));
- }
- }
-
- public void testAffineExtendedConversion() throws Exception {
- BigInteger[] km1 = Ed25519.toAffine(Ed25519.toExtended(KM));
- BigInteger[] kn1 = Ed25519.toAffine(Ed25519.toExtended(KN));
-
- assertArrayEquals(KM, km1);
- assertArrayEquals(KN, kn1);
-
- assertArrayEquals(ID, Ed25519.toAffine(ID_EX));
- assertArrayEquals(ID_EX, Ed25519.toExtended(ID));
- }
-
- public void testRepresentationCheck() throws Exception {
- Ed25519.checkPointIsInAffineRepresentation(KM);
- Ed25519.checkPointIsInExtendedRepresentation(ID_EX);
-
- try {
- Ed25519.checkPointIsInExtendedRepresentation(KM);
- fail("Point is not really in extended representation, expected failure");
- } catch (Ed25519Exception e) {
- assertThat(e.getMessage(), containsString("not in extended"));
- }
-
- try {
- Ed25519.checkPointIsInAffineRepresentation(Ed25519.toExtended(KM));
- fail("Point is not really in affine representation, expected failure");
- } catch (Ed25519Exception e) {
- assertThat(e.getMessage(), containsString("not in affine"));
- }
- }
-
- public void testAddSubtractExtendedPoints() throws Exception {
- // Adding/subtracting identity to/from itself should yield the identity point
- assertArrayEquals(ID, Ed25519.addAffinePoints(ID, ID));
- assertArrayEquals(ID, Ed25519.subtractAffinePoints(ID, ID));
-
- // In fact adding/subtracting the identity point to/from any point should yield that point
- assertArrayEquals(KM, Ed25519.addAffinePoints(KM, ID));
- assertArrayEquals(KM, Ed25519.subtractAffinePoints(KM, ID));
-
- // Subtracting a point from itself should yield the identity element
- assertArrayEquals(ID, Ed25519.subtractAffinePoints(KM, KM));
- assertArrayEquals(ID, Ed25519.subtractAffinePoints(KN, KN));
-
- // Adding and subtracting should yield the same point
- assertArrayEquals(KM, Ed25519.subtractAffinePoints(Ed25519.addAffinePoints(KM, KN), KN));
- assertArrayEquals(KN, Ed25519.subtractAffinePoints(Ed25519.addAffinePoints(KN, KM), KM));
- }
-
- public void testScalarMultiplyExtendedPoints() throws Exception {
- // A point times one is the point itself
- assertArrayEquals(KM, Ed25519.scalarMultiplyAffinePoint(KM, ONE));
- assertArrayEquals(KN, Ed25519.scalarMultiplyAffinePoint(KN, ONE));
-
- // A point times zero is the identity point
- assertArrayEquals(ID, Ed25519.scalarMultiplyAffinePoint(KM, ZERO));
- assertArrayEquals(ID, Ed25519.scalarMultiplyAffinePoint(KN, ZERO));
-
- // The identity times a scalar is the identity
- assertArrayEquals(ID, Ed25519.scalarMultiplyAffinePoint(ID, BigInteger.valueOf(317698)));
-
- // Use test vectors
- assertArrayEquals(KM_2, Ed25519.scalarMultiplyAffinePoint(KM, BigInteger.valueOf(2)));
- assertArrayEquals(KM_3, Ed25519.scalarMultiplyAffinePoint(KM, BigInteger.valueOf(3)));
- assertArrayEquals(KM_317698, Ed25519.scalarMultiplyAffinePoint(KM, BigInteger.valueOf(317698)));
- assertArrayEquals(KM_HUGE, Ed25519.scalarMultiplyAffinePoint(KM, P));
- assertArrayEquals(KM_XRAHUGE,
- Ed25519.scalarMultiplyAffinePoint(KM, P.add(BigInteger.valueOf(10000))));
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/EnrollmentCryptoOpsTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securegcm/EnrollmentCryptoOpsTest.java
deleted file mode 100644
index 4437045..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/EnrollmentCryptoOpsTest.java
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2020 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
-//
-// https://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 com.google.protobuf.ByteString;
-import com.google.security.cryptauth.lib.securegcm.SecureGcmProto.GcmDeviceInfo;
-import com.google.security.cryptauth.lib.securemessage.PublicKeyProtoUtil;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.GenericPublicKey;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.util.Arrays;
-import javax.crypto.SecretKey;
-import junit.framework.TestCase;
-
-/**
- * Android compatible tests for the {@link EnrollmentCryptoOps} class.
- */
-public class EnrollmentCryptoOpsTest extends TestCase {
-
- private static final long DEVICE_ID = 1234567890L;
- private static final byte[] GCM_REGISTRATION_ID = { -0x80, 0, -0x80, 0, -0x80, 0 };
- private static final String DEVICE_MODEL = "TEST DEVICE";
- private static final String LOCALE = "en";
- private static final byte[] SESSION_ID = { 5, 5, 4, 4, 3, 3, 2, 2, 1, 1 };
- private static final String OAUTH_TOKEN = "1/23456etc";
-
- @Override
- protected void setUp() throws Exception {
- KeyEncodingTest.installSunEcSecurityProviderIfNecessary();
- assertEquals(
- PublicKeyProtoUtil.isLegacyCryptoRequired(), KeyEncoding.isLegacyCryptoRequired());
- super.setUp();
- }
-
- @Override
- protected void tearDown() throws Exception {
- KeyEncoding.setSimulateLegacyCrypto(false);
- super.tearDown();
- }
-
-
- public void testSimulatedEnrollment() throws Exception {
- boolean isLegacy = KeyEncoding.isLegacyCryptoRequired();
- // Step 1: Server generates an ephemeral DH key pair, saves the private key, and sends
- // the public key to the client as server_ephemeral_key.
- KeyPair serverEphemeralKeyPair =
- EnrollmentCryptoOps.generateEnrollmentKeyAgreementKeyPair(isLegacy);
- byte[] savedServerPrivateKey =
- KeyEncoding.encodeKeyAgreementPrivateKey(serverEphemeralKeyPair.getPrivate());
- byte[] serverEphemeralKey = KeyEncoding.encodeKeyAgreementPublicKey(
- serverEphemeralKeyPair.getPublic());
-
- // Step 2a: Client generates an ephemeral DH key pair, and completes the DH key exchange
- // to derive the master key.
- KeyPair clientEphemeralKeyPair =
- EnrollmentCryptoOps.generateEnrollmentKeyAgreementKeyPair(isLegacy);
- byte[] clientEphemeralKey = KeyEncoding.encodeKeyAgreementPublicKey(
- clientEphemeralKeyPair.getPublic());
- SecretKey clientMasterKey = EnrollmentCryptoOps.doKeyAgreement(
- clientEphemeralKeyPair.getPrivate(),
- KeyEncoding.parseKeyAgreementPublicKey(serverEphemeralKey));
-
- // Step 2b: Client generates its user key pair, and fills in a GcmDeviceInfo message containing
- // the enrollment request (which includes the user public key).
- KeyPair userKeyPair = isLegacy ? PublicKeyProtoUtil.generateRSA2048KeyPair()
- : PublicKeyProtoUtil.generateEcP256KeyPair();
- GcmDeviceInfo clientInfo = createGcmDeviceInfo(userKeyPair.getPublic(), clientMasterKey);
-
- // Step 2c: Client signcrypts the enrollment request to the server, using a combination of the
- // master key and its user signing key.
- byte[] enrollmentMessage = EnrollmentCryptoOps.encryptEnrollmentMessage(
- clientInfo, clientMasterKey, userKeyPair.getPrivate());
-
-
- // Step 3a: Server receives the client's DH public key and completes the key exchange using
- // the saved DH private key.
- SecretKey serverMasterKey = EnrollmentCryptoOps.doKeyAgreement(
- KeyEncoding.parseKeyAgreementPrivateKey(savedServerPrivateKey, isLegacy),
- KeyEncoding.parseKeyAgreementPublicKey(clientEphemeralKey));
-
- // Step 3b: Server uses the exchanged master key to de-signcrypt the enrollment request
- // (which also provides the user public key in the clear).
- GcmDeviceInfo serverInfo = EnrollmentCryptoOps.decryptEnrollmentMessage(
- enrollmentMessage, serverMasterKey, isLegacy);
-
- // Verify that the server sees the client's original enrollment request
- assertTrue(Arrays.equals(clientInfo.toByteArray(), serverInfo.toByteArray()));
-
- // Confirm that the server can recover a valid user PublicKey from the enrollment
- PublicKey serverUserPublicKey = KeyEncoding.parseUserPublicKey(
- serverInfo.getUserPublicKey().toByteArray());
- assertTrue(serverUserPublicKey.equals(userKeyPair.getPublic()));
- }
-
- public void testSimulatedEnrollmentWithForcedLegacy() throws Exception {
- if (PublicKeyProtoUtil.isLegacyCryptoRequired()) {
- // We already test with legacy in this case
- return;
- }
- KeyEncoding.setSimulateLegacyCrypto(true);
- testSimulatedEnrollment();
- }
-
- private GcmDeviceInfo createGcmDeviceInfo(PublicKey userPublicKey, SecretKey masterKey) {
- // One possible method of generating a key handle:
- GenericPublicKey encodedUserPublicKey = PublicKeyProtoUtil.encodePublicKey(userPublicKey);
- byte[] keyHandle = EnrollmentCryptoOps.sha256(encodedUserPublicKey.toByteArray());
-
- return GcmDeviceInfo.newBuilder()
- .setAndroidDeviceId(DEVICE_ID)
- .setGcmRegistrationId(ByteString.copyFrom(GCM_REGISTRATION_ID))
- .setDeviceMasterKeyHash(
- ByteString.copyFrom(EnrollmentCryptoOps.getMasterKeyHash(masterKey)))
- .setUserPublicKey(ByteString.copyFrom(KeyEncoding.encodeUserPublicKey(userPublicKey)))
- .setDeviceModel(DEVICE_MODEL)
- .setLocale(LOCALE)
- .setKeyHandle(ByteString.copyFrom(keyHandle))
- .setEnrollmentSessionId(ByteString.copyFrom(SESSION_ID))
- .setOauthToken(OAUTH_TOKEN)
- .build();
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/KeyEncodingTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securegcm/KeyEncodingTest.java
deleted file mode 100644
index 7012eae..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/KeyEncodingTest.java
+++ /dev/null
@@ -1,189 +0,0 @@
-// Copyright 2020 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
-//
-// https://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 com.google.security.cryptauth.lib.securemessage.PublicKeyProtoUtil;
-import java.security.Key;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.Provider;
-import java.security.PublicKey;
-import java.security.Security;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Arrays;
-import javax.crypto.interfaces.DHPrivateKey;
-import javax.crypto.interfaces.DHPublicKey;
-import junit.framework.TestCase;
-
-/**
- * Android compatible tests for the {@link KeyEncoding} class.
- */
-public class KeyEncodingTest extends TestCase {
- private static final byte[] RAW_KEY_BYTES = {
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
- 1, 2};
-
- private Boolean isLegacy;
- private KeyPair userKeyPair;
-
- @Override
- protected void setUp() throws Exception {
- installSunEcSecurityProviderIfNecessary();
- isLegacy = PublicKeyProtoUtil.isLegacyCryptoRequired();
- setUserKeyPair();
- super.setUp();
- }
-
- @Override
- protected void tearDown() throws Exception {
- KeyEncoding.setSimulateLegacyCrypto(false);
- isLegacy = PublicKeyProtoUtil.isLegacyCryptoRequired();
- super.tearDown();
- }
-
- private void setUserKeyPair() {
- userKeyPair = isLegacy ? PublicKeyProtoUtil.generateRSA2048KeyPair()
- : PublicKeyProtoUtil.generateEcP256KeyPair();
- }
-
- public void testSimulateLegacyCrypto() {
- if (isLegacy) {
- return; // Nothing to test if we are already stuck in a legacy platform
- }
- assertFalse(KeyEncoding.isLegacyCryptoRequired());
- KeyEncoding.setSimulateLegacyCrypto(true);
- assertTrue(KeyEncoding.isLegacyCryptoRequired());
- }
-
- public void testMasterKeyEncoding() {
- // Require that master keys are encoded/decoded as raw byte arrays
- assertTrue(Arrays.equals(
- RAW_KEY_BYTES,
- KeyEncoding.encodeMasterKey(KeyEncoding.parseMasterKey(RAW_KEY_BYTES))));
- }
-
- public void testUserPublicKeyEncoding() throws InvalidKeySpecException {
- PublicKey pk = userKeyPair.getPublic();
- byte[] encodedPk = KeyEncoding.encodeUserPublicKey(pk);
- PublicKey decodedPk = KeyEncoding.parseUserPublicKey(encodedPk);
- assertKeysEqual(pk, decodedPk);
- }
-
- public void testUserPrivateKeyEncoding() throws InvalidKeySpecException {
- PrivateKey sk = userKeyPair.getPrivate();
- byte[] encodedSk = KeyEncoding.encodeUserPrivateKey(sk);
- PrivateKey decodedSk = KeyEncoding.parseUserPrivateKey(encodedSk, isLegacy);
- assertKeysEqual(sk, decodedSk);
- }
-
- public void testKeyAgreementPublicKeyEncoding() throws InvalidKeySpecException {
- KeyPair clientKeyPair = EnrollmentCryptoOps.generateEnrollmentKeyAgreementKeyPair(isLegacy);
- PublicKey pk = clientKeyPair.getPublic();
- byte[] encodedPk = KeyEncoding.encodeKeyAgreementPublicKey(pk);
- PublicKey decodedPk = KeyEncoding.parseKeyAgreementPublicKey(encodedPk);
- assertKeysEqual(pk, decodedPk);
- }
-
- public void testKeyAgreementPrivateKeyEncoding() throws InvalidKeySpecException {
- KeyPair clientKeyPair = EnrollmentCryptoOps.generateEnrollmentKeyAgreementKeyPair(isLegacy);
- PrivateKey sk = clientKeyPair.getPrivate();
- byte[] encodedSk = KeyEncoding.encodeKeyAgreementPrivateKey(sk);
- PrivateKey decodedSk = KeyEncoding.parseKeyAgreementPrivateKey(encodedSk, isLegacy);
- assertKeysEqual(sk, decodedSk);
- }
-
- public void testEncodingsWithForcedLegacy() throws InvalidKeySpecException {
- if (PublicKeyProtoUtil.isLegacyCryptoRequired()) {
- // We already test with legacy in this case
- return;
- }
- KeyEncoding.setSimulateLegacyCrypto(true);
- isLegacy = true;
- setUserKeyPair();
- testUserPublicKeyEncoding();
- testUserPrivateKeyEncoding();
- testKeyAgreementPublicKeyEncoding();
- testKeyAgreementPrivateKeyEncoding();
- }
-
- public void testSigningPublicKeyEncoding() throws InvalidKeySpecException {
- KeyPair keyPair = PublicKeyProtoUtil.generateEcP256KeyPair();
- PublicKey pk = keyPair.getPublic();
- byte[] encodedPk = KeyEncoding.encodeSigningPublicKey(pk);
- PublicKey decodedPk = KeyEncoding.parseSigningPublicKey(encodedPk);
- assertKeysEqual(pk, decodedPk);
- }
-
- public void testSigningPrivateKeyEncoding() throws InvalidKeySpecException {
- KeyPair keyPair = PublicKeyProtoUtil.generateEcP256KeyPair();
- PrivateKey sk = keyPair.getPrivate();
- byte[] encodedSk = KeyEncoding.encodeSigningPrivateKey(sk);
- PrivateKey decodedSk = KeyEncoding.parseSigningPrivateKey(encodedSk);
- assertKeysEqual(sk, decodedSk);
- }
-
- public void testDeviceSyncPublicKeyEncoding() throws InvalidKeySpecException {
- KeyPair keyPair = PublicKeyProtoUtil.generateEcP256KeyPair();
- PublicKey pk = keyPair.getPublic();
- byte[] encodedPk = KeyEncoding.encodeDeviceSyncGroupPublicKey(pk);
- PublicKey decodedPk = KeyEncoding.parseDeviceSyncGroupPublicKey(encodedPk);
- assertKeysEqual(pk, decodedPk);
- }
-
- void assertKeysEqual(Key a, Key b) {
- if ((a instanceof ECPublicKey)
- || (a instanceof ECPrivateKey)
- || (a instanceof RSAPublicKey)
- || (a instanceof RSAPrivateKey)) {
- assertNotNull(a.getEncoded());
- assertTrue(Arrays.equals(a.getEncoded(), b.getEncoded()));
- }
- if (a instanceof DHPublicKey) {
- DHPublicKey ya = (DHPublicKey) a;
- DHPublicKey yb = (DHPublicKey) b;
- assertEquals(ya.getY(), yb.getY());
- assertEquals(ya.getParams().getG(), yb.getParams().getG());
- assertEquals(ya.getParams().getP(), yb.getParams().getP());
- }
- if (a instanceof DHPrivateKey) {
- DHPrivateKey xa = (DHPrivateKey) a;
- DHPrivateKey xb = (DHPrivateKey) b;
- assertEquals(xa.getX(), xb.getX());
- assertEquals(xa.getParams().getG(), xb.getParams().getG());
- assertEquals(xa.getParams().getP(), xb.getParams().getP());
- }
- }
-
- /**
- * Registers the SunEC security provider if no EC security providers are currently registered.
- */
- // TODO(shabsi): Remove this method when b/7891565 is fixed
- static void installSunEcSecurityProviderIfNecessary() {
- if (Security.getProviders("KeyPairGenerator.EC") == null) {
- try {
- Class<?> providerClass = Class.forName("sun.security.ec.SunEC");
- Security.addProvider((Provider) providerClass.newInstance());
- } catch (Exception e) {
- // SunEC is not available, nothing we can do
- }
- }
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/TransportCryptoOpsTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securegcm/TransportCryptoOpsTest.java
deleted file mode 100644
index 9e45c0a..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/TransportCryptoOpsTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2020 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
-//
-// https://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 com.google.security.cryptauth.lib.securegcm.SecureGcmProto.Tickle;
-import com.google.security.cryptauth.lib.securegcm.TransportCryptoOps.Payload;
-import com.google.security.cryptauth.lib.securegcm.TransportCryptoOps.PayloadType;
-import com.google.security.cryptauth.lib.securemessage.PublicKeyProtoUtil;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.util.Arrays;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-import junit.framework.TestCase;
-
-/**
- * Android compatible tests for the {@link TransportCryptoOps} class.
- */
-public class TransportCryptoOpsTest extends TestCase {
- private static final byte[] KEY_BYTES = {
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
- 1, 2
- };
- private static final byte[] KEY_HANDLE = { 9 };
-
- private SecretKey masterKey;
-
- @Override
- protected void setUp() throws Exception {
- KeyEncodingTest.installSunEcSecurityProviderIfNecessary();
- masterKey = new SecretKeySpec(KEY_BYTES, "AES");
- super.setUp();
- }
-
- public void testServerMessage() throws Exception {
- long tickleExpiry = 12345L;
- Tickle tickle = Tickle.newBuilder()
- .setExpiryTime(tickleExpiry)
- .build();
-
- // Simulate sending a message
- byte[] signcryptedMessage = TransportCryptoOps.signcryptServerMessage(
- new Payload(PayloadType.TICKLE, tickle.toByteArray()),
- masterKey,
- KEY_HANDLE);
-
- // Simulate the process of receiving the message
- assertTrue(Arrays.equals(KEY_HANDLE, TransportCryptoOps.getKeyHandleFor(signcryptedMessage)));
- Payload received = TransportCryptoOps.verifydecryptServerMessage(signcryptedMessage, masterKey);
- assertEquals(PayloadType.TICKLE, received.getPayloadType());
- Tickle receivedTickle = Tickle.parseFrom(received.getMessage());
- assertEquals(tickleExpiry, receivedTickle.getExpiryTime());
- }
-
- public void testClientMessage() throws Exception {
- if (PublicKeyProtoUtil.isLegacyCryptoRequired()) {
- return; // This test isn't for legacy crypto
- }
- KeyPair userKeyPair = PublicKeyProtoUtil.generateEcP256KeyPair();
- doTestClientMessageWith(userKeyPair);
- }
-
- public void testClientMessageWithLegacyCrypto() throws Exception {
- KeyPair userKeyPair = PublicKeyProtoUtil.generateRSA2048KeyPair();
- doTestClientMessageWith(userKeyPair);
- }
-
- private void doTestClientMessageWith(KeyPair userKeyPair) throws Exception {
- PublicKey userPublicKey = userKeyPair.getPublic();
- // Will use a Tickle for the test message, even though that would normally
- // only be sent from the server to the client
- long tickleExpiry = 12345L;
- Tickle tickle = Tickle.newBuilder()
- .setExpiryTime(tickleExpiry)
- .build();
-
- // Simulate sending a message
- byte[] signcryptedMessage = TransportCryptoOps.signcryptClientMessage(
- new Payload(PayloadType.TICKLE, tickle.toByteArray()),
- userKeyPair,
- masterKey);
-
- // Simulate the process of receiving the message
- byte[] encodedUserPublicKey = TransportCryptoOps.getEncodedUserPublicKeyFor(signcryptedMessage);
- assertTrue(Arrays.equals(KeyEncoding.encodeUserPublicKey(userPublicKey), encodedUserPublicKey));
- userPublicKey = KeyEncoding.parseUserPublicKey(encodedUserPublicKey);
- // At this point the server would have looked up the masterKey for this userPublicKey
-
- Payload received = TransportCryptoOps.verifydecryptClientMessage(
- signcryptedMessage, userPublicKey, masterKey);
-
- assertEquals(PayloadType.TICKLE, received.getPayloadType());
- Tickle receivedTickle = Tickle.parseFrom(received.getMessage());
- assertEquals(tickleExpiry, receivedTickle.getExpiryTime());
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2CppCompatibilityTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2CppCompatibilityTest.java
deleted file mode 100644
index db319e0..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2CppCompatibilityTest.java
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2020 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
-//
-// https://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 com.google.security.cryptauth.lib.securegcm.Ukey2Handshake.HandshakeCipher;
-import com.google.security.cryptauth.lib.securegcm.Ukey2ShellCppWrapper.Mode;
-import java.util.Arrays;
-import junit.framework.TestCase;
-
-/**
- * Tests the compatibility between the Java and C++ implementations of the UKEY2 protocol. This
- * integration test executes and talks to a compiled binary exposing the C++ implementation (wrapped
- * by {@link Ukey2ShellCppWrapper}).
- *
- * <p>The C++ implementation is located in //security/cryptauth/lib/securegcm.
- */
-public class Ukey2CppCompatibilityTest extends TestCase {
- private static final int VERIFICATION_STRING_LENGTH = 32;
-
- private static final byte[] sPayload1 = "payload to encrypt1".getBytes();
- private static final byte[] sPayload2 = "payload to encrypt2".getBytes();
-
- /** Tests full handshake with C++ client and Java server. */
- public void testCppClientJavaServer() throws Exception {
- Ukey2ShellCppWrapper cppUkey2Shell =
- new Ukey2ShellCppWrapper(Mode.INITIATOR, VERIFICATION_STRING_LENGTH);
- cppUkey2Shell.startShell();
- Ukey2Handshake javaUkey2Handshake = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
-
- // ClientInit:
- byte[] clientInit = cppUkey2Shell.readHandshakeMessage();
- javaUkey2Handshake.parseHandshakeMessage(clientInit);
-
- // ServerInit:
- byte[] serverInit = javaUkey2Handshake.getNextHandshakeMessage();
- cppUkey2Shell.writeHandshakeMessage(serverInit);
-
- // ClientFinished:
- byte[] clientFinished = cppUkey2Shell.readHandshakeMessage();
- javaUkey2Handshake.parseHandshakeMessage(clientFinished);
-
- // Verification String:
- cppUkey2Shell.confirmAuthString(
- javaUkey2Handshake.getVerificationString(VERIFICATION_STRING_LENGTH));
- javaUkey2Handshake.verifyHandshake();
-
- // Secure channel:
- D2DConnectionContext javaSecureContext = javaUkey2Handshake.toConnectionContext();
-
- // ukey2_shell encodes data:
- byte[] encodedData = cppUkey2Shell.sendEncryptCommand(sPayload1);
- byte[] decodedData = javaSecureContext.decodeMessageFromPeer(encodedData);
- assertTrue(Arrays.equals(sPayload1, decodedData));
-
- // ukey2_shell decodes data:
- encodedData = javaSecureContext.encodeMessageToPeer(sPayload2);
- decodedData = cppUkey2Shell.sendDecryptCommand(encodedData);
- assertTrue(Arrays.equals(sPayload2, decodedData));
-
- // ukey2_shell session unique:
- byte[] localSessionUnique = javaSecureContext.getSessionUnique();
- byte[] remoteSessionUnique = cppUkey2Shell.sendSessionUniqueCommand();
- assertTrue(Arrays.equals(localSessionUnique, remoteSessionUnique));
-
- cppUkey2Shell.stopShell();
- }
-
- /** Tests full handshake with C++ server and Java client. */
- public void testCppServerJavaClient() throws Exception {
- Ukey2ShellCppWrapper cppUkey2Shell =
- new Ukey2ShellCppWrapper(Mode.RESPONDER, VERIFICATION_STRING_LENGTH);
- cppUkey2Shell.startShell();
- Ukey2Handshake javaUkey2Handshake = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
-
- // ClientInit:
- byte[] clientInit = javaUkey2Handshake.getNextHandshakeMessage();
- cppUkey2Shell.writeHandshakeMessage(clientInit);
-
- // ServerInit:
- byte[] serverInit = cppUkey2Shell.readHandshakeMessage();
- javaUkey2Handshake.parseHandshakeMessage(serverInit);
-
- // ClientFinished:
- byte[] clientFinished = javaUkey2Handshake.getNextHandshakeMessage();
- cppUkey2Shell.writeHandshakeMessage(clientFinished);
-
- // Verification String:
- cppUkey2Shell.confirmAuthString(
- javaUkey2Handshake.getVerificationString(VERIFICATION_STRING_LENGTH));
- javaUkey2Handshake.verifyHandshake();
-
- // Secure channel:
- D2DConnectionContext javaSecureContext = javaUkey2Handshake.toConnectionContext();
-
- // ukey2_shell encodes data:
- byte[] encodedData = cppUkey2Shell.sendEncryptCommand(sPayload1);
- byte[] decodedData = javaSecureContext.decodeMessageFromPeer(encodedData);
- assertTrue(Arrays.equals(sPayload1, decodedData));
-
- // ukey2_shell decodes data:
- encodedData = javaSecureContext.encodeMessageToPeer(sPayload2);
- decodedData = cppUkey2Shell.sendDecryptCommand(encodedData);
- assertTrue(Arrays.equals(sPayload2, decodedData));
-
- // ukey2_shell session unique:
- byte[] localSessionUnique = javaSecureContext.getSessionUnique();
- byte[] remoteSessionUnique = cppUkey2Shell.sendSessionUniqueCommand();
- assertTrue(Arrays.equals(localSessionUnique, remoteSessionUnique));
-
- cppUkey2Shell.stopShell();
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2HandshakeTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2HandshakeTest.java
deleted file mode 100644
index f5d0e1a..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2HandshakeTest.java
+++ /dev/null
@@ -1,818 +0,0 @@
-// Copyright 2020 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
-//
-// https://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 com.google.protobuf.ByteString;
-import com.google.security.cryptauth.lib.securegcm.Ukey2Handshake.AlertException;
-import com.google.security.cryptauth.lib.securegcm.Ukey2Handshake.HandshakeCipher;
-import com.google.security.cryptauth.lib.securegcm.Ukey2Handshake.State;
-import com.google.security.cryptauth.lib.securegcm.UkeyProto.Ukey2ClientFinished;
-import com.google.security.cryptauth.lib.securegcm.UkeyProto.Ukey2ClientInit;
-import com.google.security.cryptauth.lib.securegcm.UkeyProto.Ukey2ClientInit.CipherCommitment;
-import com.google.security.cryptauth.lib.securegcm.UkeyProto.Ukey2Message;
-import com.google.security.cryptauth.lib.securegcm.UkeyProto.Ukey2ServerInit;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-import junit.framework.TestCase;
-import org.junit.Assert;
-
-/**
- * Android compatible tests for the {@link Ukey2Handshake} class.
- */
-public class Ukey2HandshakeTest extends TestCase {
-
- private static final int MAX_AUTH_STRING_LENGTH = 32;
-
- @Override
- protected void setUp() throws Exception {
- KeyEncodingTest.installSunEcSecurityProviderIfNecessary();
- super.setUp();
- }
-
- /**
- * Tests correct use
- */
- public void testHandshake() throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage;
-
- assertEquals(State.IN_PROGRESS, client.getHandshakeState());
- assertEquals(State.IN_PROGRESS, server.getHandshakeState());
-
- // Message 1 (Client Init)
- handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- assertEquals(State.IN_PROGRESS, client.getHandshakeState());
- assertEquals(State.IN_PROGRESS, server.getHandshakeState());
-
- // Message 2 (Server Init)
- handshakeMessage = server.getNextHandshakeMessage();
- client.parseHandshakeMessage(handshakeMessage);
- assertEquals(State.IN_PROGRESS, client.getHandshakeState());
- assertEquals(State.IN_PROGRESS, server.getHandshakeState());
-
- // Message 3 (Client Finish)
- handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- assertEquals(State.VERIFICATION_NEEDED, client.getHandshakeState());
- assertEquals(State.VERIFICATION_NEEDED, server.getHandshakeState());
-
- // Get the auth string
- byte[] clientAuthString = client.getVerificationString(MAX_AUTH_STRING_LENGTH);
- byte[] serverAuthString = server.getVerificationString(MAX_AUTH_STRING_LENGTH);
- Assert.assertArrayEquals(clientAuthString, serverAuthString);
- assertEquals(State.VERIFICATION_IN_PROGRESS, client.getHandshakeState());
- assertEquals(State.VERIFICATION_IN_PROGRESS, server.getHandshakeState());
-
- // Verify the auth string
- client.verifyHandshake();
- server.verifyHandshake();
- assertEquals(State.FINISHED, client.getHandshakeState());
- assertEquals(State.FINISHED, server.getHandshakeState());
-
- // Make a context
- D2DConnectionContext clientContext = client.toConnectionContext();
- D2DConnectionContext serverContext = server.toConnectionContext();
- assertContextsCompatible(clientContext, serverContext);
- assertEquals(State.ALREADY_USED, client.getHandshakeState());
- assertEquals(State.ALREADY_USED, server.getHandshakeState());
- }
-
- /**
- * Verify enums for ciphers match the proto values
- */
- public void testCipherEnumValuesCorrect() {
- assertEquals(
- "You added a cipher, but forgot to change the test", 1, HandshakeCipher.values().length);
-
- assertEquals(UkeyProto.Ukey2HandshakeCipher.P256_SHA512,
- HandshakeCipher.P256_SHA512.getValue());
- }
-
- /**
- * Tests incorrect use by callers (client and servers accidentally sending the wrong message at
- * the wrong time)
- */
- public void testHandshakeClientAndServerSendRepeatedOutOfOrderMessages() throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // Client sends ClientInit (again) instead of ClientFinished
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- server.getNextHandshakeMessage(); // do this to avoid illegal state
- try {
- server.parseHandshakeMessage(handshakeMessage);
- fail("Expected Alert for client sending ClientInit twice");
- } catch (HandshakeException e) {
- // success
- }
- assertEquals(State.ERROR, server.getHandshakeState());
-
- // Server sends ClientInit back to client instead of ServerInit
- client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- handshakeMessage = client.getNextHandshakeMessage();
- try {
- client.parseHandshakeMessage(handshakeMessage);
- fail("Expected Alert for server sending ClientInit back to client");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, client.getHandshakeState());
-
- // Clients sends ServerInit back to client instead of ClientFinished
- client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
- try {
- server.parseHandshakeMessage(handshakeMessage);
- fail("Expected Alert for client sending ServerInit back to server");
- } catch (HandshakeException e) {
- // success
- }
- assertEquals(State.ERROR, server.getHandshakeState());
- }
-
- /**
- * Tests that verification codes are different for different handshake runs. Also tests a full
- * man-in-the-middle attack.
- */
- public void testVerificationCodeUniqueToSession() throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // Client 1 and Server 1
- Ukey2Handshake client1 = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server1 = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client1.getNextHandshakeMessage();
- server1.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server1.getNextHandshakeMessage();
- client1.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = client1.getNextHandshakeMessage();
- server1.parseHandshakeMessage(handshakeMessage);
- byte[] client1AuthString = client1.getVerificationString(MAX_AUTH_STRING_LENGTH);
- byte[] server1AuthString = server1.getVerificationString(MAX_AUTH_STRING_LENGTH);
- Assert.assertArrayEquals(client1AuthString, server1AuthString);
-
- // Client 2 and Server 2
- Ukey2Handshake client2 = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server2 = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- handshakeMessage = client2.getNextHandshakeMessage();
- server2.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server2.getNextHandshakeMessage();
- client2.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = client2.getNextHandshakeMessage();
- server2.parseHandshakeMessage(handshakeMessage);
- byte[] client2AuthString = client2.getVerificationString(MAX_AUTH_STRING_LENGTH);
- byte[] server2AuthString = server2.getVerificationString(MAX_AUTH_STRING_LENGTH);
- Assert.assertArrayEquals(client2AuthString, server2AuthString);
-
- // Make sure the verification strings differ
- assertFalse(Arrays.equals(client1AuthString, client2AuthString));
- }
-
- /**
- * Test an attack where the adversary swaps out the public key in the final message (i.e.,
- * commitment doesn't match public key)
- */
- public void testPublicKeyDoesntMatchCommitment() throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // Run handshake as usual, but stop before sending client finished
- Ukey2Handshake client1 = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server1 = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client1.getNextHandshakeMessage();
- server1.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server1.getNextHandshakeMessage();
-
- // Run another handshake and get the final client finished
- Ukey2Handshake client2 = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server2 = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- handshakeMessage = client2.getNextHandshakeMessage();
- server2.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server2.getNextHandshakeMessage();
- client2.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = client2.getNextHandshakeMessage();
-
- // Now use the client finished from second handshake in first handshake (simulates where an
- // attacker switches out the last message).
- try {
- server1.parseHandshakeMessage(handshakeMessage);
- fail("Expected server to catch mismatched ClientFinished");
- } catch (HandshakeException e) {
- // success
- }
- assertEquals(State.ERROR, server1.getHandshakeState());
-
- // Make sure caller can't actually do anything with the server now that an error has occurred
- try {
- server1.getVerificationString(MAX_AUTH_STRING_LENGTH);
- fail("Server allows operations post error");
- } catch (IllegalStateException e) {
- // success
- }
- try {
- server1.verifyHandshake();
- fail("Server allows operations post error");
- } catch (IllegalStateException e) {
- // success
- }
- try {
- server1.toConnectionContext();
- fail("Server allows operations post error");
- } catch (IllegalStateException e) {
- // success
- }
- }
-
- /**
- * Test commitment having unsupported version
- */
- public void testClientInitUnsupportedVersion() throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // Get ClientInit and modify the version to be too big
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
-
- Ukey2Message.Builder message = Ukey2Message.newBuilder(
- Ukey2Message.parseFrom(handshakeMessage));
- Ukey2ClientInit.Builder clientInit =
- Ukey2ClientInit.newBuilder(Ukey2ClientInit.parseFrom(message.getMessageData()));
- clientInit.setVersion(Ukey2Handshake.VERSION + 1);
- message.setMessageData(ByteString.copyFrom(clientInit.build().toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- server.parseHandshakeMessage(handshakeMessage);
- fail("Server did not catch unsupported version (too big) in ClientInit");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, server.getHandshakeState());
-
- // Get ClientInit and modify the version to be too big
- client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- handshakeMessage = client.getNextHandshakeMessage();
-
- message = Ukey2Message.newBuilder(
- Ukey2Message.parseFrom(handshakeMessage));
- clientInit = Ukey2ClientInit.newBuilder(Ukey2ClientInit.parseFrom(message.getMessageData()));
- clientInit.setVersion(0 /* minimum version is 1 */);
- message.setMessageData(ByteString.copyFrom(clientInit.build().toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- server.parseHandshakeMessage(handshakeMessage);
- fail("Server did not catch unsupported version (too small) in ClientInit");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, server.getHandshakeState());
- }
-
- /**
- * Tests that server catches wrong number of random bytes in ClientInit
- */
- public void testWrongNonceLengthInClientInit() throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // Get ClientInit and modify the nonce
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
-
- Ukey2Message.Builder message = Ukey2Message.newBuilder(
- Ukey2Message.parseFrom(handshakeMessage));
- Ukey2ClientInit.Builder clientInit =
- Ukey2ClientInit.newBuilder(Ukey2ClientInit.parseFrom(message.getMessageData()));
- clientInit.setRandom(
- ByteString.copyFrom(
- Arrays.copyOf(
- clientInit.getRandom().toByteArray(),
- 31 /* as per go/ukey2, nonces must be 32 bytes long */)));
- message.setMessageData(ByteString.copyFrom(clientInit.build().toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- server.parseHandshakeMessage(handshakeMessage);
- fail("Server did not catch nonce being too short in ClientInit");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, server.getHandshakeState());
- }
-
- /**
- * Test that server catches missing commitment in ClientInit message
- */
- public void testServerCatchesMissingCommitmentInClientInit() throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // Get ClientInit and modify the commitment
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
-
- Ukey2Message.Builder message = Ukey2Message.newBuilder(
- Ukey2Message.parseFrom(handshakeMessage));
- Ukey2ClientInit clientInit =
- Ukey2ClientInit.newBuilder(Ukey2ClientInit.parseFrom(message.getMessageData()))
- .build();
- Ukey2ClientInit.Builder badClientInit = Ukey2ClientInit.newBuilder()
- .setVersion(clientInit.getVersion())
- .setRandom(clientInit.getRandom());
- for (CipherCommitment commitment : clientInit.getCipherCommitmentsList()) {
- badClientInit.addCipherCommitments(commitment);
- }
-
- message.setMessageData(ByteString.copyFrom(badClientInit.build().toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- server.parseHandshakeMessage(handshakeMessage);
- fail("Server did not catch missing commitment in ClientInit");
- } catch (AlertException e) {
- // success
- }
- }
-
- /**
- * Test that client catches invalid version in ServerInit
- */
- public void testServerInitUnsupportedVersion() throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // Get ServerInit and modify the version to be too big
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
-
- Ukey2Message.Builder message = Ukey2Message.newBuilder(
- Ukey2Message.parseFrom(handshakeMessage));
- Ukey2ServerInit serverInit =
- Ukey2ServerInit.newBuilder(Ukey2ServerInit.parseFrom(message.getMessageData()))
- .setVersion(Ukey2Handshake.VERSION + 1)
- .build();
- message.setMessageData(ByteString.copyFrom(serverInit.toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- client.parseHandshakeMessage(handshakeMessage);
- fail("Client did not catch unsupported version (too big) in ServerInit");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, client.getHandshakeState());
-
- // Get ServerInit and modify the version to be too big
- client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
-
- message = Ukey2Message.newBuilder(Ukey2Message.parseFrom(handshakeMessage));
- serverInit =
- Ukey2ServerInit.newBuilder(Ukey2ServerInit.parseFrom(message.getMessageData()))
- .setVersion(0 /* minimum version is 1 */)
- .build();
- message.setMessageData(ByteString.copyFrom(serverInit.toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- client.parseHandshakeMessage(handshakeMessage);
- fail("Client did not catch unsupported version (too small) in ServerInit");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, client.getHandshakeState());
- }
-
- /**
- * Tests that client catches wrong number of random bytes in ServerInit
- */
- public void testWrongNonceLengthInServerInit() throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // Get ServerInit and modify the nonce
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
-
- Ukey2Message.Builder message = Ukey2Message.newBuilder(
- Ukey2Message.parseFrom(handshakeMessage));
- Ukey2ServerInit.Builder serverInitBuilder =
- Ukey2ServerInit.newBuilder(Ukey2ServerInit.parseFrom(message.getMessageData()));
- Ukey2ServerInit serverInit = serverInitBuilder.setRandom(ByteString.copyFrom(Arrays.copyOf(
- serverInitBuilder.getRandom().toByteArray(),
- 31 /* as per go/ukey2, nonces must be 32 bytes long */)))
- .build();
- message.setMessageData(ByteString.copyFrom(serverInit.toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- client.parseHandshakeMessage(handshakeMessage);
- fail("Client did not catch nonce being too short in ServerInit");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, client.getHandshakeState());
- }
-
- /**
- * Test that client catches missing or incorrect handshake cipher in serverInit
- */
- public void testMissingOrIncorrectHandshakeCipherInServerInit() throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // Get ServerInit
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
- Ukey2Message.Builder message = Ukey2Message.newBuilder(
- Ukey2Message.parseFrom(handshakeMessage));
- Ukey2ServerInit serverInit = Ukey2ServerInit.parseFrom(message.getMessageData());
-
- // remove handshake cipher
- Ukey2ServerInit badServerInit = Ukey2ServerInit.newBuilder()
- .setPublicKey(serverInit.getPublicKey())
- .setRandom(serverInit.getRandom())
- .setVersion(serverInit.getVersion())
- .build();
-
- message.setMessageData(ByteString.copyFrom(badServerInit.toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- client.parseHandshakeMessage(handshakeMessage);
- fail("Client did not catch missing handshake cipher in ServerInit");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, client.getHandshakeState());
-
- // Get ServerInit
- client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
- message = Ukey2Message.newBuilder(Ukey2Message.parseFrom(handshakeMessage));
- serverInit = Ukey2ServerInit.parseFrom(message.getMessageData());
-
- // put in a bad handshake cipher
- badServerInit = Ukey2ServerInit.newBuilder()
- .setPublicKey(serverInit.getPublicKey())
- .setRandom(serverInit.getRandom())
- .setVersion(serverInit.getVersion())
- .setHandshakeCipher(UkeyProto.Ukey2HandshakeCipher.RESERVED)
- .build();
-
- message.setMessageData(ByteString.copyFrom(badServerInit.toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- client.parseHandshakeMessage(handshakeMessage);
- fail("Client did not catch bad handshake cipher in ServerInit");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, client.getHandshakeState());
- }
-
- /**
- * Test that client catches missing or incorrect public key in serverInit
- */
- public void testMissingOrIncorrectPublicKeyInServerInit() throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // Get ServerInit
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
- Ukey2Message.Builder message = Ukey2Message.newBuilder(
- Ukey2Message.parseFrom(handshakeMessage));
- Ukey2ServerInit serverInit = Ukey2ServerInit.parseFrom(message.getMessageData());
-
- // remove public key
- Ukey2ServerInit badServerInit = Ukey2ServerInit.newBuilder()
- .setRandom(serverInit.getRandom())
- .setVersion(serverInit.getVersion())
- .setHandshakeCipher(serverInit.getHandshakeCipher())
- .build();
-
- message.setMessageData(ByteString.copyFrom(badServerInit.toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- client.parseHandshakeMessage(handshakeMessage);
- fail("Client did not catch missing public key in ServerInit");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, client.getHandshakeState());
-
- // Get ServerInit
- client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
- message = Ukey2Message.newBuilder(
- Ukey2Message.parseFrom(handshakeMessage));
- serverInit = Ukey2ServerInit.parseFrom(message.getMessageData());
-
- // put in a bad public key
- badServerInit = Ukey2ServerInit.newBuilder()
- .setPublicKey(ByteString.copyFrom(new byte[] {42, 12, 1}))
- .setRandom(serverInit.getRandom())
- .setVersion(serverInit.getVersion())
- .setHandshakeCipher(serverInit.getHandshakeCipher())
- .build();
-
- message.setMessageData(ByteString.copyFrom(badServerInit.toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- client.parseHandshakeMessage(handshakeMessage);
- fail("Client did not catch bad public key in ServerInit");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, client.getHandshakeState());
- }
-
- /**
- * Test that client catches missing or incorrect public key in clientFinished
- */
- public void testMissingOrIncorrectPublicKeyInClientFinished() throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // Get ClientFinished
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
- client.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = client.getNextHandshakeMessage();
- Ukey2Message.Builder message = Ukey2Message.newBuilder(
- Ukey2Message.parseFrom(handshakeMessage));
-
- // remove public key
- Ukey2ClientFinished.Builder badClientFinished = Ukey2ClientFinished.newBuilder();
-
- message.setMessageData(ByteString.copyFrom(badClientFinished.build().toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- server.parseHandshakeMessage(handshakeMessage);
- fail("Server did not catch missing public key in ClientFinished");
- } catch (HandshakeException e) {
- // success
- }
- assertEquals(State.ERROR, server.getHandshakeState());
-
- // Get ClientFinished
- client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
- client.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = client.getNextHandshakeMessage();
- message = Ukey2Message.newBuilder(Ukey2Message.parseFrom(handshakeMessage));
-
- // remove public key
- badClientFinished = Ukey2ClientFinished.newBuilder()
- .setPublicKey(ByteString.copyFrom(new byte[] {42, 12, 1}));
-
- message.setMessageData(ByteString.copyFrom(badClientFinished.build().toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- server.parseHandshakeMessage(handshakeMessage);
- fail("Server did not catch bad public key in ClientFinished");
- } catch (HandshakeException e) {
- // success
- }
- assertEquals(State.ERROR, server.getHandshakeState());
- }
-
- /**
- * Tests that items (nonces, commitments, public keys) that should be random are at least
- * different on every run.
- */
- public void testRandomItemsDifferentOnEveryRun() throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- int numberOfRuns = 50;
-
- // Search for collisions
- Set<Integer> commitments = new HashSet<>(numberOfRuns);
- Set<Integer> clientNonces = new HashSet<>(numberOfRuns);
- Set<Integer> serverNonces = new HashSet<>(numberOfRuns);
- Set<Integer> serverPublicKeys = new HashSet<>(numberOfRuns);
- Set<Integer> clientPublicKeys = new HashSet<>(numberOfRuns);
-
- for (int i = 0; i < numberOfRuns; i++) {
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
- Ukey2Message message = Ukey2Message.parseFrom(handshakeMessage);
- Ukey2ClientInit clientInit = Ukey2ClientInit.parseFrom(message.getMessageData());
-
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
- message = Ukey2Message.parseFrom(handshakeMessage);
- Ukey2ServerInit serverInit = Ukey2ServerInit.parseFrom(message.getMessageData());
-
- client.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = client.getNextHandshakeMessage();
- message = Ukey2Message.parseFrom(handshakeMessage);
- Ukey2ClientFinished clientFinished = Ukey2ClientFinished.parseFrom(message.getMessageData());
-
- // Clean up to save some memory (b/32054837)
- client = null;
- server = null;
- handshakeMessage = null;
- message = null;
- System.gc();
-
- // ClientInit randomness
- Integer nonceHash = Integer.valueOf(Arrays.hashCode(clientInit.getRandom().toByteArray()));
- if (clientNonces.contains(nonceHash) || serverNonces.contains(nonceHash)) {
- fail("Nonce in ClientINit has repeated!");
- }
- clientNonces.add(nonceHash);
-
- Integer commitmentHash = 0;
- for (CipherCommitment commitement : clientInit.getCipherCommitmentsList()) {
- commitmentHash += Arrays.hashCode(commitement.toByteArray());
- }
- if (commitments.contains(nonceHash)) {
- fail("Commitment has repeated!");
- }
- commitments.add(commitmentHash);
-
- // ServerInit randomness
- nonceHash = Integer.valueOf(Arrays.hashCode(serverInit.getRandom().toByteArray()));
- if (serverNonces.contains(nonceHash) || clientNonces.contains(nonceHash)) {
- fail("Nonce in ServerInit repeated!");
- }
- serverNonces.add(nonceHash);
-
- Integer publicKeyHash =
- Integer.valueOf(Arrays.hashCode(serverInit.getPublicKey().toByteArray()));
- if (serverPublicKeys.contains(publicKeyHash) || clientPublicKeys.contains(publicKeyHash)) {
- fail("Public Key in ServerInit repeated!");
- }
- serverPublicKeys.add(publicKeyHash);
-
- // Client Finished randomness
- publicKeyHash = Integer.valueOf(Arrays.hashCode(clientFinished.getPublicKey().toByteArray()));
- if (serverPublicKeys.contains(publicKeyHash) || clientPublicKeys.contains(publicKeyHash)) {
- fail("Public Key in ClientFinished repeated!");
- }
- clientPublicKeys.add(publicKeyHash);
- }
- }
-
- /**
- * Tests that {@link Ukey2Handshake#getVerificationString(int)} enforces sane verification string
- * lengths.
- */
- public void testGetVerificationEnforcesSaneLengths() throws Exception {
- if (KeyEncoding.isLegacyCryptoRequired()) {
- // this means we're running on an old SDK, which doesn't support the
- // necessary crypto. Let's not test anything in this case.
- return;
- }
-
- // Run the protocol
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
- client.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
-
- // Try to get too short verification string
- try {
- client.getVerificationString(0);
- fail("Too short verification string allowed");
- } catch (IllegalArgumentException e) {
- // success
- }
-
- // Try to get too long verification string
- try {
- server.getVerificationString(MAX_AUTH_STRING_LENGTH + 1);
- fail("Too long verification string allowed");
- } catch (IllegalArgumentException e) {
- // success
- }
- }
-
- /**
- * Asserts that the given client and server contexts are compatible
- */
- private void assertContextsCompatible(
- D2DConnectionContext clientContext, D2DConnectionContext serverContext) {
- assertNotNull(clientContext);
- assertNotNull(serverContext);
- assertEquals(D2DConnectionContextV1.PROTOCOL_VERSION, clientContext.getProtocolVersion());
- assertEquals(D2DConnectionContextV1.PROTOCOL_VERSION, serverContext.getProtocolVersion());
- assertEquals(clientContext.getEncodeKey(), serverContext.getDecodeKey());
- assertEquals(clientContext.getDecodeKey(), serverContext.getEncodeKey());
- assertFalse(clientContext.getEncodeKey().equals(clientContext.getDecodeKey()));
- assertEquals(0, clientContext.getSequenceNumberForEncoding());
- assertEquals(0, clientContext.getSequenceNumberForDecoding());
- assertEquals(0, serverContext.getSequenceNumberForEncoding());
- assertEquals(0, serverContext.getSequenceNumberForDecoding());
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2ShellCppWrapper.java b/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2ShellCppWrapper.java
deleted file mode 100644
index 2b73653..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2ShellCppWrapper.java
+++ /dev/null
@@ -1,342 +0,0 @@
-// Copyright 2020 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
-//
-// https://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 com.google.common.io.BaseEncoding;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.ProcessBuilder.Redirect;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Arrays;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import javax.annotation.Nullable;
-
-/**
- * A wrapper to execute and interact with the //security/cryptauth/lib/securegcm:ukey2_shell binary.
- *
- * <p>This binary is a shell over the C++ implementation of the UKEY2 protocol, so this wrapper is
- * used to test compatibility between the C++ and Java implementations.
- *
- * <p>The ukey2_shell is invoked as follows:
- *
- * <pre>{@code
- * ukey2_shell --mode=<mode> --verification_string_length=<length>
- * }</pre>
- *
- * where {@code mode={initiator, responder}} and {@code verification_string_length} is a positive
- * integer.
- */
-public class Ukey2ShellCppWrapper {
- // The path the the ukey2_shell binary.
- private static final String BINARY_PATH = "build/src/main/cpp/src/securegcm/ukey2_shell";
-
- // The time to wait before timing out a read or write operation to the shell.
- @SuppressWarnings("GoodTime") // TODO(b/147378611): store a java.time.Duration instead
- private static final long IO_TIMEOUT_MILLIS = 5000;
-
- public enum Mode {
- INITIATOR,
- RESPONDER
- }
-
- private final Mode mode;
- private final int verificationStringLength;
- private final ExecutorService executorService;
-
- @Nullable private Process shellProcess;
- private boolean secureContextEstablished;
-
- /**
- * @param mode The mode to run the shell in (initiator or responder).
- * @param verificationStringLength The length of the verification string used in the handshake.
- */
- public Ukey2ShellCppWrapper(Mode mode, int verificationStringLength) {
- this.mode = mode;
- this.verificationStringLength = verificationStringLength;
- this.executorService = Executors.newSingleThreadExecutor();
- }
-
- /**
- * Begins execution of the ukey2_shell binary.
- *
- * @throws IOException
- */
- public void startShell() throws IOException {
- if (shellProcess != null) {
- throw new IllegalStateException("Shell already started.");
- }
-
- String modeArg = "--mode=" + getModeString();
- String verificationStringLengthArg = "--verification_string_length=" + verificationStringLength;
-
- final ProcessBuilder builder =
- new ProcessBuilder(BINARY_PATH, modeArg, verificationStringLengthArg);
-
- // Merge the shell's stderr with the stderr of the current process.
- builder.redirectError(Redirect.INHERIT);
-
- shellProcess = builder.start();
- }
-
- /**
- * Stops execution of the ukey2_shell binary.
- *
- * @throws IOException
- */
- public void stopShell() {
- if (shellProcess == null) {
- throw new IllegalStateException("Shell not started.");
- }
- shellProcess.destroy();
- }
-
- /**
- * @return the handshake message read from the shell.
- * @throws IOException
- */
- public byte[] readHandshakeMessage() throws IOException {
- return readFrameWithTimeout();
- }
-
- /**
- * Sends the handshake message to the shell.
- *
- * @param message
- * @throws IOException
- */
- public void writeHandshakeMessage(byte[] message) throws IOException {
- writeFrameWithTimeout(message);
- }
-
- /**
- * Reads the auth string from the shell and compares it with {@code authString}. If verification
- * succeeds, then write "ok" back as a confirmation.
- *
- * @param authString the auth string to compare to.
- * @throws IOException
- */
- public void confirmAuthString(byte[] authString) throws IOException {
- byte[] shellAuthString = readFrameWithTimeout();
- if (!Arrays.equals(authString, shellAuthString)) {
- throw new IOException(
- String.format(
- "Unable to verify auth string: 0x%s != 0x%s",
- BaseEncoding.base16().encode(authString),
- BaseEncoding.base16().encode(shellAuthString)));
- }
- writeFrameWithTimeout("ok".getBytes());
- secureContextEstablished = true;
- }
-
- /**
- * Sends {@code payload} to be encrypted by the shell. This function can only be called after a
- * handshake is performed and a secure context established.
- *
- * @param payload the data to be encrypted.
- * @return the encrypted message returned by the shell.
- * @throws IOException
- */
- public byte[] sendEncryptCommand(byte[] payload) throws IOException {
- writeFrameWithTimeout(createExpression("encrypt", payload));
- return readFrameWithTimeout();
- }
-
- /**
- * Sends {@code message} to be decrypted by the shell. This function can only be called after a
- * handshake is performed and a secure context established.
- *
- * @param message the data to be decrypted.
- * @return the decrypted payload returned by the shell.
- * @throws IOException
- */
- public byte[] sendDecryptCommand(byte[] message) throws IOException {
- writeFrameWithTimeout(createExpression("decrypt", message));
- return readFrameWithTimeout();
- }
-
- /**
- * Requests the session unique value from the shell. This function can only be called after a
- * handshake is performed and a secure context established.
- *
- * @return the session unique value returned by the shell.
- * @throws IOException
- */
- public byte[] sendSessionUniqueCommand() throws IOException {
- writeFrameWithTimeout(createExpression("session_unique", null));
- return readFrameWithTimeout();
- }
-
- /**
- * Reads a frame from the shell's stdout with a timeout.
- *
- * @return The contents of the frame.
- * @throws IOException
- */
- private byte[] readFrameWithTimeout() throws IOException {
- Future<byte[]> future =
- executorService.submit(
- new Callable<byte[]>() {
- @Override
- public byte[] call() throws Exception {
- return readFrame();
- }
- });
-
- try {
- return future.get(IO_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- throw new IOException(e);
- }
- }
-
- /**
- * Writes a frame to the shell's stdin with a timeout.
- *
- * @param contents the contents of the frame.
- * @throws IOException
- */
- private void writeFrameWithTimeout(final byte[] contents) throws IOException {
- Future<?> future =
- executorService.submit(
- new Runnable() {
- @Override
- public void run() {
- try {
- writeFrame(contents);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- });
-
- try {
- future.get(IO_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- throw new IOException(e);
- }
- }
-
- /**
- * Reads a frame from the shell's stdout, which has the format:
- *
- * <pre>{@code
- * +---------------------+-----------------+
- * | 4-bytes | |length| bytes |
- * +---------------------+-----------------+
- * | (unsigned) length | contents |
- * +---------------------+-----------------+
- * }</pre>
- *
- * @return the contents that were read
- * @throws IOException
- */
- private byte[] readFrame() throws IOException {
- if (shellProcess == null) {
- throw new IllegalStateException("Shell not started.");
- }
-
- InputStream inputStream = shellProcess.getInputStream();
- byte[] lengthBytes = new byte[4];
- if (inputStream.read(lengthBytes) != lengthBytes.length) {
- throw new IOException("Failed to read length.");
- }
-
- int length = ByteBuffer.wrap(lengthBytes).order(ByteOrder.BIG_ENDIAN).getInt();
- if (length < 0) {
- throw new IOException("Length too large: " + Arrays.toString(lengthBytes));
- }
-
- byte[] contents = new byte[length];
- int bytesRead = inputStream.read(contents);
- if (bytesRead != length) {
- throw new IOException("Failed to read entire contents: " + bytesRead + " != " + length);
- }
-
- return contents;
- }
-
- /**
- * Writes a frame to the shell's stdin, which has the format:
- *
- * <pre>{@code
- * +---------------------+-----------------+
- * | 4-bytes | |length| bytes |
- * +---------------------+-----------------+
- * | (unsigned) length | contents |
- * +---------------------+-----------------+
- * }</pre>
- *
- * @param contents the contents to send.
- * @throws IOException
- */
- private void writeFrame(byte[] contents) throws IOException {
- if (shellProcess == null) {
- throw new IllegalStateException("Shell not started.");
- }
-
- // The length is big-endian encoded, network byte order.
- long length = contents.length;
- byte[] lengthBytes = new byte[4];
- lengthBytes[0] = (byte) (length >> 32 & 0xFF);
- lengthBytes[1] = (byte) (length >> 16 & 0xFF);
- lengthBytes[2] = (byte) (length >> 8 & 0xFF);
- lengthBytes[3] = (byte) (length >> 0 & 0xFF);
-
- OutputStream outputStream = shellProcess.getOutputStream();
- outputStream.write(lengthBytes);
- outputStream.write(contents);
- outputStream.flush();
- }
-
- /**
- * Creates an expression to be processed when a secure connection is established, after the
- * handshake is done.
- *
- * @param command The command to send.
- * @param argument The argument of the command. Can be null.
- * @return the expression that can be sent to the shell.
- * @throws IOException.
- */
- private byte[] createExpression(String command, @Nullable byte[] argument) throws IOException {
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- outputStream.write(command.getBytes());
- outputStream.write(" ".getBytes());
- if (argument != null) {
- outputStream.write(argument);
- }
- return outputStream.toByteArray();
- }
-
- /** @return the mode string to use in the argument to start the ukey2_shell process. */
- private String getModeString() {
- switch (mode) {
- case INITIATOR:
- return "initiator";
- case RESPONDER:
- return "responder";
- default:
- throw new IllegalArgumentException("Uknown mode " + mode);
- }
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securemessage/CryptoOpsTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securemessage/CryptoOpsTest.java
deleted file mode 100644
index 65fa094..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securemessage/CryptoOpsTest.java
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright 2020 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
-//
-// https://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.securemessage;
-
-import static org.junit.Assert.assertThrows;
-
-import com.google.security.cryptauth.lib.securemessage.CryptoOps.EncType;
-import com.google.security.cryptauth.lib.securemessage.CryptoOps.SigType;
-import java.util.Arrays;
-import javax.crypto.KeyGenerator;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-import junit.framework.TestCase;
-
-/**
- * Unit tests for the CryptoOps class
- */
-public class CryptoOpsTest extends TestCase {
-
- /** HKDF Test Case 1 IKM from RFC 5869 */
- private static final byte[] HKDF_CASE1_IKM = {
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b
- };
-
- /** HKDF Test Case 1 salt from RFC 5869 */
- private static final byte[] HKDF_CASE1_SALT = {
- 0x00, 0x01, 0x02, 0x03, 0x04,
- 0x05, 0x06, 0x07, 0x08, 0x09,
- 0x0a, 0x0b, 0x0c
- };
-
- /** HKDF Test Case 1 info from RFC 5869 */
- private static final byte[] HKDF_CASE1_INFO = {
- (byte) 0xf0, (byte) 0xf1, (byte) 0xf2, (byte) 0xf3, (byte) 0xf4,
- (byte) 0xf5, (byte) 0xf6, (byte) 0xf7, (byte) 0xf8, (byte) 0xf9
- };
-
- /** First 32 bytes of HKDF Test Case 1 OKM (output) from RFC 5869 */
- private static final byte[] HKDF_CASE1_OKM = {
- (byte) 0x3c, (byte) 0xb2, (byte) 0x5f, (byte) 0x25, (byte) 0xfa,
- (byte) 0xac, (byte) 0xd5, (byte) 0x7a, (byte) 0x90, (byte) 0x43,
- (byte) 0x4f, (byte) 0x64, (byte) 0xd0, (byte) 0x36, (byte) 0x2f,
- (byte) 0x2a, (byte) 0x2d, (byte) 0x2d, (byte) 0x0a, (byte) 0x90,
- (byte) 0xcf, (byte) 0x1a, (byte) 0x5a, (byte) 0x4c, (byte) 0x5d,
- (byte) 0xb0, (byte) 0x2d, (byte) 0x56, (byte) 0xec, (byte) 0xc4,
- (byte) 0xc5, (byte) 0xbf, (byte) 0x34, (byte) 0x00, (byte) 0x72,
- (byte) 0x08, (byte) 0xd5, (byte) 0xb8, (byte) 0x87, (byte) 0x18,
- (byte) 0x58, (byte) 0x65
- };
-
- private SecretKey aesKey1;
- private SecretKey aesKey2;
-
- @Override
- protected void setUp() throws Exception {
- KeyGenerator aesKeygen = KeyGenerator.getInstance("AES");
- aesKeygen.init(256);
- aesKey1 = aesKeygen.generateKey();
- aesKey2 = aesKeygen.generateKey();
- super.setUp();
- }
-
- public void testNoPurposeConflicts() {
- // Ensure that signature algorithms and encryption algorithms are not given identical purposes
- // (this prevents confusion of derived keys).
- for (SigType sigType : SigType.values()) {
- for (EncType encType : EncType.values()) {
- assertFalse(CryptoOps.getPurpose(sigType).equals(CryptoOps.getPurpose(encType)));
- }
- }
- }
-
- public void testDeriveAes256KeyFor() throws Exception {
- // Test that deriving with the same key and purpose twice is deterministic
- assertTrue(Arrays.equals(CryptoOps.deriveAes256KeyFor(aesKey1, "A").getEncoded(),
- CryptoOps.deriveAes256KeyFor(aesKey1, "A").getEncoded()));
- // Test that derived keys with different purposes differ
- assertFalse(Arrays.equals(CryptoOps.deriveAes256KeyFor(aesKey1, "A").getEncoded(),
- CryptoOps.deriveAes256KeyFor(aesKey1, "B").getEncoded()));
- // Test that derived keys with the same purpose but different master keys differ
- assertFalse(Arrays.equals(CryptoOps.deriveAes256KeyFor(aesKey1, "A").getEncoded(),
- CryptoOps.deriveAes256KeyFor(aesKey2, "A").getEncoded()));
- }
-
- public void testHkdf() throws Exception {
- SecretKey inputKey = new SecretKeySpec(HKDF_CASE1_IKM, "AES");
- byte[] result = CryptoOps.hkdf(inputKey, HKDF_CASE1_SALT, HKDF_CASE1_INFO);
- byte[] expectedResult = Arrays.copyOf(HKDF_CASE1_OKM, 32);
- assertTrue(Arrays.equals(result, expectedResult));
- }
-
- public void testHkdfLongOutput() throws Exception {
- SecretKey inputKey = new SecretKeySpec(HKDF_CASE1_IKM, "AES");
- byte[] result = CryptoOps.hkdf(inputKey, HKDF_CASE1_SALT, HKDF_CASE1_INFO, 42);
- byte[] expectedResult = Arrays.copyOf(HKDF_CASE1_OKM, 42);
- assertTrue(Arrays.equals(result, expectedResult));
- }
-
- public void testHkdfShortOutput() throws Exception {
- SecretKey inputKey = new SecretKeySpec(HKDF_CASE1_IKM, "AES");
- byte[] result = CryptoOps.hkdf(inputKey, HKDF_CASE1_SALT, HKDF_CASE1_INFO, 12);
- byte[] expectedResult = Arrays.copyOf(HKDF_CASE1_OKM, 12);
- assertTrue(Arrays.equals(result, expectedResult));
- }
-
- public void testHkdfInvalidLengths() throws Exception {
- SecretKey inputKey = new SecretKeySpec(HKDF_CASE1_IKM, "AES");
-
- // Negative length
- assertThrows(
- IllegalArgumentException.class,
- () -> CryptoOps.hkdf(inputKey, HKDF_CASE1_SALT, HKDF_CASE1_INFO, -5));
-
- // Too long, would be more than 256 blocks
- assertThrows(
- IllegalArgumentException.class,
- () -> CryptoOps.hkdf(inputKey, HKDF_CASE1_SALT, HKDF_CASE1_INFO, 32 * 256 + 1));
- }
-
- public void testConcat() {
- byte[] a = { 1, 2, 3, 4};
- byte[] b = { 5 , 6 };
- byte[] expectedResult = { 1, 2, 3, 4, 5, 6 };
- byte[] result = CryptoOps.concat(a, b);
- assertEquals(a.length + b.length, result.length);
- assertTrue(Arrays.equals(expectedResult, result));
-
- byte[] empty = { };
- assertEquals(0, CryptoOps.concat(empty, empty).length);
- assertTrue(Arrays.equals(a, CryptoOps.concat(a, empty)));
- assertTrue(Arrays.equals(a, CryptoOps.concat(empty, a)));
-
- assertEquals(0, CryptoOps.concat(null, null).length);
- assertTrue(Arrays.equals(a, CryptoOps.concat(a, null)));
- assertTrue(Arrays.equals(a, CryptoOps.concat(null, a)));
- }
-
- public void testSubarray() {
- byte[] in = { 1, 2, 3, 4, 5, 6, 7 };
- assertTrue(Arrays.equals(in, CryptoOps.subarray(in, 0, in.length)));
- assertEquals(0, CryptoOps.subarray(in, 0, 0).length);
- byte[] expectedResult1 = { 1 };
- assertTrue(Arrays.equals(expectedResult1, CryptoOps.subarray(in, 0, 1)));
- byte[] expectedResult34 = { 3, 4 };
- assertTrue(Arrays.equals(expectedResult34, CryptoOps.subarray(in, 2, 4)));
- assertThrows(IndexOutOfBoundsException.class, () -> CryptoOps.subarray(in, 0, in.length + 1));
- assertThrows(IndexOutOfBoundsException.class, () -> CryptoOps.subarray(in, -1, in.length));
- assertThrows(
- IndexOutOfBoundsException.class, () -> CryptoOps.subarray(in, in.length, in.length));
- assertThrows(
- IndexOutOfBoundsException.class,
- () -> CryptoOps.subarray(in, Integer.MIN_VALUE, in.length));
- assertThrows(
- IndexOutOfBoundsException.class, () -> CryptoOps.subarray(in, 1, Integer.MIN_VALUE));
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securemessage/NullsGoogle3Test.java b/src/main/javatest/com/google/security/cryptauth/lib/securemessage/NullsGoogle3Test.java
deleted file mode 100644
index c28d2f9..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securemessage/NullsGoogle3Test.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2020 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
-//
-// https://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.securemessage;
-
-import com.google.common.testing.NullPointerTester;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.SecureMessage;
-import junit.framework.TestCase;
-
-/**
- * Non-portable Google3-based test to check null pointer behavior.
- */
-public class NullsGoogle3Test extends TestCase {
-
- /**
- * We test all of the classes in one place to avoid a proliferation of similar test cases,
- * noting that {@link NullPointerTester} emits the name of the class where the breakge occurs.
- */
- public void testNulls() {
- final NullPointerTester tester = new NullPointerTester();
- tester.testAllPublicStaticMethods(CryptoOps.class);
- tester.testAllPublicStaticMethods(PublicKeyProtoUtil.class);
-
- tester.setDefault(SecureMessage.class, SecureMessage.getDefaultInstance());
- tester.testAllPublicStaticMethods(SecureMessageParser.class);
-
- tester.testAllPublicStaticMethods(SecureMessageBuilder.class);
- tester.testAllPublicConstructors(SecureMessageBuilder.class);
- tester.testAllPublicInstanceMethods(new SecureMessageBuilder());
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securemessage/PublicKeyProtoUtilTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securemessage/PublicKeyProtoUtilTest.java
deleted file mode 100644
index 8581622..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securemessage/PublicKeyProtoUtilTest.java
+++ /dev/null
@@ -1,412 +0,0 @@
-// Copyright 2020 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
-//
-// https://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.securemessage;
-
-import com.google.common.io.BaseEncoding;
-import com.google.protobuf.ByteString;
-import com.google.security.annotations.SuppressInsecureCipherModeCheckerNoReview;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.DhPublicKey;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.EcP256PublicKey;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.GenericPublicKey;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.SimpleRsaPublicKey;
-import java.math.BigInteger;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECPoint;
-import java.security.spec.ECPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Arrays;
-import javax.crypto.KeyAgreement;
-import javax.crypto.interfaces.DHPrivateKey;
-import javax.crypto.interfaces.DHPublicKey;
-import junit.framework.TestCase;
-
-/** Tests for the PublicKeyProtoUtil class. */
-public class PublicKeyProtoUtilTest extends TestCase {
-
- private static final byte[] ZERO_BYTE = {0};
- private PublicKey ecPublicKey;
- private PublicKey rsaPublicKey;
-
- /**
- * Diffie Hellman {@link PublicKey}s require special treatment, so we store them specifically as a
- * {@link DHPublicKey} to minimize casting.
- */
- private DHPublicKey dhPublicKey;
-
- @Override
- public void setUp() {
- if (!isAndroidOsWithoutEcSupport()) {
- ecPublicKey = PublicKeyProtoUtil.generateEcP256KeyPair().getPublic();
- }
- rsaPublicKey = PublicKeyProtoUtil.generateRSA2048KeyPair().getPublic();
- dhPublicKey = (DHPublicKey) PublicKeyProtoUtil.generateDh2048KeyPair().getPublic();
- }
-
- public void testPublicKeyProtoSpecificEncodeParse() throws Exception {
- if (!isAndroidOsWithoutEcSupport()) {
- assertEquals(
- ecPublicKey,
- PublicKeyProtoUtil.parseEcPublicKey(PublicKeyProtoUtil.encodeEcPublicKey(ecPublicKey)));
- }
-
- assertEquals(
- rsaPublicKey,
- PublicKeyProtoUtil.parseRsa2048PublicKey(
- PublicKeyProtoUtil.encodeRsa2048PublicKey(rsaPublicKey)));
-
- // DHPublicKey objects don't seem to properly implement equals(), so we have to test that
- // the individual y and p values match (it is safe to assume g = 2 is used if p is correct).
- DHPublicKey parsedDHPublicKey =
- PublicKeyProtoUtil.parseDh2048PublicKey(
- PublicKeyProtoUtil.encodeDh2048PublicKey(dhPublicKey));
- assertEquals(dhPublicKey.getY(), parsedDHPublicKey.getY());
- assertEquals(dhPublicKey.getParams().getP(), parsedDHPublicKey.getParams().getP());
- assertEquals(dhPublicKey.getParams().getG(), parsedDHPublicKey.getParams().getG());
- }
-
- public void testPublicKeyProtoGenericEncodeParse() throws Exception {
- if (!isAndroidOsWithoutEcSupport()) {
- assertEquals(
- ecPublicKey,
- PublicKeyProtoUtil.parsePublicKey(
- PublicKeyProtoUtil.encodePaddedEcPublicKey(ecPublicKey)));
- assertEquals(
- ecPublicKey,
- PublicKeyProtoUtil.parsePublicKey(PublicKeyProtoUtil.encodePublicKey(ecPublicKey)));
- }
-
- assertEquals(
- rsaPublicKey,
- PublicKeyProtoUtil.parsePublicKey(PublicKeyProtoUtil.encodePublicKey(rsaPublicKey)));
-
- // See above explanation for why we treat DHPublicKey objects differently.
- DHPublicKey parsedDHPublicKey =
- PublicKeyProtoUtil.parseDh2048PublicKey(
- PublicKeyProtoUtil.encodeDh2048PublicKey(dhPublicKey));
- assertEquals(dhPublicKey.getY(), parsedDHPublicKey.getY());
- assertEquals(dhPublicKey.getParams().getP(), parsedDHPublicKey.getParams().getP());
- assertEquals(dhPublicKey.getParams().getG(), parsedDHPublicKey.getParams().getG());
- }
-
- public void testPaddedECPublicKeyEncodeHasPaddedNullByte() throws Exception {
- if (isAndroidOsWithoutEcSupport()) {
- return;
- }
-
- // Key where the x coordinate is 33 bytes, y coordinate is 32 bytes
- ECPublicKey maxXByteLengthKey =
- buildEcPublicKey(
- BaseEncoding.base64().decode("AM730WQL7ZAmvyAJX4euNdr3+nAIueGlYYGXE6p732h6"),
- BaseEncoding.base64().decode("JEnmaDpKn0fH4/0kKGb97qUSwI2uT+ta0GLe3V7REfk="));
- // Key where both coordinates are 33 bytes
- ECPublicKey maxByteLengthKey =
- buildEcPublicKey(
- BaseEncoding.base64().decode("AOg9TQCxFfVdXv7lO/6UVDyiPsu8XDkEWQIPUfqX6UHP"),
- BaseEncoding.base64().decode("AP/RW8uVyu6QImpbza51CqG1mtBTh5c9pjv9CUwOuB7E"));
- // Key where both coordinates are 32 bytes
- ECPublicKey notMaxByteLengthKey =
- buildEcPublicKey(
- BaseEncoding.base64().decode("M35bxV8HKr0e8v7f4zuXgw6TYFawvikFdI71u9S1ONI="),
- BaseEncoding.base64().decode("OXR+xCpD8AR0VR8TeBXA00eIr3rWE6sV6KrOM6MoWsc="));
- GenericPublicKey encodedMaxXByteLengthKey =
- PublicKeyProtoUtil.encodePublicKey(maxXByteLengthKey);
- GenericPublicKey paddedEncodedMaxXByteLengthKey =
- PublicKeyProtoUtil.encodePaddedEcPublicKey(maxXByteLengthKey);
- GenericPublicKey encodedMaxByteLengthKey = PublicKeyProtoUtil.encodePublicKey(maxByteLengthKey);
- GenericPublicKey paddedEncodedMaxByteLengthKey =
- PublicKeyProtoUtil.encodePaddedEcPublicKey(maxByteLengthKey);
- GenericPublicKey encodedNotMaxByteLengthKey =
- PublicKeyProtoUtil.encodePublicKey(notMaxByteLengthKey);
- GenericPublicKey paddedEncodedNotMaxByteLengthKey =
- PublicKeyProtoUtil.encodePaddedEcPublicKey(notMaxByteLengthKey);
-
- assertEquals(maxXByteLengthKey, PublicKeyProtoUtil.parsePublicKey(encodedMaxXByteLengthKey));
- assertEquals(
- maxXByteLengthKey, PublicKeyProtoUtil.parsePublicKey(paddedEncodedMaxXByteLengthKey));
- assertEquals(maxByteLengthKey, PublicKeyProtoUtil.parsePublicKey(encodedMaxByteLengthKey));
- assertEquals(
- maxByteLengthKey, PublicKeyProtoUtil.parsePublicKey(paddedEncodedMaxByteLengthKey));
- assertEquals(
- notMaxByteLengthKey, PublicKeyProtoUtil.parsePublicKey(paddedEncodedNotMaxByteLengthKey));
- assertEquals(
- notMaxByteLengthKey, PublicKeyProtoUtil.parsePublicKey(encodedNotMaxByteLengthKey));
-
- assertEquals(33, paddedEncodedMaxXByteLengthKey.getEcP256PublicKey().getX().size());
- assertEquals(33, paddedEncodedMaxXByteLengthKey.getEcP256PublicKey().getY().size());
- assertEquals(0, paddedEncodedMaxXByteLengthKey.getEcP256PublicKey().getX().byteAt(0));
- assertEquals(0, paddedEncodedMaxXByteLengthKey.getEcP256PublicKey().getY().byteAt(0));
- assertEquals(33, encodedMaxXByteLengthKey.getEcP256PublicKey().getX().size());
- assertEquals(32, encodedMaxXByteLengthKey.getEcP256PublicKey().getY().size());
-
- assertEquals(33, paddedEncodedMaxByteLengthKey.getEcP256PublicKey().getX().size());
- assertEquals(33, paddedEncodedMaxByteLengthKey.getEcP256PublicKey().getY().size());
- assertEquals(0, paddedEncodedMaxByteLengthKey.getEcP256PublicKey().getX().byteAt(0));
- assertEquals(0, paddedEncodedMaxByteLengthKey.getEcP256PublicKey().getY().byteAt(0));
- assertEquals(33, encodedMaxByteLengthKey.getEcP256PublicKey().getX().size());
- assertEquals(33, encodedMaxByteLengthKey.getEcP256PublicKey().getY().size());
-
- assertEquals(32, encodedNotMaxByteLengthKey.getEcP256PublicKey().getX().size());
- assertEquals(32, encodedNotMaxByteLengthKey.getEcP256PublicKey().getY().size());
- assertEquals(0, paddedEncodedNotMaxByteLengthKey.getEcP256PublicKey().getX().byteAt(0));
- assertEquals(0, paddedEncodedNotMaxByteLengthKey.getEcP256PublicKey().getY().byteAt(0));
- assertEquals(33, paddedEncodedNotMaxByteLengthKey.getEcP256PublicKey().getX().size());
- assertEquals(33, paddedEncodedNotMaxByteLengthKey.getEcP256PublicKey().getY().size());
- }
-
- @SuppressInsecureCipherModeCheckerNoReview
- public void testWrongPublicKeyType() throws Exception {
- KeyPairGenerator dsaGen = KeyPairGenerator.getInstance("DSA");
- dsaGen.initialize(512);
- PublicKey pk = dsaGen.generateKeyPair().getPublic();
-
- if (!isAndroidOsWithoutEcSupport()) {
- // Try to encode it as EC
- try {
- PublicKeyProtoUtil.encodeEcPublicKey(pk);
- fail();
- } catch (IllegalArgumentException expected) {
- }
-
- try {
- PublicKeyProtoUtil.encodePaddedEcPublicKey(pk);
- fail();
- } catch (IllegalArgumentException expected) {
- }
- }
-
- // Try to encode it as RSA
- try {
- PublicKeyProtoUtil.encodeRsa2048PublicKey(pk);
- fail();
- } catch (IllegalArgumentException expected) {
- }
-
- // Try to encode it as DH
- try {
- PublicKeyProtoUtil.encodeDh2048PublicKey(pk);
- fail();
- } catch (IllegalArgumentException expected) {
- }
-
- // Try to encode it as Generic
- try {
- PublicKeyProtoUtil.encodePublicKey(pk);
- fail();
- } catch (IllegalArgumentException expected) {
- }
- }
-
- public void testEcPublicKeyProtoInvalidEncoding() throws Exception {
- if (isAndroidOsWithoutEcSupport()) {
- return;
- }
-
- EcP256PublicKey validProto = PublicKeyProtoUtil.encodeEcPublicKey(ecPublicKey);
- EcP256PublicKey.Builder invalidProto = EcP256PublicKey.newBuilder(validProto);
-
- // Mess up the X coordinate by repeating it twice
- byte[] newX =
- CryptoOps.concat(validProto.getX().toByteArray(), validProto.getX().toByteArray());
- checkParsingFailsFor(invalidProto.setX(ByteString.copyFrom(newX)).build());
-
- // Mess up the Y coordinate by erasing it
- invalidProto = EcP256PublicKey.newBuilder(validProto);
- checkParsingFailsFor(invalidProto.setY(ByteString.EMPTY).build());
-
- // Pick a point that is likely not on the curve by copying X over Y
- invalidProto = EcP256PublicKey.newBuilder(validProto);
- checkParsingFailsFor(invalidProto.setY(validProto.getX()).build());
-
- // Try the point (0, 0)
- invalidProto = EcP256PublicKey.newBuilder(validProto);
- checkParsingFailsFor(
- invalidProto
- .setX(ByteString.copyFrom(ZERO_BYTE))
- .setY(ByteString.copyFrom(ZERO_BYTE))
- .build());
- }
-
- private void checkParsingFailsFor(EcP256PublicKey invalid) {
- try {
- // Should fail to decode
- PublicKeyProtoUtil.parseEcPublicKey(invalid);
- fail();
- } catch (InvalidKeySpecException expected) {
- }
- }
-
- public void testSimpleRsaPublicKeyProtoInvalidEncoding() throws Exception {
- SimpleRsaPublicKey validProto = PublicKeyProtoUtil.encodeRsa2048PublicKey(rsaPublicKey);
- SimpleRsaPublicKey.Builder invalidProto;
-
- // Double the number of bits in the modulus
- invalidProto = SimpleRsaPublicKey.newBuilder(validProto);
- byte[] newN =
- CryptoOps.concat(validProto.getN().toByteArray(), validProto.getN().toByteArray());
- checkParsingFailsFor(invalidProto.setN(ByteString.copyFrom(newN)).build());
-
- // Set the modulus to 0
- invalidProto = SimpleRsaPublicKey.newBuilder(validProto);
- checkParsingFailsFor(invalidProto.setN(ByteString.copyFrom(ZERO_BYTE)).build());
-
- // Set the modulus to 65537 (way too small)
- invalidProto = SimpleRsaPublicKey.newBuilder(validProto);
- checkParsingFailsFor(
- invalidProto.setN(ByteString.copyFrom(BigInteger.valueOf(65537).toByteArray())).build());
- }
-
- private static void checkParsingFailsFor(SimpleRsaPublicKey invalid) {
- try {
- // Should fail to decode
- PublicKeyProtoUtil.parseRsa2048PublicKey(invalid);
- fail();
- } catch (InvalidKeySpecException expected) {
- }
- }
-
- public void testSimpleDhPublicKeyProtoInvalidEncoding() throws Exception {
- DhPublicKey validProto = PublicKeyProtoUtil.encodeDh2048PublicKey(dhPublicKey);
- DhPublicKey.Builder invalidProto;
-
- // Double the number of bits in the public element encoding
- invalidProto = DhPublicKey.newBuilder(validProto);
- byte[] newY =
- CryptoOps.concat(validProto.getY().toByteArray(), validProto.getY().toByteArray());
- checkParsingFailsFor(invalidProto.setY(ByteString.copyFrom(newY)).build());
-
- // Set the public element to 0
- invalidProto = DhPublicKey.newBuilder(validProto);
- checkParsingFailsFor(invalidProto.setY(ByteString.copyFrom(ZERO_BYTE)).build());
- }
-
- private static void checkParsingFailsFor(DhPublicKey invalid) {
- try {
- // Should fail to decode
- PublicKeyProtoUtil.parseDh2048PublicKey(invalid);
- fail();
- } catch (InvalidKeySpecException expected) {
- }
- }
-
- public void testDhKeyAgreementWorks() throws Exception {
- int minExpectedSecretLength = (PublicKeyProtoUtil.DH_P.bitLength() / 8) - 4;
-
- KeyPair clientKeyPair = PublicKeyProtoUtil.generateDh2048KeyPair();
- KeyPair serverKeyPair = PublicKeyProtoUtil.generateDh2048KeyPair();
- BigInteger clientY = ((DHPublicKey) clientKeyPair.getPublic()).getY();
- BigInteger serverY = ((DHPublicKey) serverKeyPair.getPublic()).getY();
- assertFalse(clientY.equals(serverY)); // DHPublicKeys should not be equal
-
- // Run client side of the key exchange
- byte[] clientSecret = doDhAgreement(clientKeyPair.getPrivate(), serverKeyPair.getPublic());
- assert (clientSecret.length >= minExpectedSecretLength);
-
- // Run the server side of the key exchange
- byte[] serverSecret = doDhAgreement(serverKeyPair.getPrivate(), clientKeyPair.getPublic());
- assert (serverSecret.length >= minExpectedSecretLength);
-
- assertTrue(Arrays.equals(clientSecret, serverSecret));
- }
-
- public void testDh2048PrivateKeyEncoding() throws Exception {
- KeyPair testPair = PublicKeyProtoUtil.generateDh2048KeyPair();
- DHPrivateKey sk = (DHPrivateKey) testPair.getPrivate();
- DHPrivateKey skParsed =
- PublicKeyProtoUtil.parseDh2048PrivateKey(PublicKeyProtoUtil.encodeDh2048PrivateKey(sk));
- assertEquals(sk.getX(), skParsed.getX());
- assertEquals(sk.getParams().getP(), skParsed.getParams().getP());
- assertEquals(sk.getParams().getG(), skParsed.getParams().getG());
- }
-
- public void testParseEcPublicKeyOnLegacyPlatform() {
- if (!PublicKeyProtoUtil.isLegacyCryptoRequired()) {
- return; // This test only runs on legacy platforms
- }
- byte[] pointBytes = {
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
- 1, 2
- };
-
- try {
- PublicKeyProtoUtil.parseEcPublicKey(
- EcP256PublicKey.newBuilder()
- .setX(ByteString.copyFrom(pointBytes))
- .setY(ByteString.copyFrom(pointBytes))
- .build());
- fail();
- } catch (InvalidKeySpecException expected) {
- // Should get this specific exception when EC doesn't work
- }
- }
-
- public void testIsLegacyCryptoRequired() {
- assertEquals(isAndroidOsWithoutEcSupport(), PublicKeyProtoUtil.isLegacyCryptoRequired());
- }
-
- /** @return true if running on an Android OS that doesn't support Elliptic Curve algorithms */
- public static boolean isAndroidOsWithoutEcSupport() {
- try {
- Class<?> clazz = ClassLoader.getSystemClassLoader().loadClass("android.os.Build$VERSION");
- int sdkVersion = clazz.getField("SDK_INT").getInt(null);
- if (sdkVersion < PublicKeyProtoUtil.ANDROID_HONEYCOMB_SDK_INT) {
- return true;
- }
- } catch (ClassNotFoundException e) {
- // Not running on Android
- return false;
- } catch (SecurityException e) {
- throw new AssertionError(e);
- } catch (NoSuchFieldException e) {
- throw new AssertionError(e);
- } catch (IllegalArgumentException e) {
- throw new AssertionError(e);
- } catch (IllegalAccessException e) {
- throw new AssertionError(e);
- }
- return false;
- }
-
- @SuppressInsecureCipherModeCheckerNoReview
- private static byte[] doDhAgreement(PrivateKey secretKey, PublicKey peerKey) throws Exception {
- KeyAgreement agreement = KeyAgreement.getInstance("DH");
- agreement.init(secretKey);
- agreement.doPhase(peerKey, true);
- return agreement.generateSecret();
- }
-
- private static ECPublicKey buildEcPublicKey(byte[] encodedX, byte[] encodedY) throws Exception {
- try {
- BigInteger wX = new BigInteger(encodedX);
- BigInteger wY = new BigInteger(encodedY);
- return (ECPublicKey)
- KeyFactory.getInstance("EC")
- .generatePublic(
- new ECPublicKeySpec(
- new ECPoint(wX, wY),
- ((ECPublicKey) PublicKeyProtoUtil.generateEcP256KeyPair().getPublic())
- .getParams()));
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
- }
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securemessage/SecureMessageSimpleTestVectorTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securemessage/SecureMessageSimpleTestVectorTest.java
deleted file mode 100644
index 285b259..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securemessage/SecureMessageSimpleTestVectorTest.java
+++ /dev/null
@@ -1,403 +0,0 @@
-// Copyright 2020 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
-//
-// https://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.securemessage;
-
-import com.google.protobuf.InvalidProtocolBufferException;
-import com.google.security.cryptauth.lib.securemessage.CryptoOps.EncType;
-import com.google.security.cryptauth.lib.securemessage.CryptoOps.SigType;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.GenericPublicKey;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.Header;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.HeaderAndBody;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.SecureMessage;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.util.Arrays;
-import javax.crypto.KeyGenerator;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-import junit.framework.TestCase;
-
-/**
- * Tests the library against some very basic test vectors, to help ensure wire-format
- * compatibility is not broken.
- */
-public class SecureMessageSimpleTestVectorTest extends TestCase {
-
- private static final KeyFactory EC_KEY_FACTORY;
- static {
- try {
- if (PublicKeyProtoUtil.isLegacyCryptoRequired()) {
- EC_KEY_FACTORY = null;
- } else {
- EC_KEY_FACTORY = KeyFactory.getInstance("EC");
- }
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- private static final byte[] TEST_ASSOCIATED_DATA = {
- 11, 22, 33, 44, 55
- };
- private static final byte[] TEST_METADATA = {
- 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28
- };
- private static final byte[] TEST_VKID = {
- 0, 0, 1
- };
- private static final byte[] TEST_DKID = {
- -1, -1, 0,
- };
- private static final byte[] TEST_MESSAGE = {
- 0, 99, 1, 98, 2, 97, 3, 96, 4, 95, 5, 94, 6, 93, 7, 92, 8, 91, 9, 90
- };
-
- // The following fields are initialized below, in a static block that contains auto-generated test
- // vectors. Initialization can't just be done inline due to code that throws checked exceptions.
- private static final PublicKey TEST_EC_PUBLIC_KEY;
- private static final PrivateKey TEST_EC_PRIVATE_KEY;
- private static final SecretKey TEST_KEY1;
- private static final SecretKey TEST_KEY2;
- private static final byte[] TEST_VECTOR_ECDSA_ONLY;
- private static final byte[] TEST_VECTOR_ECDSA_AND_AES;
- private static final byte[] TEST_VECTOR_HMAC_AND_AES_SAME_KEYS;
- private static final byte[] TEST_VECTOR_HMAC_AND_AES_DIFFERENT_KEYS;
-
- public void testEcdsaOnly() throws Exception {
- if (PublicKeyProtoUtil.isLegacyCryptoRequired()) {
- // On older Android platforms we can't run this test.
- return;
- }
- SecureMessage testVector = SecureMessage.parseFrom(TEST_VECTOR_ECDSA_ONLY);
- Header unverifiedHeader = SecureMessageParser.getUnverifiedHeader(testVector);
- HeaderAndBody headerAndBody = SecureMessageParser.parseSignedCleartextMessage(
- testVector, TEST_EC_PUBLIC_KEY, SigType.ECDSA_P256_SHA256, TEST_ASSOCIATED_DATA);
- assertTrue(Arrays.equals(
- unverifiedHeader.toByteArray(),
- headerAndBody.getHeader().toByteArray()));
- assertTrue(Arrays.equals(TEST_MESSAGE, headerAndBody.getBody().toByteArray()));
- assertEquals(TEST_ASSOCIATED_DATA.length, unverifiedHeader.getAssociatedDataLength());
- assertTrue(Arrays.equals(TEST_METADATA, unverifiedHeader.getPublicMetadata().toByteArray()));
- assertTrue(Arrays.equals(TEST_VKID, unverifiedHeader.getVerificationKeyId().toByteArray()));
- assertFalse(unverifiedHeader.hasDecryptionKeyId());
- }
-
- public void testEcdsaAndAes() throws Exception {
- if (PublicKeyProtoUtil.isLegacyCryptoRequired()) {
- // On older Android platforms we can't run this test.
- return;
- }
- SecureMessage testVector = SecureMessage.parseFrom(TEST_VECTOR_ECDSA_AND_AES);
- Header unverifiedHeader = SecureMessageParser.getUnverifiedHeader(testVector);
- HeaderAndBody headerAndBody = SecureMessageParser.parseSignCryptedMessage(
- testVector,
- TEST_EC_PUBLIC_KEY,
- SigType.ECDSA_P256_SHA256,
- TEST_KEY1,
- EncType.AES_256_CBC,
- TEST_ASSOCIATED_DATA);
- assertTrue(Arrays.equals(
- unverifiedHeader.toByteArray(),
- headerAndBody.getHeader().toByteArray()));
- assertTrue(Arrays.equals(TEST_MESSAGE, headerAndBody.getBody().toByteArray()));
- assertEquals(TEST_ASSOCIATED_DATA.length, unverifiedHeader.getAssociatedDataLength());
- assertTrue(Arrays.equals(TEST_METADATA, unverifiedHeader.getPublicMetadata().toByteArray()));
- assertTrue(Arrays.equals(TEST_VKID, unverifiedHeader.getVerificationKeyId().toByteArray()));
- assertTrue(Arrays.equals(TEST_DKID, unverifiedHeader.getDecryptionKeyId().toByteArray()));
- }
-
- public void testHmacAndAesSameKeys() throws Exception {
- SecureMessage testVector = SecureMessage.parseFrom(TEST_VECTOR_HMAC_AND_AES_SAME_KEYS);
- Header unverifiedHeader = SecureMessageParser.getUnverifiedHeader(testVector);
-
- HeaderAndBody headerAndBody = SecureMessageParser.parseSignCryptedMessage(
- testVector,
- TEST_KEY1,
- SigType.HMAC_SHA256,
- TEST_KEY1,
- EncType.AES_256_CBC,
- TEST_ASSOCIATED_DATA);
- assertTrue(Arrays.equals(
- unverifiedHeader.toByteArray(),
- headerAndBody.getHeader().toByteArray()));
- assertTrue(Arrays.equals(TEST_MESSAGE, headerAndBody.getBody().toByteArray()));
- assertEquals(TEST_ASSOCIATED_DATA.length, unverifiedHeader.getAssociatedDataLength());
- assertTrue(Arrays.equals(TEST_METADATA, unverifiedHeader.getPublicMetadata().toByteArray()));
- assertTrue(Arrays.equals(TEST_VKID, unverifiedHeader.getVerificationKeyId().toByteArray()));
- assertTrue(Arrays.equals(TEST_DKID, unverifiedHeader.getDecryptionKeyId().toByteArray()));
- }
-
- public void testHmacAndAesDifferentKeys() throws Exception {
- SecureMessage testVector = SecureMessage.parseFrom(TEST_VECTOR_HMAC_AND_AES_DIFFERENT_KEYS);
- Header unverifiedHeader = SecureMessageParser.getUnverifiedHeader(testVector);
- HeaderAndBody headerAndBody = SecureMessageParser.parseSignCryptedMessage(
- testVector,
- TEST_KEY1,
- SigType.HMAC_SHA256,
- TEST_KEY2,
- EncType.AES_256_CBC,
- TEST_ASSOCIATED_DATA);
- assertTrue(Arrays.equals(
- unverifiedHeader.toByteArray(),
- headerAndBody.getHeader().toByteArray()));
- assertTrue(Arrays.equals(TEST_MESSAGE, headerAndBody.getBody().toByteArray()));
- assertEquals(TEST_ASSOCIATED_DATA.length, unverifiedHeader.getAssociatedDataLength());
- assertTrue(Arrays.equals(TEST_METADATA, unverifiedHeader.getPublicMetadata().toByteArray()));
- assertTrue(Arrays.equals(TEST_VKID, unverifiedHeader.getVerificationKeyId().toByteArray()));
- assertTrue(Arrays.equals(TEST_DKID, unverifiedHeader.getDecryptionKeyId().toByteArray()));
- }
-
- /**
- * This code emits the test vectors to {@code System.out}. It will not generate fresh test
- * vectors unless an existing test vector is set to {@code null}, but it contains all of the code
- * used to the generate the test vector values. Ideally, existing test vectors should never be
- * regenerated, but having this code available should make it easier to add new test vectors.
- */
- public void testGenerateTestVectorsPseudoTest() throws Exception {
- if (PublicKeyProtoUtil.isLegacyCryptoRequired()) {
- // On older Android platforms we can't run this test.
- return;
- }
- System.out.printf(" static {\n try {\n");
- String indent = " ";
- PublicKey testEcPublicKey = TEST_EC_PUBLIC_KEY;
- PrivateKey testEcPrivateKey = TEST_EC_PRIVATE_KEY;
- if (testEcPublicKey == null) {
- KeyPair testEcKeyPair = PublicKeyProtoUtil.generateEcP256KeyPair();
- testEcPublicKey = testEcKeyPair.getPublic();
- testEcPrivateKey = testEcKeyPair.getPrivate();
- }
- System.out.printf("%s%s = parsePublicKey(new byte[] %s);\n",
- indent,
- "TEST_EC_PUBLIC_KEY",
- byteArrayToJavaCode(indent, encodePublicKey(testEcPublicKey)));
- System.out.printf("%s%s = parseEcPrivateKey(new byte[] %s);\n",
- indent,
- "TEST_EC_PRIVATE_KEY",
- byteArrayToJavaCode(indent, encodeEcPrivateKey(testEcPrivateKey)));
-
- SecretKey testKey1 = TEST_KEY1;
- if (testKey1 == null) {
- testKey1 = makeAesKey();
- }
- System.out.printf("%s%s = new SecretKeySpec(new byte[] %s, \"AES\");\n",
- indent,
- "TEST_KEY1",
- byteArrayToJavaCode(indent, testKey1.getEncoded()));
-
- SecretKey testKey2 = TEST_KEY2;
- if (testKey2 == null) {
- testKey2 = makeAesKey();
- }
- System.out.printf("%s%s = new SecretKeySpec(new byte[] %s, \"AES\");\n",
- indent,
- "TEST_KEY2",
- byteArrayToJavaCode(indent, testKey2.getEncoded()));
-
- byte[] testVectorEcdsaOnly = TEST_VECTOR_ECDSA_ONLY;
- if (testVectorEcdsaOnly == null) {
- testVectorEcdsaOnly = new SecureMessageBuilder()
- .setAssociatedData(TEST_ASSOCIATED_DATA)
- .setPublicMetadata(TEST_METADATA)
- .setVerificationKeyId(TEST_VKID)
- .buildSignedCleartextMessage(
- testEcPrivateKey, SigType.ECDSA_P256_SHA256, TEST_MESSAGE).toByteArray();
- }
- printInitializerFor(indent, "TEST_VECTOR_ECDSA_ONLY", testVectorEcdsaOnly);
-
- byte[] testVectorEcdsaAndAes = TEST_VECTOR_ECDSA_AND_AES;
- if (testVectorEcdsaAndAes == null) {
- testVectorEcdsaAndAes = new SecureMessageBuilder()
- .setAssociatedData(TEST_ASSOCIATED_DATA)
- .setDecryptionKeyId(TEST_DKID)
- .setPublicMetadata(TEST_METADATA)
- .setVerificationKeyId(TEST_VKID)
- .buildSignCryptedMessage(
- testEcPrivateKey,
- SigType.ECDSA_P256_SHA256,
- testKey1,
- EncType.AES_256_CBC,
- TEST_MESSAGE).toByteArray();
- }
- printInitializerFor(indent, "TEST_VECTOR_ECDSA_AND_AES", testVectorEcdsaAndAes);
-
- byte[] testVectorHmacAndAesSameKeys = TEST_VECTOR_HMAC_AND_AES_SAME_KEYS;
- if (testVectorHmacAndAesSameKeys == null) {
- testVectorHmacAndAesSameKeys = new SecureMessageBuilder()
- .setAssociatedData(TEST_ASSOCIATED_DATA)
- .setDecryptionKeyId(TEST_DKID)
- .setPublicMetadata(TEST_METADATA)
- .setVerificationKeyId(TEST_VKID)
- .buildSignCryptedMessage(
- testKey1,
- SigType.HMAC_SHA256,
- testKey1,
- EncType.AES_256_CBC,
- TEST_MESSAGE).toByteArray();
- }
- printInitializerFor(indent, "TEST_VECTOR_HMAC_AND_AES_SAME_KEYS", testVectorHmacAndAesSameKeys);
-
- byte[] testVectorHmacAndAesDifferentKeys = TEST_VECTOR_HMAC_AND_AES_DIFFERENT_KEYS;
- if (testVectorHmacAndAesDifferentKeys == null) {
- testVectorHmacAndAesDifferentKeys = new SecureMessageBuilder()
- .setAssociatedData(TEST_ASSOCIATED_DATA)
- .setDecryptionKeyId(TEST_DKID)
- .setPublicMetadata(TEST_METADATA)
- .setVerificationKeyId(TEST_VKID)
- .buildSignCryptedMessage(
- testKey1,
- SigType.HMAC_SHA256,
- testKey2,
- EncType.AES_256_CBC,
- TEST_MESSAGE).toByteArray();
- }
- printInitializerFor(
- indent, "TEST_VECTOR_HMAC_AND_AES_DIFFERENT_KEYS", testVectorHmacAndAesDifferentKeys);
-
- System.out.printf(
- " } catch (Exception e) {\n throw new RuntimeException(e);\n }\n }\n");
- }
-
- private SecretKey makeAesKey() throws NoSuchAlgorithmException {
- KeyGenerator aesKeygen = KeyGenerator.getInstance("AES");
- aesKeygen.init(256);
- return aesKeygen.generateKey();
- }
-
- private void printInitializerFor(String indent, String name, byte[] value) {
- System.out.printf("%s%s = new byte[] %s;\n",
- indent,
- name,
- byteArrayToJavaCode(indent, value));
- }
-
- private static String byteArrayToJavaCode(String lineIndent, byte[] array) {
- String newline = "\n" + lineIndent + " ";
- String unwrappedArray = Arrays.toString(array).replace("[", "").replace("]", "");
- int wrapAfter = 16;
- int count = wrapAfter;
- StringBuilder result = new StringBuilder("{");
- for (String entry : unwrappedArray.split(" ")) {
- if (++count > wrapAfter) {
- result.append(newline);
- count = 0;
- } else {
- result.append(" ");
- }
- result.append(entry);
- }
- result.append(" }");
- return result.toString();
- }
-
- private static byte[] encodePublicKey(PublicKey pk) {
- return PublicKeyProtoUtil.encodePublicKey(pk).toByteArray();
- }
-
- private static PublicKey parsePublicKey(byte[] encodedPk)
- throws InvalidKeySpecException, InvalidProtocolBufferException {
- GenericPublicKey gpk = GenericPublicKey.parseFrom(encodedPk);
- if (PublicKeyProtoUtil.isLegacyCryptoRequired()
- && gpk.getType() == SecureMessageProto.PublicKeyType.EC_P256) {
- return null;
- }
- return PublicKeyProtoUtil.parsePublicKey(gpk);
- }
-
- private static byte[] encodeEcPrivateKey(PrivateKey sk) {
- return sk.getEncoded();
- }
-
- private static PrivateKey parseEcPrivateKey(byte[] sk) throws InvalidKeySpecException {
- if (PublicKeyProtoUtil.isLegacyCryptoRequired()) {
- return null;
- }
- return EC_KEY_FACTORY.generatePrivate(new PKCS8EncodedKeySpec(sk));
- }
-
- // The following block of code was automatically generated by cut and pasting the output of the
- // generateTestVectorsPseudoTest, which should reliably emit this same test vectors. Please
- // DO NOT DELETE any of these existing test vectors unless you _really_ know what you are doing.
- //
- // --- AUTO GENERATED CODE BEGINS HERE ---
- static {
- try {
- TEST_EC_PUBLIC_KEY = parsePublicKey(new byte[] {
- 8, 1, 18, 70, 10, 33, 0, -109, 9, 5, 8, -89, -3, -68, -86, -19, 17,
- -126, -11, -95, 35, 101, 102, -57, -84, -118, 73, 83, 66, -62, -49, -91, 71, -19,
- 52, 123, 113, 119, 45, 18, 33, 0, -65, -19, 83, -66, -12, 62, 102, -67, 116,
- 64, 42, 55, -84, -101, 90, -106, 113, -89, -30, 57, -112, 96, -99, -126, 14, 83,
- 41, 95, -24, -114, 23, -5 });
- TEST_EC_PRIVATE_KEY = parseEcPrivateKey(new byte[] {
- 48, 65, 2, 1, 0, 48, 19, 6, 7, 42, -122, 72, -50, 61, 2, 1, 6,
- 8, 42, -122, 72, -50, 61, 3, 1, 7, 4, 39, 48, 37, 2, 1, 1, 4,
- 32, 26, -82, -61, -86, -59, -8, 2, -62, -17, -20, 122, 3, 85, -102, -76, 81,
- 51, 39, -9, 12, 99, -117, 127, 19, 121, 109, -31, -49, 110, 121, 76, -107 });
- TEST_KEY1 = new SecretKeySpec(new byte[] {
- -89, 105, 62, -41, -75, 78, 70, 110, -62, -58, -80, -81, -99, -62, 39, 38, 37,
- -7, -112, -83, 81, 23, 125, -72, -100, 103, -34, -23, -68, 21, -46, -104 }, "AES");
- TEST_KEY2 = new SecretKeySpec(new byte[] {
- -6, 48, 107, 61, -99, -89, 111, 33, 70, 54, -13, 111, 81, -120, 50, 89, -119,
- -113, -114, 63, 12, -68, 40, 42, -77, -58, -49, 18, 69, 91, -20, -65 }, "AES");
- TEST_VECTOR_ECDSA_ONLY = new byte[] {
- 10, 56, 10, 32, 8, 2, 16, 1, 26, 3, 0, 0, 1, 50, 19, 10, 11,
- 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
- 56, 5, 18, 20, 0, 99, 1, 98, 2, 97, 3, 96, 4, 95, 5, 94, 6,
- 93, 7, 92, 8, 91, 9, 90, 18, 72, 48, 70, 2, 33, 0, -79, 59, 50,
- 21, 54, 61, -92, 77, -34, -77, -45, -105, 107, -28, -19, 91, -78, 120, 68, 33,
- 11, -76, -1, 50, 64, -127, -78, 6, 108, 115, -13, 126, 2, 33, 0, -72, -44,
- 52, 93, 105, 109, -127, -111, 11, 33, -111, 97, -114, 9, 117, -68, -45, 64, 63,
- 43, 60, -44, -89, -107, -59, -45, 56, 100, -66, -40, 46, -60 };
- TEST_VECTOR_ECDSA_AND_AES = new byte[] {
- 10, 107, 10, 55, 8, 2, 16, 2, 26, 3, 0, 0, 1, 34, 3, -1, -1,
- 0, 42, 16, -86, 16, 55, -8, -85, -47, -77, -36, -127, 44, -10, -44, -63, 115,
- -111, 26, 50, 19, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
- 23, 24, 25, 26, 27, 28, 56, 5, 18, 48, -110, 23, -67, 122, -118, 96, -4,
- 32, -113, -104, -107, -16, 76, 37, -61, -67, -63, 90, 38, 96, -47, -105, 56, -34,
- 50, -30, 82, 25, 100, 36, 69, 50, 68, 60, 38, 96, -108, -49, -73, -10, -62,
- -76, -45, -105, -86, 93, 28, 34, 18, 70, 48, 68, 2, 33, 0, -87, -103, 11,
- -70, 34, 33, -41, 90, -83, -74, 19, -13, 127, -43, -116, -32, 88, -13, 125, -122,
- 56, -21, 79, 47, 101, 89, -80, -43, 102, 92, 4, -15, 2, 31, 109, -69, 35,
- 21, 44, -27, -77, 32, 17, -90, -68, 113, 55, -24, -122, 40, 81, 51, 0, -84,
- -29, -12, -26, 73, 105, -32, 116, -28, 84, -116, -117 };
- TEST_VECTOR_HMAC_AND_AES_SAME_KEYS = new byte[] {
- 10, 91, 10, 55, 8, 1, 16, 2, 26, 3, 0, 0, 1, 34, 3, -1, -1,
- 0, 42, 16, -110, 48, 67, 67, -31, 24, -42, 13, -44, -109, 6, 113, 34, -70,
- 121, 6, 50, 19, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
- 23, 24, 25, 26, 27, 28, 56, 5, 18, 32, -44, -102, -16, 123, 113, -75, 88,
- -33, 118, 25, 60, -65, 109, 26, -70, -123, 58, -114, 126, 8, 106, -28, 65, -38,
- -4, 68, -78, -91, 49, -13, 22, -122, 18, 32, 20, -120, -113, -76, 85, -35, -53,
- 37, -18, 66, -38, 32, 10, 30, 89, 112, -39, -27, 24, 93, -36, -100, -127, -79,
- 94, -7, -19, -41, -47, -29, 1, 12 };
- TEST_VECTOR_HMAC_AND_AES_DIFFERENT_KEYS = new byte[] {
- 10, 107, 10, 55, 8, 1, 16, 2, 26, 3, 0, 0, 1, 34, 3, -1, -1,
- 0, 42, 16, -96, -7, 39, 79, -37, 40, 1, -30, 97, 0, 123, -7, -124, -75,
- -127, -18, 50, 19, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
- 23, 24, 25, 26, 27, 28, 56, 5, 18, 48, 90, 40, -48, -113, 84, -32, 47,
- 98, 54, -128, 127, 115, 32, 87, -86, 4, -26, 99, 9, -88, 13, 77, 127, 114,
- -48, -117, -94, 96, -86, -105, -123, 11, 116, -69, -83, -110, 3, -10, 0, -34, 72,
- 10, -58, 3, -119, -94, 23, -114, 18, 32, -25, -126, 95, 125, -110, -62, -36, -78,
- 97, 72, -54, -114, 97, -68, -46, 107, 53, 55, -57, 88, 127, -20, -23, 80, -9,
- -91, 115, 42, 24, 49, -76, -111 };
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securemessage/SecureMessageTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securemessage/SecureMessageTest.java
deleted file mode 100644
index 40e5091..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securemessage/SecureMessageTest.java
+++ /dev/null
@@ -1,766 +0,0 @@
-// Copyright 2020 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
-//
-// https://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.securemessage;
-
-import com.google.protobuf.ByteString;
-import com.google.protobuf.InvalidProtocolBufferException;
-import com.google.protobuf.UninitializedMessageException;
-import com.google.security.cryptauth.lib.securemessage.CryptoOps.EncType;
-import com.google.security.cryptauth.lib.securemessage.CryptoOps.SigType;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.EcP256PublicKey;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.GenericPublicKey;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.Header;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.HeaderAndBody;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.SecureMessage;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.SimpleRsaPublicKey;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.SignatureException;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Arrays;
-import java.util.List;
-import javax.crypto.KeyGenerator;
-import javax.crypto.SecretKey;
-import junit.framework.TestCase;
-
-/**
- * Tests for the SecureMessageBuilder and SecureMessageParser classes.
- */
-public class SecureMessageTest extends TestCase {
- // Not to be used when generating cross-platform test vectors (due to default charset encoding)
- public static final byte[] TEST_MESSAGE =
- "Testing 1 2 3... Testing 1 2 3... Testing 1 2 3...".getBytes();
-
- private static final byte[] TEST_KEY_ID =
- { 0, 1, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
- // Not to be used when generating cross-platform test vectors (due to default charset encoding)
- private static final byte[] TEST_METADATA = "Some protocol metadata string goes here".getBytes();
- private static final byte[] TEST_ASSOCIATED_DATA = {
- 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11 };
- private static final byte[] ZERO_BYTE = { 0 };
- private static final byte[] EMPTY_BYTES = { };
-
- private static final List<byte[]> MESSAGE_VALUES = Arrays.asList(
- EMPTY_BYTES,
- TEST_MESSAGE
- );
-
- private static final List<byte[]> KEY_ID_VALUES = Arrays.asList(
- null,
- EMPTY_BYTES,
- TEST_KEY_ID);
-
- private static final List<byte[]> METADATA_VALUES = Arrays.asList(
- null,
- EMPTY_BYTES,
- TEST_METADATA
- );
-
- private static final List<byte[]> ASSOCIATED_DATA_VALUES = Arrays.asList(
- null,
- ZERO_BYTE,
- TEST_ASSOCIATED_DATA);
-
- private byte[] message;
- private byte[] metadata;
- private byte[] verificationKeyId;
- private byte[] decryptionKeyId;
- private byte[] associatedData;
- private PublicKey ecPublicKey;
- private PrivateKey ecPrivateKey;
- private PublicKey rsaPublicKey;
- private PrivateKey rsaPrivateKey;
- private SecretKey aesEncryptionKey;
- private SecretKey hmacKey;
- private SecureMessageBuilder secureMessageBuilder = new SecureMessageBuilder();
- private SecureRandom rng = new SecureRandom();
-
- @Override
- public void setUp() {
- message = TEST_MESSAGE;
- metadata = null;
- verificationKeyId = null;
- decryptionKeyId = null;
- associatedData = null;
- if (!PublicKeyProtoUtil.isLegacyCryptoRequired()) {
- KeyPair ecKeyPair = PublicKeyProtoUtil.generateEcP256KeyPair();
- ecPublicKey = ecKeyPair.getPublic();
- ecPrivateKey = ecKeyPair.getPrivate();
- }
- KeyPair rsaKeyPair = PublicKeyProtoUtil.generateRSA2048KeyPair();
- rsaPublicKey = rsaKeyPair.getPublic();
- rsaPrivateKey = rsaKeyPair.getPrivate();
- try {
- aesEncryptionKey = makeAesKey();
- hmacKey = makeAesKey();
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- fail();
- }
- secureMessageBuilder.reset();
- }
-
- private SecureMessage sign(SigType sigType) throws NoSuchAlgorithmException, InvalidKeyException {
- return getPreconfiguredBuilder().buildSignedCleartextMessage(
- getSigningKeyFor(sigType), sigType, message);
- }
-
- private SecureMessage signCrypt(SigType sigType, EncType encType)
- throws NoSuchAlgorithmException, InvalidKeyException {
- return getPreconfiguredBuilder().buildSignCryptedMessage(
- getSigningKeyFor(sigType), sigType, aesEncryptionKey, encType, message);
- }
-
- private void verify(SecureMessage signed, SigType sigType)
- throws NoSuchAlgorithmException, InvalidKeyException, SignatureException,
- InvalidProtocolBufferException {
- HeaderAndBody headerAndBody = SecureMessageParser.parseSignedCleartextMessage(
- signed,
- getVerificationKeyFor(sigType),
- sigType,
- associatedData);
- consistencyCheck(signed, headerAndBody, sigType, EncType.NONE);
- }
-
- private void verifyDecrypt(SecureMessage encryptedAndSigned, SigType sigType, EncType encType)
- throws InvalidProtocolBufferException, InvalidKeyException, NoSuchAlgorithmException,
- SignatureException {
- HeaderAndBody headerAndBody = SecureMessageParser.parseSignCryptedMessage(
- encryptedAndSigned,
- getVerificationKeyFor(sigType),
- sigType,
- aesEncryptionKey,
- encType,
- associatedData);
- consistencyCheck(encryptedAndSigned, headerAndBody, sigType, encType);
- }
-
- // A collection of different kinds of "alterations" that can be made to SecureMessage protos.
- enum Alteration {
- DECRYPTION_KEY_ID,
- ENCTYPE,
- HEADER_AND_BODY_PROTO,
- MESSAGE,
- METADATA,
- RESIGNCRYPTION_ATTACK,
- SIGTYPE,
- VERIFICATION_KEY_ID,
- ASSOCIATED_DATA_LENGTH,
- }
-
- private void doSignAndVerify(SigType sigType) throws Exception {
- System.out.println("BEGIN_TEST -- Testing SigType: " + sigType + " with:");
- System.out.println("VerificationKeyId: " + Arrays.toString(verificationKeyId));
- System.out.println("Metadata: " + Arrays.toString(metadata));
- System.out.println("AssociatedData: " + Arrays.toString(associatedData));
- System.out.println("Message: " + Arrays.toString(message));
- // Positive test cases
- SecureMessage signed = sign(sigType);
- verify(signed, sigType);
-
- // Negative test cases
- for (Alteration altType : getAlterationsToTest()) {
- System.out.println("Testing alteration: " + altType.toString());
- SecureMessage modified = modifyMessage(signed, altType);
- try {
- verify(modified, sigType);
- fail(altType.toString());
- } catch (SignatureException e) {
- // We expect this
- }
- }
-
- // Try verifying with the wrong associated data
- if ((associatedData == null) || (associatedData.length == 0)) {
- associatedData = ZERO_BYTE;
- } else {
- associatedData = null;
- }
- try {
- verify(signed, sigType);
- fail("Expected verification to fail due to incorrect associatedData");
- } catch (SignatureException e) {
- // We expect this
- }
-
- System.out.println("PASS_TEST -- Testing SigType: " + sigType);
- }
-
- private List<Alteration> getAlterationsToTest() {
- if (isRunningInAndroid()) {
- // Android is very slow. Only try one alteration attack, intead of all of them.
- int randomAlteration = Math.abs(rng.nextInt()) % Alteration.values().length;
- return Arrays.asList(Alteration.values()[randomAlteration]);
- } else {
- // Just try all of them
- return Arrays.asList(Alteration.values());
- }
- }
-
- private void doSignCryptAndVerifyDecrypt(SigType sigType) throws Exception {
- // For now, EncType is always AES_256_CBC
- EncType encType = EncType.AES_256_CBC;
- System.out.println("BEGIN_TEST -- Testing SigType: " + sigType
- + " EncType: " + encType + " with:");
- System.out.println("DecryptionKeyId: " + Arrays.toString(decryptionKeyId));
- System.out.println("VerificationKeyId: " + Arrays.toString(verificationKeyId));
- System.out.println("Metadata: " + Arrays.toString(metadata));
- System.out.println("AssociatedData: " + Arrays.toString(associatedData));
- System.out.println("Message: " + Arrays.toString(message));
- SecureMessage encryptedAndSigned = null;
- encryptedAndSigned = signCrypt(sigType, encType);
- verifyDecrypt(encryptedAndSigned, sigType, encType);
-
- // Negative test cases
- for (Alteration altType : getAlterationsToTest()) {
- if (skipAlterationTestFor(altType, sigType)) {
- System.out.println("Skipping alteration test: " + altType.toString());
- continue;
- }
-
- System.out.println("Testing alteration: " + altType.toString());
- SecureMessage modified = modifyMessage(encryptedAndSigned, altType);
- try {
- verifyDecrypt(modified, sigType, encType);
- fail();
- } catch (SignatureException e) {
- // We expect this
- }
- }
- System.out.println("PASS_TEST -- Testing SigType: " + sigType + " EncType: " + encType);
- }
-
- private boolean skipAlterationTestFor(Alteration altType, SigType sigType) {
- // The RESIGNCRYPTION_ATTACK may be allowed to succeed iff the same symmetric key
- // is being reused for both signature and encryption.
- return (altType == Alteration.RESIGNCRYPTION_ATTACK)
- // Intentionally testing equality of object address here
- && (getVerificationKeyFor(sigType) == aesEncryptionKey);
- }
-
- private SecureMessage modifyMessage(SecureMessage original, Alteration altType) throws Exception {
- ByteString bogus = ByteString.copyFromUtf8("BOGUS");
- HeaderAndBody origHAB = HeaderAndBody.parseFrom(original.getHeaderAndBody());
- HeaderAndBody.Builder newHAB = HeaderAndBody.newBuilder(origHAB);
- Header.Builder newHeader = Header.newBuilder(origHAB.getHeader());
- Header origHeader = origHAB.getHeader();
- SecureMessage.Builder result = SecureMessage.newBuilder(original);
- switch (altType) {
- case DECRYPTION_KEY_ID:
- if (origHeader.hasDecryptionKeyId()) {
- newHeader.clearDecryptionKeyId();
- } else {
- newHeader.setDecryptionKeyId(ByteString.copyFrom(TEST_KEY_ID));
- }
- break;
- case ENCTYPE:
- if (origHeader.getEncryptionScheme() == SecureMessageProto.EncScheme.NONE) {
- newHeader.setEncryptionScheme(SecureMessageProto.EncScheme.AES_256_CBC);
- } else {
- newHeader.setEncryptionScheme(SecureMessageProto.EncScheme.NONE);
- }
- break;
- case HEADER_AND_BODY_PROTO:
- // Substitute a junk byte string instead of the HeeaderAndBody proto message
- return result.setHeaderAndBody(bogus).build();
- case MESSAGE:
- byte[] origBody = origHAB.getBody().toByteArray();
- if (origBody.length > 0) {
- // Lop off trailing byte of the body
- byte[] truncatedBody = CryptoOps.subarray(origBody, 0, origBody.length - 1);
- newHAB.setBody(ByteString.copyFrom(truncatedBody));
- } else {
- newHAB.setBody(bogus);
- }
- break;
- case METADATA:
- if (origHeader.hasPublicMetadata()) {
- newHeader.clearPublicMetadata();
- } else {
- newHeader.setPublicMetadata(bogus);
- }
- break;
- case RESIGNCRYPTION_ATTACK:
- // Simulate stripping a signature, and re-signing a message to see if it will be decrypted.
- newHeader
- .setVerificationKeyId(bogus)
- // In case original was cleartext
- .setEncryptionScheme(SecureMessageProto.EncScheme.AES_256_CBC);
-
- // Now that we've mildly changed the header, compute a new signature for it.
- newHAB.setHeader(newHeader.build());
- byte[] headerAndBodyBytes = newHAB.build().toByteArray();
- result.setHeaderAndBody(ByteString.copyFrom(headerAndBodyBytes));
- SigType sigType = SigType.valueOf(origHeader.getSignatureScheme());
- // Note that in all cases where this attack applies, the associatedData is not normally
- // used directly inside the signature (but rather inside the inner ciphertext).
- result.setSignature(ByteString.copyFrom(CryptoOps.sign(
- sigType, getSigningKeyFor(sigType), rng, headerAndBodyBytes)));
- return result.build();
- case SIGTYPE:
- if (origHeader.getSignatureScheme() == SecureMessageProto.SigScheme.ECDSA_P256_SHA256) {
- newHeader
- .setSignatureScheme(SecureMessageProto.SigScheme.HMAC_SHA256);
- } else {
- newHeader
- .setSignatureScheme(SecureMessageProto.SigScheme.ECDSA_P256_SHA256);
- }
- break;
- case VERIFICATION_KEY_ID:
- if (origHeader.hasVerificationKeyId()) {
- newHeader.clearVerificationKeyId();
- } else {
- newHeader.setVerificationKeyId(
- ByteString.copyFrom(TEST_KEY_ID));
- }
- break;
- case ASSOCIATED_DATA_LENGTH:
- int adLength = origHeader.getAssociatedDataLength();
- switch (adLength) {
- case 0:
- newHeader.setAssociatedDataLength(1);
- break;
- case 1:
- newHeader.setAssociatedDataLength(0);
- break;
- default:
- newHeader.setAssociatedDataLength(adLength - 1);
- }
- break;
- default:
- fail("Forgot to implement an alteration attack: " + altType);
- break;
- }
- // Set the header.
- newHAB.setHeader(newHeader.build());
-
- return result.setHeaderAndBody(ByteString.copyFrom(newHAB.build().toByteArray()))
- .build();
- }
-
- public void testEcDsaSignedOnly() throws Exception {
- doTestSignedOnly(SigType.ECDSA_P256_SHA256);
- }
-
- public void testRsaSignedOnly() throws Exception {
- doTestSignedOnly(SigType.RSA2048_SHA256);
- }
-
- public void testHmacSignedOnly() throws Exception {
- doTestSignedOnly(SigType.HMAC_SHA256);
- }
-
- private void doTestSignedOnly(SigType sigType) throws Exception {
- if (isUnsupported(sigType)) {
- return;
- }
-
- // decryptionKeyId must be left null for signature-only operation
- for (byte[] vkId : KEY_ID_VALUES) {
- verificationKeyId = vkId;
- for (byte[] md : METADATA_VALUES) {
- metadata = md;
- for (byte[] ad : ASSOCIATED_DATA_VALUES) {
- associatedData = ad;
- for (byte[] msg : MESSAGE_VALUES) {
- message = msg;
- doSignAndVerify(sigType);
- }
- }
- }
- }
-
- // Test that use of a DecryptionKeyId is not allowed for signature-only
- try {
- decryptionKeyId = TEST_KEY_ID; // Should trigger a failure
- doSignAndVerify(sigType);
- fail();
- } catch (IllegalStateException expected) {
- }
-}
-
- public void testEncryptedAndMACed() throws Exception {
- for (byte[] dkId : KEY_ID_VALUES) {
- decryptionKeyId = dkId;
- for (byte[] vkId : KEY_ID_VALUES) {
- verificationKeyId = vkId;
- for (byte[] md : METADATA_VALUES) {
- metadata = md;
- for (byte[] ad : ASSOCIATED_DATA_VALUES) {
- associatedData = ad;
- for (byte[] msg : MESSAGE_VALUES) {
- message = msg;
- doSignCryptAndVerifyDecrypt(SigType.HMAC_SHA256);
- }
- }
- }
- }
- }
- }
-
- public void testEncryptedAndMACedWithSameKey() throws Exception {
- hmacKey = aesEncryptionKey; // Re-use the same key for both
- testEncryptedAndMACed();
- }
-
- public void testEncryptedAndEcdsaSigned() throws Exception {
- doTestEncryptedAndSigned(SigType.ECDSA_P256_SHA256);
- }
-
- public void testEncryptedAndRsaSigned() throws Exception {
- doTestEncryptedAndSigned(SigType.RSA2048_SHA256);
- }
-
- public void doTestEncryptedAndSigned(SigType sigType) throws Exception {
- if (isUnsupported(sigType)) {
- return; // EC operations aren't supported on older Android releases
- }
-
- for (byte[] dkId : KEY_ID_VALUES) {
- decryptionKeyId = dkId;
- for (byte[] vkId : KEY_ID_VALUES) {
- verificationKeyId = vkId;
- if ((verificationKeyId == null) && sigType.isPublicKeyScheme()) {
- continue; // Null verificationKeyId is not allowed with public key signcryption
- }
- for (byte[] md : METADATA_VALUES) {
- metadata = md;
- for (byte[] ad : ASSOCIATED_DATA_VALUES) {
- associatedData = ad;
- for (byte[] msg : MESSAGE_VALUES) {
- message = msg;
- doSignCryptAndVerifyDecrypt(sigType);
- }
- }
- }
- }
- }
-
- // Verify that a missing verificationKeyId is not allowed here
- try {
- verificationKeyId = null; // Should trigger a failure
- signCrypt(sigType, EncType.AES_256_CBC);
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testSignCryptionRequiresEncryption() throws Exception {
- try {
- signCrypt(SigType.RSA2048_SHA256, EncType.NONE);
- } catch (IllegalArgumentException expected) {
- }
- }
-
- public void testAssociatedData() throws Exception {
- // How much extra room might the encoding of AssociatedDataLength take up?
- int maxAssociatedDataOverheadBytes = 4;
- // How many bytes might normally vary in the encoding length for SecureMessages generated with
- // fresh randomness but identical contents (e.g., due to MSBs being 0)
- int maxJitter = 2;
- verificationKeyId = TEST_KEY_ID; // So that public key signcryption will work
- message = TEST_MESSAGE;
-
- for (SigType sigType : SigType.values()) {
- if (isUnsupported(sigType)) {
- continue;
- }
- associatedData = null;
- SecureMessage signed = sign(sigType);
- int signedLength = signed.toByteArray().length;
- associatedData = EMPTY_BYTES;
- // Check that EMPTY_BYTES is equivalent to null associated data under verification
- verify(signed, sigType);
- // We already tested that incorrect associated data fails elsewhere in negative test cases
- associatedData = TEST_ASSOCIATED_DATA;
- SecureMessage signedWithAssociatedData = sign(sigType);
- int signedWithAssociatedDataLength = signedWithAssociatedData.toByteArray().length;
- String logInfo = "Testing associated data overhead for signature using: " + sigType
- + " signedLength=" + signedLength
- + " signedWithAssociatedDataLength=" + signedWithAssociatedDataLength;
- System.out.println(logInfo);
- assertTrue(logInfo,
- signedWithAssociatedData.toByteArray().length
- <= signed.toByteArray().length + maxAssociatedDataOverheadBytes + maxJitter);
- }
-
- for (SigType sigType : SigType.values()) {
- if (isUnsupported(sigType)) {
- continue;
- }
- associatedData = null;
- SecureMessage signCrypted = signCrypt(sigType, EncType.AES_256_CBC);
- int signCryptedLength = signCrypted.toByteArray().length;
- // Check that EMPTY_BYTES is equivalent to null associated data under verification
- associatedData = EMPTY_BYTES;
- verifyDecrypt(signCrypted, sigType, EncType.AES_256_CBC);
- // We already tested that incorrect associated data fails elsewhere in negative test cases
- associatedData = TEST_ASSOCIATED_DATA;
- SecureMessage signCryptedWithAssociatedData = signCrypt(sigType, EncType.AES_256_CBC);
- int signCryptedWithAssociatedDataLength = signCryptedWithAssociatedData.toByteArray().length;
- String logInfo = "Testing associated data overhead for signcryption using: " + sigType
- + " signCryptedLength=" + signCryptedLength
- + " signCryptedWithAssociatedDataLength=" + signCryptedWithAssociatedDataLength;
- System.out.println(logInfo);
- assertTrue(logInfo,
- signCryptedWithAssociatedData.toByteArray().length
- <= signCrypted.toByteArray().length + maxAssociatedDataOverheadBytes + maxJitter);
- }
- }
-
- public void testEncryptedAndEcdsaSignedUsingPublicKeyProto() throws Exception {
- if (isUnsupported(SigType.ECDSA_P256_SHA256)) {
- return;
- }
-
- // Safest usage of SignCryption is to set the VerificationKeyId to an actual representation of
- // the verification key.
- verificationKeyId = PublicKeyProtoUtil.encodeEcPublicKey(ecPublicKey).toByteArray();
- SecureMessage encryptedAndSigned = signCrypt(SigType.ECDSA_P256_SHA256, EncType.AES_256_CBC);
-
- // Simulate extracting the verification key ID from the SecureMessage (non-standard usage)
- ecPublicKey =
- PublicKeyProtoUtil.parseEcPublicKey(
- EcP256PublicKey.parseFrom(
- SecureMessageParser.getUnverifiedHeader(encryptedAndSigned)
- .getVerificationKeyId()));
-
- // Note that this verification uses the encoded/decoded ecPublicKey value
- verifyDecrypt(encryptedAndSigned, SigType.ECDSA_P256_SHA256, EncType.AES_256_CBC);
- }
-
- public void testEncryptedAndRsaSignedUsingPublicKeyProto() throws Exception {
- // Safest usage of SignCryption is to set the VerificationKeyId to an actual representation of
- // the verification key.
- verificationKeyId = PublicKeyProtoUtil.encodeRsa2048PublicKey(rsaPublicKey).toByteArray();
- SecureMessage encryptedAndSigned = signCrypt(SigType.RSA2048_SHA256, EncType.AES_256_CBC);
-
- // Simulate extracting the verification key ID from the SecureMessage (non-standard usage)
- rsaPublicKey =
- PublicKeyProtoUtil.parseRsa2048PublicKey(
- SimpleRsaPublicKey.parseFrom(
- SecureMessageParser.getUnverifiedHeader(encryptedAndSigned)
- .getVerificationKeyId()));
-
- // Note that this verification uses the encoded/decoded SimpleRsaPublicKey value
- verifyDecrypt(encryptedAndSigned, SigType.RSA2048_SHA256, EncType.AES_256_CBC);
- }
-
- // TODO(shabsi): The test was only corrupting header but wasn't setting the body. With protolite,
- // not setting a required field causes problems. Modify the SecureMessageParser test and
- // enable/remove this test.
- /*
- public void testCorruptUnverifiedHeader() throws Exception {
- // Create a sample message
- SecureMessage original = signCrypt(SigType.HMAC_SHA256, EncType.AES_256_CBC);
- HeaderAndBody originalHAB = HeaderAndBody.parseFrom(original.getHeaderAndBody().toByteArray());
- for (CorruptHeaderType corruptionType : CorruptHeaderType.values()) {
- // Mess with the HeaderAndBody field
- HeaderAndBody.Builder corruptHAB = HeaderAndBody.newBuilder(originalHAB);
- try {
- corruptHeaderWith(corruptionType, corruptHAB);
- // Construct the corrupted message using the modified HeaderAndBody
- SecureMessage.Builder corrupt = SecureMessage.newBuilder(original);
- corrupt.setHeaderAndBody(ByteString.copyFrom(corruptHAB.build().toByteArray())).build();
- SecureMessageParser.getUnverifiedHeader(corrupt.build());
- fail("Corrupt header type " + corruptionType + " parsed without error");
- } catch (InvalidProtocolBufferException expected) {
- }
- }
- }
- */
-
- public void testParseEmptyMessage() throws Exception {
- byte[] bogusData = new byte[0];
-
- try {
- SecureMessageParser.parseSignedCleartextMessage(
- SecureMessage.parseFrom(bogusData),
- aesEncryptionKey,
- SigType.HMAC_SHA256);
- fail("Empty message verified without error");
- } catch (SignatureException | UninitializedMessageException
- | InvalidProtocolBufferException expected) {
- }
- }
-
- public void testParseKeyInvalidInputs() throws Exception {
- GenericPublicKey[] badKeys = new GenericPublicKey[] {
- GenericPublicKey.newBuilder().setType(SecureMessageProto.PublicKeyType.EC_P256).build(),
- GenericPublicKey.newBuilder().setType(SecureMessageProto.PublicKeyType.RSA2048).build(),
- GenericPublicKey.newBuilder().setType(SecureMessageProto.PublicKeyType.DH2048_MODP).build(),
- };
- for (int i = 0; i < badKeys.length; i++) {
- GenericPublicKey key = badKeys[i];
- try {
- PublicKeyProtoUtil.parsePublicKey(key);
- fail(String.format("%sth key was parsed without exceptions", i));
- } catch (InvalidKeySpecException expected) {
- }
- }
- }
-
- enum CorruptHeaderType {
- EMPTY,
- // TODO(shabsi): Remove these test cases and modify code in SecureMessageParser appropriately.
- // UNSET,
- // JUNK,
- }
-
- private void corruptHeaderWith(CorruptHeaderType corruptionType,
- HeaderAndBody.Builder protoToModify) {
- switch (corruptionType) {
- case EMPTY:
- protoToModify.setHeader(Header.getDefaultInstance());
- break;
- /*
- case JUNK:
- Header.Builder junk = Header.newBuilder();
- junk.setDecryptionKeyId(ByteString.copyFromUtf8("fooooo"));
- junk.setIv(ByteString.copyFromUtf8("bar"));
- // Don't set signature scheme.
- junk.setVerificationKeyId(ByteString.copyFromUtf8("bazzzzz"));
- protoToModify.setHeader(junk.build());
- break;
- case UNSET:
- protoToModify.clearHeader();
- break;
- */
- default:
- throw new RuntimeException("Broken test code");
- }
- }
-
- private void consistencyCheck(
- SecureMessage secmsg, HeaderAndBody headerAndBody, SigType sigType, EncType encType)
- throws InvalidProtocolBufferException {
- Header header = SecureMessageParser.getUnverifiedHeader(secmsg);
- checkHeader(header, sigType, encType); // Checks that the "unverified header" looks right
- checkHeaderAndBody(header, headerAndBody); // Matches header vs. the "verified" headerAndBody
- }
-
- private Header checkHeader(Header header, SigType sigType, EncType encType) {
- assertEquals(sigType.getSigScheme(), header.getSignatureScheme());
- assertEquals(encType.getEncScheme(), header.getEncryptionScheme());
- checkKeyIdsAndMetadata(verificationKeyId, decryptionKeyId, metadata, associatedData, header);
- return header;
- }
-
- private void checkHeaderAndBody(Header header, HeaderAndBody headerAndBody) {
- assertTrue(header.equals(headerAndBody.getHeader()));
- assertTrue(Arrays.equals(message, headerAndBody.getBody().toByteArray()));
- }
-
- private void checkKeyIdsAndMetadata(byte[] verificationKeyId, byte[] decryptionKeyId,
- byte[] metadata, byte[] associatedData, Header header) {
- if (verificationKeyId == null) {
- assertFalse(header.hasVerificationKeyId());
- } else {
- assertTrue(Arrays.equals(verificationKeyId, header.getVerificationKeyId().toByteArray()));
- }
- if (decryptionKeyId == null) {
- assertFalse(header.hasDecryptionKeyId());
- } else {
- assertTrue(Arrays.equals(decryptionKeyId, header.getDecryptionKeyId().toByteArray()));
- }
- if (metadata == null) {
- assertFalse(header.hasPublicMetadata());
- } else {
- assertTrue(Arrays.equals(metadata, header.getPublicMetadata().toByteArray()));
- }
- if (associatedData == null) {
- assertFalse(header.hasAssociatedDataLength());
- } else {
- assertEquals(associatedData.length, header.getAssociatedDataLength());
- }
- }
-
- private SecretKey makeAesKey() throws NoSuchAlgorithmException {
- KeyGenerator aesKeygen = KeyGenerator.getInstance("AES");
- aesKeygen.init(256);
- return aesKeygen.generateKey();
- }
-
- private Key getSigningKeyFor(SigType sigType) {
- if (sigType == SigType.ECDSA_P256_SHA256) {
- return ecPrivateKey;
- }
- if (sigType == SigType.RSA2048_SHA256) {
- return rsaPrivateKey;
- }
- if (sigType == SigType.HMAC_SHA256) {
- return hmacKey;
- }
- return null; // This should not happen
- }
-
- private Key getVerificationKeyFor(SigType sigType) {
- try {
- if (sigType == SigType.ECDSA_P256_SHA256) {
- return PublicKeyProtoUtil.parseEcPublicKey(
- PublicKeyProtoUtil.encodeEcPublicKey(ecPublicKey));
- }
- if (sigType == SigType.RSA2048_SHA256) {
- return PublicKeyProtoUtil.parseRsa2048PublicKey(
- PublicKeyProtoUtil.encodeRsa2048PublicKey(rsaPublicKey));
- }
- } catch (InvalidKeySpecException e) {
- throw new AssertionError(e);
- }
-
- assertFalse(sigType.isPublicKeyScheme());
- // For symmetric key schemes
- return getSigningKeyFor(sigType);
- }
-
- private SecureMessageBuilder getPreconfiguredBuilder() {
- // Re-use a single instance of SecureMessageBuilder for efficiency.
- SecureMessageBuilder builder = secureMessageBuilder.reset();
- if (verificationKeyId != null) {
- builder.setVerificationKeyId(verificationKeyId);
- }
- if (decryptionKeyId != null) {
- builder.setDecryptionKeyId(decryptionKeyId);
- }
- if (metadata != null) {
- builder.setPublicMetadata(metadata);
- }
- if (associatedData != null) {
- builder.setAssociatedData(associatedData);
- }
- return builder;
- }
-
- private static boolean isUnsupported(SigType sigType) {
- // EC operations aren't supported on older Android releases
- return PublicKeyProtoUtil.isLegacyCryptoRequired()
- && (sigType == SigType.ECDSA_P256_SHA256);
- }
-
- private static boolean isRunningInAndroid() {
- try {
- ClassLoader.getSystemClassLoader().loadClass("android.os.Build$VERSION");
- return true;
- } catch (ClassNotFoundException e) {
- // Not running on Android
- return false;
- }
- }
-}
diff --git a/src/main/proto/CMakeLists.txt b/src/main/proto/CMakeLists.txt
deleted file mode 100644
index cd94f3f..0000000
--- a/src/main/proto/CMakeLists.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright 2020 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
-#
-# https://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.
-
-add_cc_proto_library(
- proto_device_to_device_messages_cc_proto
- PROTOS device_to_device_messages.proto
- DEPS proto_securemessage_cc_proto
- INCS ${CMAKE_CURRENT_BINARY_DIR}/..
-)
-
-add_cc_proto_library(
- proto_securegcm_cc_proto
- PROTOS securegcm.proto
- INCS ${CMAKE_CURRENT_BINARY_DIR}/..
-)
-
-add_cc_proto_library(
- proto_ukey_cc_proto
- PROTOS ukey.proto
- INCS ${CMAKE_CURRENT_BINARY_DIR}/..
-)
diff --git a/src/main/proto/device_to_device_messages.proto b/src/main/proto/device_to_device_messages.proto
index 5600373..c3bd2cf 100644
--- a/src/main/proto/device_to_device_messages.proto
+++ b/src/main/proto/device_to_device_messages.proto
@@ -1,28 +1,27 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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.
+ */
syntax = "proto2";
package securegcm;
-import "securemessage.proto";
-
-option optimize_for = LITE_RUNTIME;
option java_package = "com.google.security.cryptauth.lib.securegcm";
option java_outer_classname = "DeviceToDeviceMessagesProto";
option objc_class_prefix = "SGCM";
+import "securemessage.proto";
+
// Used by protocols between devices
message DeviceToDeviceMessage {
// the payload of the message
@@ -53,7 +52,9 @@ message ResponderHello {
}
// Type of curve
-enum Curve { ED_25519 = 1; }
+enum Curve {
+ ED_25519 = 1;
+}
// A convenience proto for encoding curve points in affine representation
message EcPoint {
@@ -79,3 +80,4 @@ message SpakeHandshakeMessage {
// since the key exchange is already complete on the sender's side.
optional bytes payload = 4;
}
+
diff --git a/src/main/proto/passwordless_auth_payloads.proto b/src/main/proto/passwordless_auth_payloads.proto
index 69c2784..054d91c 100644
--- a/src/main/proto/passwordless_auth_payloads.proto
+++ b/src/main/proto/passwordless_auth_payloads.proto
@@ -1,27 +1,28 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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.
+ */
syntax = "proto2";
package securegcm;
-option optimize_for = LITE_RUNTIME;
option java_package = "com.google.security.cryptauth.lib.securegcm";
option java_outer_classname = "SecureGcmPasswordlessAuthProto";
option objc_class_prefix = "SGCM";
+
message IdentityAssertion {
+
// Browser data contains the challenge, origin, etc.
optional bytes browser_data_hash = 1;
diff --git a/src/main/proto/proximity_payloads.proto b/src/main/proto/proximity_payloads.proto
index d7a4956..9c2f1ec 100644
--- a/src/main/proto/proximity_payloads.proto
+++ b/src/main/proto/proximity_payloads.proto
@@ -1,34 +1,33 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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.
+ */
syntax = "proto2";
-package securegcm;
-
import "securegcm.proto";
-option optimize_for = LITE_RUNTIME;
+package securegcm;
+
option java_package = "com.google.security.cryptauth.lib.securegcm";
option java_outer_classname = "SecureGcmProximityAuthProto";
option objc_class_prefix = "SGCM";
-// Message used when one device wants to initiate a Proximity Auth pairing with
-// another device DEPRECATED. DO NOT USE
+// Message used when one device wants to initiate a Proximity Auth pairing with another device
+// DEPRECATED. DO NOT USE
message CloudToDeviceProximityAuthPairing {
- // The name or description of the device that wants to pair with another
- // personal device of the user. This is a string that may be shown to the
- // user or may be kept in logs.
+// The name or description of the device that wants to pair with another
+// personal device of the user. This is a string that may be shown to the
+// user or may be kept in logs.
optional string initiating_device_name = 1;
// The original device's Bluetooth address in human readable form
diff --git a/src/main/proto/securegcm.proto b/src/main/proto/securegcm.proto
index 0325f06..b7dae25 100644
--- a/src/main/proto/securegcm.proto
+++ b/src/main/proto/securegcm.proto
@@ -1,22 +1,21 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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.
+ */
syntax = "proto2";
package securegcm;
-option optimize_for = LITE_RUNTIME;
option java_package = "com.google.security.cryptauth.lib.securegcm";
option java_outer_classname = "SecureGcmProto";
option objc_class_prefix = "SGCM";
@@ -37,7 +36,7 @@ message GcmDeviceInfo {
optional bytes apn_registration_id = 202;
// Does the user have notifications enabled for the given device address.
- optional bool notification_enabled = 203 [default = true];
+ optional bool notification_enabled = 203 [ default = true ];
// Used for device_address of DeviceInfo field 2, a Bluetooth Mac address for
// the device (e.g., to be used with EasyUnlock)
@@ -105,45 +104,45 @@ message GcmDeviceInfo {
optional string device_manufacturer = 31;
// Used to indicate which type of device this is.
- optional DeviceType device_type = 32 [default = ANDROID];
+ optional DeviceType device_type = 32 [ default = ANDROID ];
// Fields corresponding to screenlock type/features and hardware features
// should be numbered in the 400 range.
// Is this device using a secure screenlock (e.g., pattern or pin unlock)
- optional bool using_secure_screenlock = 400 [default = false];
+ optional bool using_secure_screenlock = 400 [ default = false ];
// Is auto-unlocking the screenlock (e.g., when at "home") supported?
- optional bool auto_unlock_screenlock_supported = 401 [default = false];
+ optional bool auto_unlock_screenlock_supported = 401 [ default = false ];
// Is auto-unlocking the screenlock (e.g., when at "home") enabled?
- optional bool auto_unlock_screenlock_enabled = 402 [default = false];
+ optional bool auto_unlock_screenlock_enabled = 402 [ default = false ];
// Does the device have a Bluetooth (classic) radio?
- optional bool bluetooth_radio_supported = 403 [default = false];
+ optional bool bluetooth_radio_supported = 403 [ default = false ];
// Is the Bluetooth (classic) radio on?
- optional bool bluetooth_radio_enabled = 404 [default = false];
+ optional bool bluetooth_radio_enabled = 404 [ default = false ];
// Does the device hardware support a mobile data connection?
- optional bool mobile_data_supported = 405 [default = false];
+ optional bool mobile_data_supported = 405 [ default = false ];
// Does the device support tethering?
- optional bool tethering_supported = 406 [default = false];
+ optional bool tethering_supported = 406 [ default = false ];
// Does the device have a BLE radio?
- optional bool ble_radio_supported = 407 [default = false];
+ optional bool ble_radio_supported = 407 [ default = false ];
// Is the device a "Pixel Experience" Android device?
- optional bool pixel_experience = 408 [default = false];
+ optional bool pixel_experience = 408 [ default = false ];
// Is the device running in the ARC++ container on a chromebook?
- optional bool arc_plus_plus = 409 [default = false];
+ optional bool arc_plus_plus = 409 [ default = false ];
// Is the value set in |using_secure_screenlock| reliable? On some Android
// devices, the platform API to get the screenlock state is not trustworthy.
// See b/32212161.
- optional bool is_screenlock_state_flaky = 410 [default = false];
+ optional bool is_screenlock_state_flaky = 410 [ default = false ];
// A list of multi-device software features supported by the device.
repeated SoftwareFeature supported_software_features = 411;
@@ -179,7 +178,7 @@ enum DeviceType {
OSX = 5;
}
-// MultiDevice features which may be supported and enabled on a device. See
+// MultiDevice features which may be supported and enabled on a device.
enum SoftwareFeature {
UNKNOWN_FEATURE = 0;
BETTER_TOGETHER_HOST = 1;
diff --git a/src/main/proto/securemessage.proto b/src/main/proto/securemessage.proto
index 5118d35..062e425 100644
--- a/src/main/proto/securemessage.proto
+++ b/src/main/proto/securemessage.proto
@@ -1,24 +1,22 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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.
+ */
// Proto definitions for SecureMessage format
syntax = "proto2";
package securemessage;
-
-option optimize_for = LITE_RUNTIME;
option java_package = "com.google.security.cryptauth.lib.securemessage";
option java_outer_classname = "SecureMessageProto";
option objc_class_prefix = "SMSG";
@@ -58,7 +56,7 @@ message Header {
optional bytes public_metadata = 6;
// The length of some associated data this is not sent in this SecureMessage,
// but which will be bound to the signature.
- optional uint32 associated_data_length = 7 [default = 0];
+ optional uint32 associated_data_length = 7 [ default = 0 ];
}
message HeaderAndBody {
diff --git a/src/main/proto/ukey.proto b/src/main/proto/ukey.proto
index 327d8d3..155b279 100644
--- a/src/main/proto/ukey.proto
+++ b/src/main/proto/ukey.proto
@@ -1,22 +1,21 @@
-// Copyright 2020 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
-//
-// https://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.
-
+/* Copyright 2018 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
+ *
+ * https://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.
+ */
syntax = "proto2";
package securegcm;
-option optimize_for = LITE_RUNTIME;
option java_package = "com.google.security.cryptauth.lib.securegcm";
option java_outer_classname = "UkeyProto";
@@ -37,12 +36,12 @@ message Ukey2Message {
message Ukey2Alert {
enum AlertType {
// Framing errors
- BAD_MESSAGE = 1; // The message could not be deserialized
- BAD_MESSAGE_TYPE = 2; // message_type has an undefined value
- INCORRECT_MESSAGE = 3; // message_type received does not correspond to
- // expected type at this stage of the protocol
- BAD_MESSAGE_DATA = 4; // Could not deserialize message_data as per
- // value inmessage_type
+ BAD_MESSAGE = 1; // The message could not be deserialized
+ BAD_MESSAGE_TYPE = 2; // message_type has an undefined value
+ INCORRECT_MESSAGE = 3; // message_type received does not correspond to
+ // expected type at this stage of the protocol
+ BAD_MESSAGE_DATA = 4; // Could not deserialize message_data as per
+ // value inmessage_type
// ClientInit and ServerInit errors
BAD_VERSION = 100; // version is invalid; server cannot find
@@ -55,9 +54,9 @@ message Ukey2Alert {
BAD_PUBLIC_KEY = 104; // The public key could not be parsed
// Other errors
- INTERNAL_ERROR = 200; // An internal error has occurred. error_message
- // may contain additional details for logging
- // and debugging.
+ INTERNAL_ERROR = 200; // An internal error has occurred. error_message
+ // may contain additional details for logging
+ // and debugging.
}
optional AlertType type = 1;
diff --git a/third_party/absl b/third_party/absl
deleted file mode 160000
-Subproject 62f05b1f57ad660e9c09e02ce7d591dcc4d0ca0
diff --git a/third_party/gtest b/third_party/gtest
deleted file mode 160000
-Subproject 703bd9caab50b139428cea1aaff9974ebee5742
diff --git a/third_party/protobuf b/third_party/protobuf
deleted file mode 160000
-Subproject d0bfd5221182da1a7cc280f3337b5e41a89539c
diff --git a/third_party/secure_message b/third_party/secure_message
deleted file mode 160000
-Subproject e7b6988454bc94601616fbbf0db3559f73a1ebd