summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorErin Yan <yiranyan@google.com>2020-11-05 10:37:43 -0800
committerErin Yan <yiranyan@google.com>2020-11-09 10:04:38 -0800
commit1182deea1e4656a8cda0da2da1f3898e777c573c (patch)
tree1dc34eee126c3e4d9384526199eb4650fc04ad17
parentd0f5ee5f7446e510636f03a2f3cbe1fe7128f552 (diff)
downloadukey2-1182deea1e4656a8cda0da2da1f3898e777c573c.tar.gz
Update ukey2
This update is being pulled in from github https://github.com/google/ukey2 This update only includes java lib Bug: 172576615 Test: Build Change-Id: I7ee4375b7ed3c3908ec6672f529a88aa6080efb9
-rw-r--r--.gitignore3
-rw-r--r--README.md65
-rw-r--r--build.gradle28
-rw-r--r--src/.gradle/3.2.1/taskArtifacts/fileSnapshots.binbin18577 -> 0 bytes
-rw-r--r--src/.gradle/3.2.1/taskArtifacts/taskArtifacts.binbin18786 -> 0 bytes
-rw-r--r--src/.gradle/3.2.1/taskArtifacts/taskArtifacts.lockbin17 -> 0 bytes
-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/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
30 files changed, 576 insertions, 1053 deletions
diff --git a/.gitignore b/.gitignore
index 9d4d4f8..a676aea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
build/**
.gradle/**
+.idea/**
+.gitignore
+.gitmodules
diff --git a/README.md b/README.md
index 2f56370..87a652d 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,9 @@ This is not an officially supported Google product
**Coathored by:** Alexei Czeskis, Thai Duong, Eduardo' Vela'' \<Nava\>, and Adam Stubblefield.
-**Status:** Implemented in Java (aczeskis@google.com)
+**Status:**
+Implemented in Java by Alexei Czeskis (aczeskis@google.com)
+Ported from Java to C++ by Tim Song (tengs@google.com)
**Design reviewers:** Thai Duong, Bruno Blanchet, Martin Abadi, and Bo Wang
@@ -327,3 +329,64 @@ 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 bb46e75..8989eb0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,23 +1,35 @@
+// 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 {
- jcenter()
+ mavenCentral()
}
buildscript {
repositories {
- jcenter()
+ mavenCentral()
}
dependencies {
- classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.7'
+ classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.12'
}
}
dependencies {
- compile "com.google.code.findbugs:jsr305:3.0.0"
- compile "com.google.protobuf:protobuf-java:3.4.0"
- compile "com.google.guava:guava:r05"
+ implementation "com.google.code.findbugs:jsr305:3.0.0"
+ implementation "com.google.protobuf:protobuf-java:3.8.0"
+ implementation "com.google.guava:guava:19.0"
}
-
-
diff --git a/src/.gradle/3.2.1/taskArtifacts/fileSnapshots.bin b/src/.gradle/3.2.1/taskArtifacts/fileSnapshots.bin
deleted file mode 100644
index f0cc1a8..0000000
--- a/src/.gradle/3.2.1/taskArtifacts/fileSnapshots.bin
+++ /dev/null
Binary files differ
diff --git a/src/.gradle/3.2.1/taskArtifacts/taskArtifacts.bin b/src/.gradle/3.2.1/taskArtifacts/taskArtifacts.bin
deleted file mode 100644
index b1f5426..0000000
--- a/src/.gradle/3.2.1/taskArtifacts/taskArtifacts.bin
+++ /dev/null
Binary files differ
diff --git a/src/.gradle/3.2.1/taskArtifacts/taskArtifacts.lock b/src/.gradle/3.2.1/taskArtifacts/taskArtifacts.lock
deleted file mode 100644
index 6fa2a90..0000000
--- a/src/.gradle/3.2.1/taskArtifacts/taskArtifacts.lock
+++ /dev/null
Binary files differ
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 09023a5..fb4af63 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 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.
- */
+// 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.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 92aa02d..d0efa44 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 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.
- */
+// 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 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 4b5fb5e..1566849 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 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.
- */
+// 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 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 48dc52f..a7203d1 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 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.
- */
+// 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.annotations.VisibleForTesting;
@@ -55,6 +55,11 @@ 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() { }
@@ -196,7 +201,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 ae43d90..f929a3a 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 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.
- */
+// 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.InvalidProtocolBufferException;
@@ -64,11 +64,6 @@ 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;
@@ -176,8 +171,10 @@ public class D2DDiffieHellmanKeyExchangeHandshake implements D2DHandshakeContext
responderEncodeKey = masterKey;
break;
case D2DConnectionContextV1.PROTOCOL_VERSION:
- initiatorEncodeKey = D2DCryptoOps.deriveNewKeyForPurpose(masterKey, INITIATOR_PURPOSE);
- responderEncodeKey = D2DCryptoOps.deriveNewKeyForPurpose(masterKey, RESPONDER_PURPOSE);
+ initiatorEncodeKey = D2DCryptoOps.deriveNewKeyForPurpose(masterKey,
+ D2DCryptoOps.INITIATOR_PURPOSE);
+ responderEncodeKey = D2DCryptoOps.deriveNewKeyForPurpose(masterKey,
+ D2DCryptoOps.RESPONDER_PURPOSE);
break;
default:
throw new IllegalStateException("Unexpected protocol version: " + protocolVersionToUse);
@@ -238,8 +235,10 @@ public class D2DDiffieHellmanKeyExchangeHandshake implements D2DHandshakeContext
initiatorEncodeKey = masterKey;
responderEncodeKey = masterKey;
} else {
- initiatorEncodeKey = D2DCryptoOps.deriveNewKeyForPurpose(masterKey, INITIATOR_PURPOSE);
- responderEncodeKey = D2DCryptoOps.deriveNewKeyForPurpose(masterKey, RESPONDER_PURPOSE);
+ initiatorEncodeKey = D2DCryptoOps.deriveNewKeyForPurpose(masterKey,
+ D2DCryptoOps.INITIATOR_PURPOSE);
+ responderEncodeKey = D2DCryptoOps.deriveNewKeyForPurpose(masterKey,
+ D2DCryptoOps.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 8c35d22..5fc1d7b 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 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.
- */
+// 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;
/**
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
deleted file mode 100644
index da5abf1..0000000
--- a/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DSpakeEd25519Handshake.java
+++ /dev/null
@@ -1,648 +0,0 @@
-/* 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 2ea2563..454b942 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,24 +1,23 @@
-/* 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.
- */
+// 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 java.math.BigInteger.ONE;
import static java.math.BigInteger.ZERO;
import com.google.common.annotations.VisibleForTesting;
-
import java.math.BigInteger;
/**
@@ -208,20 +207,20 @@ public class Ed25519 {
};
}
- /**
- * Converts a point in affine representation to extended representation
- */
- @VisibleForTesting
+ /** 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
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
- */
- @VisibleForTesting
+ /** 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
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 328cb53..450c806 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 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.
- */
+// 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.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 bb5fffc..b717eb6 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 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.
- */
+// 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;
/**
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 9690a89..67e4ace 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 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.
- */
+// 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.InvalidProtocolBufferException;
@@ -58,6 +58,10 @@ 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);
@@ -71,6 +75,11 @@ 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 8482628..a69431f 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 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.
- */
+// 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;
/**
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 a22edc4..b053bf4 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 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.
- */
+// 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.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 0d01c9a..8e00ea9 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 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.
- */
+// 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;
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 1e3b196..876bd93 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 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.
- */
+// 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.security.annotations.SuppressInsecureCipherModeCheckerReviewed;
@@ -132,7 +132,6 @@ 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}.
@@ -412,10 +411,31 @@ 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();
}
- return hkdfSha256Expand(hkdfSha256Extract(inputKeyMaterial, salt), info);
+ if (length < 0) {
+ throw new IllegalArgumentException("Length must be positive");
+ }
+ return hkdfSha256Expand(hkdfSha256Extract(inputKeyMaterial, salt), info, length);
}
/**
@@ -464,12 +484,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[])
- * @return HMAC-SHA256(salt, inputKeyMaterial) (salt is the "key" for the HMAC)
+ * @see #hkdfSha256Expand(byte[], byte[], int)
+ * @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
*/
@@ -491,15 +511,15 @@ public class CryptoOps {
}
/**
- * Special case of HKDF (RFC 5869) expansion function, using the SHA-256 hash function and
- * allowing for a maximum output length of 256 bits.
+ * HKDF (RFC 5869) expansion function, using the SHA-256 hash function.
*
- * @param pseudoRandomKey should be generated by {@link #hkdfSha256Expand(byte[], byte[])}
+ * @param pseudoRandomKey should be generated by {@link #hkdfSha256Extract(SecretKey, 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)
+ private static byte[] hkdfSha256Expand(byte[] pseudoRandomKey, byte[] info, int length)
throws NoSuchAlgorithmException {
Mac macScheme = Mac.getInstance("HmacSHA256");
try {
@@ -507,11 +527,38 @@ public class CryptoOps {
} catch (InvalidKeyException e) {
throw new AssertionError(e); // This should never happen
}
- // 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);
+
+ // 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);
}
}
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 ab97cca..0c593fe 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,19 +1,20 @@
-/* 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.
- */
+// 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.collect.Lists;
import com.google.protobuf.ByteString;
import com.google.security.annotations.SuppressInsecureCipherModeCheckerPendingReview;
import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.DhPublicKey;
@@ -194,6 +195,37 @@ 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 f59ce4e..f1a9464 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 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.
- */
+// 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;
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 e1c60e3..d634d40 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 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.
- */
+// 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;
diff --git a/src/main/proto/device_to_device_messages.proto b/src/main/proto/device_to_device_messages.proto
index c3bd2cf..5600373 100644
--- a/src/main/proto/device_to_device_messages.proto
+++ b/src/main/proto/device_to_device_messages.proto
@@ -1,27 +1,28 @@
-/* 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.
- */
+// 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.
+
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
@@ -52,9 +53,7 @@ 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 {
@@ -80,4 +79,3 @@ 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 054d91c..69c2784 100644
--- a/src/main/proto/passwordless_auth_payloads.proto
+++ b/src/main/proto/passwordless_auth_payloads.proto
@@ -1,28 +1,27 @@
-/* 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.
- */
+// 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.
+
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 9c2f1ec..d7a4956 100644
--- a/src/main/proto/proximity_payloads.proto
+++ b/src/main/proto/proximity_payloads.proto
@@ -1,33 +1,34 @@
-/* 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";
+// 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.
-import "securegcm.proto";
+syntax = "proto2";
package securegcm;
+import "securegcm.proto";
+
+option optimize_for = LITE_RUNTIME;
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 b7dae25..0325f06 100644
--- a/src/main/proto/securegcm.proto
+++ b/src/main/proto/securegcm.proto
@@ -1,21 +1,22 @@
-/* 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.
- */
+// 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.
+
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";
@@ -36,7 +37,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)
@@ -104,45 +105,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;
@@ -178,7 +179,7 @@ enum DeviceType {
OSX = 5;
}
-// MultiDevice features which may be supported and enabled on a device.
+// MultiDevice features which may be supported and enabled on a device. See
enum SoftwareFeature {
UNKNOWN_FEATURE = 0;
BETTER_TOGETHER_HOST = 1;
diff --git a/src/main/proto/securemessage.proto b/src/main/proto/securemessage.proto
index 062e425..5118d35 100644
--- a/src/main/proto/securemessage.proto
+++ b/src/main/proto/securemessage.proto
@@ -1,22 +1,24 @@
-/* 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.
- */
+// 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.
+
// 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";
@@ -56,7 +58,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 155b279..327d8d3 100644
--- a/src/main/proto/ukey.proto
+++ b/src/main/proto/ukey.proto
@@ -1,21 +1,22 @@
-/* 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.
- */
+// 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.
+
syntax = "proto2";
package securegcm;
+option optimize_for = LITE_RUNTIME;
option java_package = "com.google.security.cryptauth.lib.securegcm";
option java_outer_classname = "UkeyProto";
@@ -36,12 +37,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
@@ -54,9 +55,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;