From 60800e155612bea797eed93c67046a23d26054cc Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Mon, 24 Nov 2014 12:54:30 -0800 Subject: [PATCH] Break out into separate repo. --- .gitignore | 3 + README.md | 85 + build.gradle | 44 + jni/Android.mk | 27 + jni/Application.mk | 1 + jni/curve25519-donna.c | 870 ++ jni/curve25519-donna.h | 6 + jni/curve25519-jni.c | 109 + jni/ed25519/additions/compare.c | 44 + jni/ed25519/additions/compare.h | 6 + jni/ed25519/additions/crypto_hash_sha512.h | 6 + jni/ed25519/additions/curve_sigs.c | 116 + jni/ed25519/additions/curve_sigs.h | 50 + jni/ed25519/additions/sign_modified.c | 47 + jni/ed25519/additions/zeroize.c | 17 + jni/ed25519/additions/zeroize.h | 12 + jni/ed25519/api.h | 4 + jni/ed25519/base.h | 1344 ++ jni/ed25519/base2.h | 40 + jni/ed25519/d.h | 1 + jni/ed25519/d2.h | 1 + jni/ed25519/fe.h | 56 + jni/ed25519/fe_0.c | 19 + jni/ed25519/fe_1.c | 19 + jni/ed25519/fe_add.c | 57 + jni/ed25519/fe_cmov.c | 63 + jni/ed25519/fe_copy.c | 29 + jni/ed25519/fe_frombytes.c | 73 + jni/ed25519/fe_invert.c | 14 + jni/ed25519/fe_isnegative.c | 16 + jni/ed25519/fe_isnonzero.c | 19 + jni/ed25519/fe_mul.c | 253 + jni/ed25519/fe_neg.c | 45 + jni/ed25519/fe_pow22523.c | 13 + jni/ed25519/fe_sq.c | 149 + jni/ed25519/fe_sq2.c | 160 + jni/ed25519/fe_sub.c | 57 + jni/ed25519/fe_tobytes.c | 119 + jni/ed25519/ge.h | 95 + jni/ed25519/ge_add.c | 11 + jni/ed25519/ge_add.h | 97 + jni/ed25519/ge_double_scalarmult.c | 96 + jni/ed25519/ge_frombytes.c | 50 + jni/ed25519/ge_madd.c | 11 + jni/ed25519/ge_madd.h | 88 + jni/ed25519/ge_msub.c | 11 + jni/ed25519/ge_msub.h | 88 + jni/ed25519/ge_p1p1_to_p2.c | 12 + jni/ed25519/ge_p1p1_to_p3.c | 13 + jni/ed25519/ge_p2_0.c | 8 + jni/ed25519/ge_p2_dbl.c | 11 + jni/ed25519/ge_p2_dbl.h | 73 + jni/ed25519/ge_p3_0.c | 9 + jni/ed25519/ge_p3_dbl.c | 12 + jni/ed25519/ge_p3_to_cached.c | 17 + jni/ed25519/ge_p3_to_p2.c | 12 + jni/ed25519/ge_p3_tobytes.c | 14 + jni/ed25519/ge_precomp_0.c | 8 + jni/ed25519/ge_scalarmult_base.c | 105 + jni/ed25519/ge_sub.c | 11 + jni/ed25519/ge_sub.h | 97 + jni/ed25519/ge_tobytes.c | 14 + jni/ed25519/main/main.c | 106 + jni/ed25519/nacl_includes/crypto_int32.h | 6 + jni/ed25519/nacl_includes/crypto_int64.h | 6 + jni/ed25519/nacl_includes/crypto_sign.h | 16 + .../crypto_sign_edwards25519sha512batch.h | 33 + jni/ed25519/nacl_includes/crypto_uint32.h | 6 + jni/ed25519/nacl_includes/crypto_uint64.h | 6 + jni/ed25519/nacl_includes/crypto_verify_32.h | 22 + jni/ed25519/nacl_sha512/blocks.c | 238 + jni/ed25519/nacl_sha512/hash.c | 71 + jni/ed25519/open.c | 48 + jni/ed25519/pow22523.h | 160 + jni/ed25519/pow225521.h | 160 + jni/ed25519/sc.h | 15 + jni/ed25519/sc_muladd.c | 368 + jni/ed25519/sc_reduce.c | 275 + jni/ed25519/sign.c | 41 + jni/ed25519/sqrtm1.h | 1 + libs/armeabi-v7a/libcurve25519.so | Bin 0 -> 135228 bytes libs/armeabi/libcurve25519.so | Bin 0 -> 131128 bytes libs/mips/libcurve25519.so | Bin 0 -> 235636 bytes libs/x86/libcurve25519.so | Bin 0 -> 163904 bytes protobuf/LocalStorageProtocol.proto | 112 + protobuf/Makefile | 3 + protobuf/WhisperTextProtocol.proto | 41 + .../test/InMemoryAxolotlStore.java | 115 + .../test/InMemoryIdentityKeyStore.java | 53 + .../test/InMemoryPreKeyStore.java | 42 + .../test/InMemorySessionStore.java | 68 + .../test/InMemorySignedPreKeyStore.java | 59 + .../test/SessionBuilderTest.java | 601 + .../test/SessionCipherTest.java | 206 + .../test/SimultaneousInitiateTests.java | 501 + .../test/ecc/Curve25519Test.java | 154 + .../test/groups/GroupCipherTest.java | 151 + .../test/groups/InMemorySenderKeyStore.java | 33 + .../org/whispersystems/test/kdf/HKDFTest.java | 139 + .../test/ratchet/ChainKeyTest.java | 59 + .../test/ratchet/RatchetingSessionTest.java | 236 + .../test/ratchet/RootKeyTest.java | 84 + src/main/AndroidManifest.xml | 7 + .../libaxolotl/DuplicateMessageException.java | 7 + .../libaxolotl/IdentityKey.java | 66 + .../libaxolotl/IdentityKeyPair.java | 66 + .../libaxolotl/InvalidKeyException.java | 34 + .../libaxolotl/InvalidKeyIdException.java | 27 + .../libaxolotl/InvalidMacException.java | 28 + .../libaxolotl/InvalidMessageException.java | 40 + .../libaxolotl/InvalidVersionException.java | 23 + .../libaxolotl/LegacyMessageException.java | 7 + .../libaxolotl/NoSessionException.java | 11 + .../libaxolotl/SessionBuilder.java | 411 + .../libaxolotl/SessionCipher.java | 469 + .../libaxolotl/StaleKeyExchangeException.java | 4 + .../UntrustedIdentityException.java | 4 + .../whispersystems/libaxolotl/ecc/Curve.java | 78 + .../libaxolotl/ecc/Curve25519.java | 98 + .../libaxolotl/ecc/DjbECPrivateKey.java | 41 + .../libaxolotl/ecc/DjbECPublicKey.java | 66 + .../libaxolotl/ecc/ECKeyPair.java | 37 + .../libaxolotl/ecc/ECPrivateKey.java | 23 + .../libaxolotl/ecc/ECPublicKey.java | 27 + .../libaxolotl/groups/GroupCipher.java | 146 + .../groups/GroupSessionBuilder.java | 38 + .../groups/ratchet/SenderChainKey.java | 49 + .../groups/ratchet/SenderMessageKey.java | 38 + .../groups/state/SenderKeyRecord.java | 64 + .../groups/state/SenderKeyState.java | 144 + .../groups/state/SenderKeyStore.java | 6 + .../libaxolotl/kdf/DerivedMessageSecrets.java | 61 + .../libaxolotl/kdf/DerivedRootSecrets.java | 26 + .../whispersystems/libaxolotl/kdf/HKDF.java | 93 + .../whispersystems/libaxolotl/kdf/HKDFv2.java | 8 + .../whispersystems/libaxolotl/kdf/HKDFv3.java | 8 + .../protocol/CiphertextMessage.java | 35 + .../protocol/KeyExchangeMessage.java | 153 + .../protocol/PreKeyWhisperMessage.java | 147 + .../SenderKeyDistributionMessage.java | 56 + .../libaxolotl/protocol/SenderKeyMessage.java | 121 + .../libaxolotl/protocol/WhisperMessage.java | 172 + .../libaxolotl/protocol/WhisperProtos.java | 3532 +++++ .../ratchet/AliceAxolotlParameters.java | 109 + .../ratchet/BobAxolotlParameters.java | 109 + .../libaxolotl/ratchet/ChainKey.java | 75 + .../libaxolotl/ratchet/MessageKeys.java | 51 + .../libaxolotl/ratchet/RatchetingSession.java | 179 + .../libaxolotl/ratchet/RootKey.java | 54 + .../ratchet/SymmetricAxolotlParameters.java | 108 + .../libaxolotl/state/AxolotlStore.java | 6 + .../libaxolotl/state/IdentityKeyStore.java | 57 + .../libaxolotl/state/PreKeyBundle.java | 96 + .../libaxolotl/state/PreKeyRecord.java | 51 + .../libaxolotl/state/PreKeyStore.java | 42 + .../libaxolotl/state/SessionRecord.java | 117 + .../libaxolotl/state/SessionState.java | 509 + .../libaxolotl/state/SessionStore.java | 68 + .../libaxolotl/state/SignedPreKeyRecord.java | 61 + .../libaxolotl/state/SignedPreKeyStore.java | 47 + .../libaxolotl/state/StorageProtos.java | 11779 ++++++++++++++++ .../libaxolotl/util/ByteUtil.java | 248 + .../whispersystems/libaxolotl/util/Hex.java | 77 + .../libaxolotl/util/KeyHelper.java | 143 + .../libaxolotl/util/Medium.java | 5 + .../whispersystems/libaxolotl/util/Pair.java | 51 + .../libaxolotl/util/guava/Absent.java | 87 + .../libaxolotl/util/guava/Function.java | 61 + .../libaxolotl/util/guava/Optional.java | 232 + .../libaxolotl/util/guava/Preconditions.java | 447 + .../libaxolotl/util/guava/Present.java | 88 + .../libaxolotl/util/guava/Supplier.java | 36 + 172 files changed, 30656 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 build.gradle create mode 100644 jni/Android.mk create mode 100644 jni/Application.mk create mode 100644 jni/curve25519-donna.c create mode 100644 jni/curve25519-donna.h create mode 100644 jni/curve25519-jni.c create mode 100644 jni/ed25519/additions/compare.c create mode 100644 jni/ed25519/additions/compare.h create mode 100644 jni/ed25519/additions/crypto_hash_sha512.h create mode 100644 jni/ed25519/additions/curve_sigs.c create mode 100644 jni/ed25519/additions/curve_sigs.h create mode 100644 jni/ed25519/additions/sign_modified.c create mode 100644 jni/ed25519/additions/zeroize.c create mode 100644 jni/ed25519/additions/zeroize.h create mode 100644 jni/ed25519/api.h create mode 100644 jni/ed25519/base.h create mode 100644 jni/ed25519/base2.h create mode 100644 jni/ed25519/d.h create mode 100644 jni/ed25519/d2.h create mode 100644 jni/ed25519/fe.h create mode 100644 jni/ed25519/fe_0.c create mode 100644 jni/ed25519/fe_1.c create mode 100644 jni/ed25519/fe_add.c create mode 100644 jni/ed25519/fe_cmov.c create mode 100644 jni/ed25519/fe_copy.c create mode 100644 jni/ed25519/fe_frombytes.c create mode 100644 jni/ed25519/fe_invert.c create mode 100644 jni/ed25519/fe_isnegative.c create mode 100644 jni/ed25519/fe_isnonzero.c create mode 100644 jni/ed25519/fe_mul.c create mode 100644 jni/ed25519/fe_neg.c create mode 100644 jni/ed25519/fe_pow22523.c create mode 100644 jni/ed25519/fe_sq.c create mode 100644 jni/ed25519/fe_sq2.c create mode 100644 jni/ed25519/fe_sub.c create mode 100644 jni/ed25519/fe_tobytes.c create mode 100644 jni/ed25519/ge.h create mode 100644 jni/ed25519/ge_add.c create mode 100644 jni/ed25519/ge_add.h create mode 100644 jni/ed25519/ge_double_scalarmult.c create mode 100644 jni/ed25519/ge_frombytes.c create mode 100644 jni/ed25519/ge_madd.c create mode 100644 jni/ed25519/ge_madd.h create mode 100644 jni/ed25519/ge_msub.c create mode 100644 jni/ed25519/ge_msub.h create mode 100644 jni/ed25519/ge_p1p1_to_p2.c create mode 100644 jni/ed25519/ge_p1p1_to_p3.c create mode 100644 jni/ed25519/ge_p2_0.c create mode 100644 jni/ed25519/ge_p2_dbl.c create mode 100644 jni/ed25519/ge_p2_dbl.h create mode 100644 jni/ed25519/ge_p3_0.c create mode 100644 jni/ed25519/ge_p3_dbl.c create mode 100644 jni/ed25519/ge_p3_to_cached.c create mode 100644 jni/ed25519/ge_p3_to_p2.c create mode 100644 jni/ed25519/ge_p3_tobytes.c create mode 100644 jni/ed25519/ge_precomp_0.c create mode 100644 jni/ed25519/ge_scalarmult_base.c create mode 100644 jni/ed25519/ge_sub.c create mode 100644 jni/ed25519/ge_sub.h create mode 100644 jni/ed25519/ge_tobytes.c create mode 100644 jni/ed25519/main/main.c create mode 100644 jni/ed25519/nacl_includes/crypto_int32.h create mode 100644 jni/ed25519/nacl_includes/crypto_int64.h create mode 100644 jni/ed25519/nacl_includes/crypto_sign.h create mode 100644 jni/ed25519/nacl_includes/crypto_sign_edwards25519sha512batch.h create mode 100644 jni/ed25519/nacl_includes/crypto_uint32.h create mode 100644 jni/ed25519/nacl_includes/crypto_uint64.h create mode 100644 jni/ed25519/nacl_includes/crypto_verify_32.h create mode 100644 jni/ed25519/nacl_sha512/blocks.c create mode 100644 jni/ed25519/nacl_sha512/hash.c create mode 100644 jni/ed25519/open.c create mode 100644 jni/ed25519/pow22523.h create mode 100644 jni/ed25519/pow225521.h create mode 100644 jni/ed25519/sc.h create mode 100644 jni/ed25519/sc_muladd.c create mode 100644 jni/ed25519/sc_reduce.c create mode 100644 jni/ed25519/sign.c create mode 100644 jni/ed25519/sqrtm1.h create mode 100755 libs/armeabi-v7a/libcurve25519.so create mode 100755 libs/armeabi/libcurve25519.so create mode 100755 libs/mips/libcurve25519.so create mode 100755 libs/x86/libcurve25519.so create mode 100644 protobuf/LocalStorageProtocol.proto create mode 100644 protobuf/Makefile create mode 100644 protobuf/WhisperTextProtocol.proto create mode 100644 src/androidTest/java/org/whispersystems/test/InMemoryAxolotlStore.java create mode 100644 src/androidTest/java/org/whispersystems/test/InMemoryIdentityKeyStore.java create mode 100644 src/androidTest/java/org/whispersystems/test/InMemoryPreKeyStore.java create mode 100644 src/androidTest/java/org/whispersystems/test/InMemorySessionStore.java create mode 100644 src/androidTest/java/org/whispersystems/test/InMemorySignedPreKeyStore.java create mode 100644 src/androidTest/java/org/whispersystems/test/SessionBuilderTest.java create mode 100644 src/androidTest/java/org/whispersystems/test/SessionCipherTest.java create mode 100644 src/androidTest/java/org/whispersystems/test/SimultaneousInitiateTests.java create mode 100644 src/androidTest/java/org/whispersystems/test/ecc/Curve25519Test.java create mode 100644 src/androidTest/java/org/whispersystems/test/groups/GroupCipherTest.java create mode 100644 src/androidTest/java/org/whispersystems/test/groups/InMemorySenderKeyStore.java create mode 100644 src/androidTest/java/org/whispersystems/test/kdf/HKDFTest.java create mode 100644 src/androidTest/java/org/whispersystems/test/ratchet/ChainKeyTest.java create mode 100644 src/androidTest/java/org/whispersystems/test/ratchet/RatchetingSessionTest.java create mode 100644 src/androidTest/java/org/whispersystems/test/ratchet/RootKeyTest.java create mode 100644 src/main/AndroidManifest.xml create mode 100644 src/main/java/org/whispersystems/libaxolotl/DuplicateMessageException.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/IdentityKey.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/IdentityKeyPair.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/InvalidKeyException.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/InvalidKeyIdException.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/InvalidMacException.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/InvalidMessageException.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/InvalidVersionException.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/LegacyMessageException.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/NoSessionException.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/SessionBuilder.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/SessionCipher.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/StaleKeyExchangeException.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/UntrustedIdentityException.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/ecc/Curve.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/ecc/Curve25519.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/ecc/DjbECPrivateKey.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/ecc/DjbECPublicKey.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/ecc/ECKeyPair.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/ecc/ECPrivateKey.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/ecc/ECPublicKey.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/groups/GroupCipher.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/groups/GroupSessionBuilder.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/groups/ratchet/SenderChainKey.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/groups/ratchet/SenderMessageKey.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyRecord.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyState.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyStore.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/kdf/DerivedMessageSecrets.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/kdf/DerivedRootSecrets.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/kdf/HKDF.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/kdf/HKDFv2.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/kdf/HKDFv3.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/protocol/CiphertextMessage.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/protocol/KeyExchangeMessage.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/protocol/PreKeyWhisperMessage.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyDistributionMessage.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyMessage.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/protocol/WhisperMessage.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/protocol/WhisperProtos.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/ratchet/AliceAxolotlParameters.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/ratchet/BobAxolotlParameters.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/ratchet/ChainKey.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/ratchet/MessageKeys.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/ratchet/RatchetingSession.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/ratchet/RootKey.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/ratchet/SymmetricAxolotlParameters.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/state/AxolotlStore.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/state/IdentityKeyStore.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/state/PreKeyBundle.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/state/PreKeyRecord.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/state/PreKeyStore.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/state/SessionRecord.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/state/SessionState.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/state/SessionStore.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/state/SignedPreKeyRecord.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/state/SignedPreKeyStore.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/state/StorageProtos.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/ByteUtil.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/Hex.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/KeyHelper.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/Medium.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/Pair.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/guava/Absent.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/guava/Function.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/guava/Optional.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/guava/Preconditions.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/guava/Present.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/util/guava/Supplier.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..8c22aafa0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/build +/obj +*.iml diff --git a/README.md b/README.md new file mode 100644 index 000000000..9a3828c08 --- /dev/null +++ b/README.md @@ -0,0 +1,85 @@ + +# Overview + +This is a ratcheting forward secrecy protocol that works in synchronous and asynchronous messaging +environments. The protocol overview is available [here](https://github.com/trevp/axolotl/wiki), +and the details of the wire format are available [here](https://github.com/WhisperSystems/TextSecure/wiki/ProtocolV2). + +## PreKeys + +This protocol uses a concept called 'PreKeys'. A PreKey is an ECPublicKey and an associated unique +ID which are stored together by a server. PreKeys can also be signed. + +At install time, clients generate a single signed PreKey, as well as a large list of unsigned +PreKeys, and transmit all of them to the server. + +## Sessions + +The axolotl protocol is session-oriented. Clients establish a "session," which is then used for +all subsequent encrypt/decrypt operations. There is no need to ever tear down a session once one +has been established. + +Sessions are established in one of three ways: + +1. PreKeyBundles. A client that wishes to send a message to a recipient can establish a session by + retrieving a PreKeyBundle for that recipient from the server. +1. PreKeyWhisperMessages. A client can receive a PreKeyWhisperMessage from a recipient and use it + to establish a session. +1. KeyExchangeMessages. Two clients can exchange KeyExchange messages to establish a session. + +## State + +An established session encapsulates a lot of state between two clients. That state is maintained +in durable records which need to be kept for the life of the session. + +State is kept in the following places: + +1. Identity State. Clients will need to maintain the state of their own identity key pair, as well + as identity keys received from other clients. +1. PreKey State. Clients will need to maintain the state of their generated PreKeys. +1. Signed PreKey States. Clients will need to maintain the state of their signed PreKeys. +1. Session State. Clients will need to maintain the state of the sessions they have established. + +# Using libaxolotl + +## Install time + +At install time, a libaxolotl client needs to generate its identity keys, registration id, and +prekeys. + + IdentityKeyPair identityKeyPair = KeyHelper.generateIdentityKeyPair(); + int registrationId = KeyHelper.generateRegistrationId(); + List preKeys = KeyHelper.generatePreKeys(startId, 100); + PreKeyRecord lastResortKey = KeyHelper.generateLastResortKey(); + SignedPreKeyRecord signedPreKey = KeyHelper.generateSignedPreKey(identityKeyPair, 5); + + // Store identityKeyPair somewhere durable and safe. + // Store registrationId somewhere durable and safe. + + // Store preKeys in PreKeyStore. + // Store signed prekey in SignedPreKeyStore. + +## Building a session + +A libaxolotl client needs to implement four interfaces: IdentityKeyStore, PreKeyStore, +SignedPreKeyStore, and SessionStore. These will manage loading and storing of identity, +prekeys, signed prekeys, and session state. + +Once those are implemented, building a session is fairly straightforward: + + SessionStore sessionStore = new MySessionStore(); + PreKeyStore preKeyStore = new MyPreKeyStore(); + SignedPreKeyStore signedPreKeyStore = new MySignedPreKeyStore(); + IdentityKeyStore identityStore = new MyIdentityKeyStore(); + + // Instantiate a SessionBuilder for a remote recipientId + deviceId tuple. + SessionBuilder sessionBuilder = new SessionBuilder(sessionStore, preKeyStore, signedPreKeyStore, + identityStore, recipientId, deviceId); + + // Build a session with a PreKey retrieved from the server. + sessionBuilder.process(retrievedPreKey); + + SessionCipher sessionCipher = new SessionCipher(sessionStore, recipientId, deviceId); + CiphertextMessage message = sessionCipher.encrypt("Hello world!".getBytes("UTF-8")); + + deliver(message.serialize()); diff --git a/build.gradle b/build.gradle new file mode 100644 index 000000000..2b623a3fd --- /dev/null +++ b/build.gradle @@ -0,0 +1,44 @@ +buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:0.14.2' + } +} + +apply plugin: 'com.android.library' + +repositories { + mavenCentral() +} + +dependencies { + compile 'com.google.protobuf:protobuf-java:2.5.0' +} + +android { + compileSdkVersion 21 + buildToolsVersion '21.1.1' + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } + + android { + sourceSets { + main { + jniLibs.srcDirs = ['libs'] + } + } + } + +} + +tasks.whenTaskAdded { task -> + if (task.name.equals("lint")) { + task.enabled = false + } +} \ No newline at end of file diff --git a/jni/Android.mk b/jni/Android.mk new file mode 100644 index 000000000..fea39cf36 --- /dev/null +++ b/jni/Android.mk @@ -0,0 +1,27 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libcurve25519-donna +LOCAL_SRC_FILES := curve25519-donna.c + +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libcurve25519-ref10 +LOCAL_SRC_FILES := $(wildcard ed25519/*.c) $(wildcard ed25519/additions/*.c) $(wildcard ed25519/nacl_sha512/*.c) +LOCAL_C_INCLUDES := ed25519/nacl_includes ed25519/additions ed25519/sha512 ed25519 + +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libcurve25519 +LOCAL_SRC_FILES := curve25519-jni.c +LOCAL_C_INCLUDES := ed25519/additions + +LOCAL_STATIC_LIBRARIES := libcurve25519-donna libcurve25519-ref10 + +include $(BUILD_SHARED_LIBRARY) + diff --git a/jni/Application.mk b/jni/Application.mk new file mode 100644 index 000000000..e942d8e62 --- /dev/null +++ b/jni/Application.mk @@ -0,0 +1 @@ +APP_ABI := armeabi armeabi-v7a x86 mips diff --git a/jni/curve25519-donna.c b/jni/curve25519-donna.c new file mode 100644 index 000000000..f2c2ac58e --- /dev/null +++ b/jni/curve25519-donna.c @@ -0,0 +1,870 @@ +/* Copyright 2008, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * curve25519-donna: Curve25519 elliptic curve, public key function + * + * http://code.google.com/p/curve25519-donna/ + * + * Adam Langley + * + * Derived from public domain C code by Daniel J. Bernstein + * + * More information about curve25519 can be found here + * http://cr.yp.to/ecdh.html + * + * djb's sample implementation of curve25519 is written in a special assembly + * language called qhasm and uses the floating point registers. + * + * This is, almost, a clean room reimplementation from the curve25519 paper. It + * uses many of the tricks described therein. Only the crecip function is taken + * from the sample implementation. */ + +#include +#include + +#ifdef _MSC_VER +#define inline __inline +#endif + +typedef uint8_t u8; +typedef int32_t s32; +typedef int64_t limb; + +/* Field element representation: + * + * Field elements are written as an array of signed, 64-bit limbs, least + * significant first. The value of the field element is: + * x[0] + 2^26·x[1] + x^51·x[2] + 2^102·x[3] + ... + * + * i.e. the limbs are 26, 25, 26, 25, ... bits wide. */ + +/* Sum two numbers: output += in */ +static void fsum(limb *output, const limb *in) { + unsigned i; + for (i = 0; i < 10; i += 2) { + output[0+i] = output[0+i] + in[0+i]; + output[1+i] = output[1+i] + in[1+i]; + } +} + +/* Find the difference of two numbers: output = in - output + * (note the order of the arguments!). */ +static void fdifference(limb *output, const limb *in) { + unsigned i; + for (i = 0; i < 10; ++i) { + output[i] = in[i] - output[i]; + } +} + +/* Multiply a number by a scalar: output = in * scalar */ +static void fscalar_product(limb *output, const limb *in, const limb scalar) { + unsigned i; + for (i = 0; i < 10; ++i) { + output[i] = in[i] * scalar; + } +} + +/* Multiply two numbers: output = in2 * in + * + * output must be distinct to both inputs. The inputs are reduced coefficient + * form, the output is not. + * + * output[x] <= 14 * the largest product of the input limbs. */ +static void fproduct(limb *output, const limb *in2, const limb *in) { + output[0] = ((limb) ((s32) in2[0])) * ((s32) in[0]); + output[1] = ((limb) ((s32) in2[0])) * ((s32) in[1]) + + ((limb) ((s32) in2[1])) * ((s32) in[0]); + output[2] = 2 * ((limb) ((s32) in2[1])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[2]) + + ((limb) ((s32) in2[2])) * ((s32) in[0]); + output[3] = ((limb) ((s32) in2[1])) * ((s32) in[2]) + + ((limb) ((s32) in2[2])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[3]) + + ((limb) ((s32) in2[3])) * ((s32) in[0]); + output[4] = ((limb) ((s32) in2[2])) * ((s32) in[2]) + + 2 * (((limb) ((s32) in2[1])) * ((s32) in[3]) + + ((limb) ((s32) in2[3])) * ((s32) in[1])) + + ((limb) ((s32) in2[0])) * ((s32) in[4]) + + ((limb) ((s32) in2[4])) * ((s32) in[0]); + output[5] = ((limb) ((s32) in2[2])) * ((s32) in[3]) + + ((limb) ((s32) in2[3])) * ((s32) in[2]) + + ((limb) ((s32) in2[1])) * ((s32) in[4]) + + ((limb) ((s32) in2[4])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[0]); + output[6] = 2 * (((limb) ((s32) in2[3])) * ((s32) in[3]) + + ((limb) ((s32) in2[1])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[1])) + + ((limb) ((s32) in2[2])) * ((s32) in[4]) + + ((limb) ((s32) in2[4])) * ((s32) in[2]) + + ((limb) ((s32) in2[0])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[0]); + output[7] = ((limb) ((s32) in2[3])) * ((s32) in[4]) + + ((limb) ((s32) in2[4])) * ((s32) in[3]) + + ((limb) ((s32) in2[2])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[2]) + + ((limb) ((s32) in2[1])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[0]); + output[8] = ((limb) ((s32) in2[4])) * ((s32) in[4]) + + 2 * (((limb) ((s32) in2[3])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[3]) + + ((limb) ((s32) in2[1])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[1])) + + ((limb) ((s32) in2[2])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[2]) + + ((limb) ((s32) in2[0])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[0]); + output[9] = ((limb) ((s32) in2[4])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[4]) + + ((limb) ((s32) in2[3])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[3]) + + ((limb) ((s32) in2[2])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[2]) + + ((limb) ((s32) in2[1])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[0]); + output[10] = 2 * (((limb) ((s32) in2[5])) * ((s32) in[5]) + + ((limb) ((s32) in2[3])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[3]) + + ((limb) ((s32) in2[1])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[1])) + + ((limb) ((s32) in2[4])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[4]) + + ((limb) ((s32) in2[2])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[2]); + output[11] = ((limb) ((s32) in2[5])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[5]) + + ((limb) ((s32) in2[4])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[4]) + + ((limb) ((s32) in2[3])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[3]) + + ((limb) ((s32) in2[2])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[2]); + output[12] = ((limb) ((s32) in2[6])) * ((s32) in[6]) + + 2 * (((limb) ((s32) in2[5])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[5]) + + ((limb) ((s32) in2[3])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[3])) + + ((limb) ((s32) in2[4])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[4]); + output[13] = ((limb) ((s32) in2[6])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[6]) + + ((limb) ((s32) in2[5])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[5]) + + ((limb) ((s32) in2[4])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[4]); + output[14] = 2 * (((limb) ((s32) in2[7])) * ((s32) in[7]) + + ((limb) ((s32) in2[5])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[5])) + + ((limb) ((s32) in2[6])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[6]); + output[15] = ((limb) ((s32) in2[7])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[7]) + + ((limb) ((s32) in2[6])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[6]); + output[16] = ((limb) ((s32) in2[8])) * ((s32) in[8]) + + 2 * (((limb) ((s32) in2[7])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[7])); + output[17] = ((limb) ((s32) in2[8])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[8]); + output[18] = 2 * ((limb) ((s32) in2[9])) * ((s32) in[9]); +} + +/* Reduce a long form to a short form by taking the input mod 2^255 - 19. + * + * On entry: |output[i]| < 14*2^54 + * On exit: |output[0..8]| < 280*2^54 */ +static void freduce_degree(limb *output) { + /* Each of these shifts and adds ends up multiplying the value by 19. + * + * For output[0..8], the absolute entry value is < 14*2^54 and we add, at + * most, 19*14*2^54 thus, on exit, |output[0..8]| < 280*2^54. */ + output[8] += output[18] << 4; + output[8] += output[18] << 1; + output[8] += output[18]; + output[7] += output[17] << 4; + output[7] += output[17] << 1; + output[7] += output[17]; + output[6] += output[16] << 4; + output[6] += output[16] << 1; + output[6] += output[16]; + output[5] += output[15] << 4; + output[5] += output[15] << 1; + output[5] += output[15]; + output[4] += output[14] << 4; + output[4] += output[14] << 1; + output[4] += output[14]; + output[3] += output[13] << 4; + output[3] += output[13] << 1; + output[3] += output[13]; + output[2] += output[12] << 4; + output[2] += output[12] << 1; + output[2] += output[12]; + output[1] += output[11] << 4; + output[1] += output[11] << 1; + output[1] += output[11]; + output[0] += output[10] << 4; + output[0] += output[10] << 1; + output[0] += output[10]; +} + +#if (-1 & 3) != 3 +#error "This code only works on a two's complement system" +#endif + +/* return v / 2^26, using only shifts and adds. + * + * On entry: v can take any value. */ +static inline limb +div_by_2_26(const limb v) +{ + /* High word of v; no shift needed. */ + const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32); + /* Set to all 1s if v was negative; else set to 0s. */ + const int32_t sign = ((int32_t) highword) >> 31; + /* Set to 0x3ffffff if v was negative; else set to 0. */ + const int32_t roundoff = ((uint32_t) sign) >> 6; + /* Should return v / (1<<26) */ + return (v + roundoff) >> 26; +} + +/* return v / (2^25), using only shifts and adds. + * + * On entry: v can take any value. */ +static inline limb +div_by_2_25(const limb v) +{ + /* High word of v; no shift needed*/ + const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32); + /* Set to all 1s if v was negative; else set to 0s. */ + const int32_t sign = ((int32_t) highword) >> 31; + /* Set to 0x1ffffff if v was negative; else set to 0. */ + const int32_t roundoff = ((uint32_t) sign) >> 7; + /* Should return v / (1<<25) */ + return (v + roundoff) >> 25; +} + +/* return v / (2^25), using only shifts and adds. + * + * On entry: v can take any value. */ +static inline s32 +div_s32_by_2_25(const s32 v) +{ + const s32 roundoff = ((uint32_t)(v >> 31)) >> 7; + return (v + roundoff) >> 25; +} + +/* Reduce all coefficients of the short form input so that |x| < 2^26. + * + * On entry: |output[i]| < 280*2^54 */ +static void freduce_coefficients(limb *output) { + unsigned i; + + output[10] = 0; + + for (i = 0; i < 10; i += 2) { + limb over = div_by_2_26(output[i]); + /* The entry condition (that |output[i]| < 280*2^54) means that over is, at + * most, 280*2^28 in the first iteration of this loop. This is added to the + * next limb and we can approximate the resulting bound of that limb by + * 281*2^54. */ + output[i] -= over << 26; + output[i+1] += over; + + /* For the first iteration, |output[i+1]| < 281*2^54, thus |over| < + * 281*2^29. When this is added to the next limb, the resulting bound can + * be approximated as 281*2^54. + * + * For subsequent iterations of the loop, 281*2^54 remains a conservative + * bound and no overflow occurs. */ + over = div_by_2_25(output[i+1]); + output[i+1] -= over << 25; + output[i+2] += over; + } + /* Now |output[10]| < 281*2^29 and all other coefficients are reduced. */ + output[0] += output[10] << 4; + output[0] += output[10] << 1; + output[0] += output[10]; + + output[10] = 0; + + /* Now output[1..9] are reduced, and |output[0]| < 2^26 + 19*281*2^29 + * So |over| will be no more than 2^16. */ + { + limb over = div_by_2_26(output[0]); + output[0] -= over << 26; + output[1] += over; + } + + /* Now output[0,2..9] are reduced, and |output[1]| < 2^25 + 2^16 < 2^26. The + * bound on |output[1]| is sufficient to meet our needs. */ +} + +/* A helpful wrapper around fproduct: output = in * in2. + * + * On entry: |in[i]| < 2^27 and |in2[i]| < 2^27. + * + * output must be distinct to both inputs. The output is reduced degree + * (indeed, one need only provide storage for 10 limbs) and |output[i]| < 2^26. */ +static void +fmul(limb *output, const limb *in, const limb *in2) { + limb t[19]; + fproduct(t, in, in2); + /* |t[i]| < 14*2^54 */ + freduce_degree(t); + freduce_coefficients(t); + /* |t[i]| < 2^26 */ + memcpy(output, t, sizeof(limb) * 10); +} + +/* Square a number: output = in**2 + * + * output must be distinct from the input. The inputs are reduced coefficient + * form, the output is not. + * + * output[x] <= 14 * the largest product of the input limbs. */ +static void fsquare_inner(limb *output, const limb *in) { + output[0] = ((limb) ((s32) in[0])) * ((s32) in[0]); + output[1] = 2 * ((limb) ((s32) in[0])) * ((s32) in[1]); + output[2] = 2 * (((limb) ((s32) in[1])) * ((s32) in[1]) + + ((limb) ((s32) in[0])) * ((s32) in[2])); + output[3] = 2 * (((limb) ((s32) in[1])) * ((s32) in[2]) + + ((limb) ((s32) in[0])) * ((s32) in[3])); + output[4] = ((limb) ((s32) in[2])) * ((s32) in[2]) + + 4 * ((limb) ((s32) in[1])) * ((s32) in[3]) + + 2 * ((limb) ((s32) in[0])) * ((s32) in[4]); + output[5] = 2 * (((limb) ((s32) in[2])) * ((s32) in[3]) + + ((limb) ((s32) in[1])) * ((s32) in[4]) + + ((limb) ((s32) in[0])) * ((s32) in[5])); + output[6] = 2 * (((limb) ((s32) in[3])) * ((s32) in[3]) + + ((limb) ((s32) in[2])) * ((s32) in[4]) + + ((limb) ((s32) in[0])) * ((s32) in[6]) + + 2 * ((limb) ((s32) in[1])) * ((s32) in[5])); + output[7] = 2 * (((limb) ((s32) in[3])) * ((s32) in[4]) + + ((limb) ((s32) in[2])) * ((s32) in[5]) + + ((limb) ((s32) in[1])) * ((s32) in[6]) + + ((limb) ((s32) in[0])) * ((s32) in[7])); + output[8] = ((limb) ((s32) in[4])) * ((s32) in[4]) + + 2 * (((limb) ((s32) in[2])) * ((s32) in[6]) + + ((limb) ((s32) in[0])) * ((s32) in[8]) + + 2 * (((limb) ((s32) in[1])) * ((s32) in[7]) + + ((limb) ((s32) in[3])) * ((s32) in[5]))); + output[9] = 2 * (((limb) ((s32) in[4])) * ((s32) in[5]) + + ((limb) ((s32) in[3])) * ((s32) in[6]) + + ((limb) ((s32) in[2])) * ((s32) in[7]) + + ((limb) ((s32) in[1])) * ((s32) in[8]) + + ((limb) ((s32) in[0])) * ((s32) in[9])); + output[10] = 2 * (((limb) ((s32) in[5])) * ((s32) in[5]) + + ((limb) ((s32) in[4])) * ((s32) in[6]) + + ((limb) ((s32) in[2])) * ((s32) in[8]) + + 2 * (((limb) ((s32) in[3])) * ((s32) in[7]) + + ((limb) ((s32) in[1])) * ((s32) in[9]))); + output[11] = 2 * (((limb) ((s32) in[5])) * ((s32) in[6]) + + ((limb) ((s32) in[4])) * ((s32) in[7]) + + ((limb) ((s32) in[3])) * ((s32) in[8]) + + ((limb) ((s32) in[2])) * ((s32) in[9])); + output[12] = ((limb) ((s32) in[6])) * ((s32) in[6]) + + 2 * (((limb) ((s32) in[4])) * ((s32) in[8]) + + 2 * (((limb) ((s32) in[5])) * ((s32) in[7]) + + ((limb) ((s32) in[3])) * ((s32) in[9]))); + output[13] = 2 * (((limb) ((s32) in[6])) * ((s32) in[7]) + + ((limb) ((s32) in[5])) * ((s32) in[8]) + + ((limb) ((s32) in[4])) * ((s32) in[9])); + output[14] = 2 * (((limb) ((s32) in[7])) * ((s32) in[7]) + + ((limb) ((s32) in[6])) * ((s32) in[8]) + + 2 * ((limb) ((s32) in[5])) * ((s32) in[9])); + output[15] = 2 * (((limb) ((s32) in[7])) * ((s32) in[8]) + + ((limb) ((s32) in[6])) * ((s32) in[9])); + output[16] = ((limb) ((s32) in[8])) * ((s32) in[8]) + + 4 * ((limb) ((s32) in[7])) * ((s32) in[9]); + output[17] = 2 * ((limb) ((s32) in[8])) * ((s32) in[9]); + output[18] = 2 * ((limb) ((s32) in[9])) * ((s32) in[9]); +} + +/* fsquare sets output = in^2. + * + * On entry: The |in| argument is in reduced coefficients form and |in[i]| < + * 2^27. + * + * On exit: The |output| argument is in reduced coefficients form (indeed, one + * need only provide storage for 10 limbs) and |out[i]| < 2^26. */ +static void +fsquare(limb *output, const limb *in) { + limb t[19]; + fsquare_inner(t, in); + /* |t[i]| < 14*2^54 because the largest product of two limbs will be < + * 2^(27+27) and fsquare_inner adds together, at most, 14 of those + * products. */ + freduce_degree(t); + freduce_coefficients(t); + /* |t[i]| < 2^26 */ + memcpy(output, t, sizeof(limb) * 10); +} + +/* Take a little-endian, 32-byte number and expand it into polynomial form */ +static void +fexpand(limb *output, const u8 *input) { +#define F(n,start,shift,mask) \ + output[n] = ((((limb) input[start + 0]) | \ + ((limb) input[start + 1]) << 8 | \ + ((limb) input[start + 2]) << 16 | \ + ((limb) input[start + 3]) << 24) >> shift) & mask; + F(0, 0, 0, 0x3ffffff); + F(1, 3, 2, 0x1ffffff); + F(2, 6, 3, 0x3ffffff); + F(3, 9, 5, 0x1ffffff); + F(4, 12, 6, 0x3ffffff); + F(5, 16, 0, 0x1ffffff); + F(6, 19, 1, 0x3ffffff); + F(7, 22, 3, 0x1ffffff); + F(8, 25, 4, 0x3ffffff); + F(9, 28, 6, 0x1ffffff); +#undef F +} + +#if (-32 >> 1) != -16 +#error "This code only works when >> does sign-extension on negative numbers" +#endif + +/* s32_eq returns 0xffffffff iff a == b and zero otherwise. */ +static s32 s32_eq(s32 a, s32 b) { + a = ~(a ^ b); + a &= a << 16; + a &= a << 8; + a &= a << 4; + a &= a << 2; + a &= a << 1; + return a >> 31; +} + +/* s32_gte returns 0xffffffff if a >= b and zero otherwise, where a and b are + * both non-negative. */ +static s32 s32_gte(s32 a, s32 b) { + a -= b; + /* a >= 0 iff a >= b. */ + return ~(a >> 31); +} + +/* Take a fully reduced polynomial form number and contract it into a + * little-endian, 32-byte array. + * + * On entry: |input_limbs[i]| < 2^26 */ +static void +fcontract(u8 *output, limb *input_limbs) { + int i; + int j; + s32 input[10]; + s32 mask; + + /* |input_limbs[i]| < 2^26, so it's valid to convert to an s32. */ + for (i = 0; i < 10; i++) { + input[i] = input_limbs[i]; + } + + for (j = 0; j < 2; ++j) { + for (i = 0; i < 9; ++i) { + if ((i & 1) == 1) { + /* This calculation is a time-invariant way to make input[i] + * non-negative by borrowing from the next-larger limb. */ + const s32 mask = input[i] >> 31; + const s32 carry = -((input[i] & mask) >> 25); + input[i] = input[i] + (carry << 25); + input[i+1] = input[i+1] - carry; + } else { + const s32 mask = input[i] >> 31; + const s32 carry = -((input[i] & mask) >> 26); + input[i] = input[i] + (carry << 26); + input[i+1] = input[i+1] - carry; + } + } + + /* There's no greater limb for input[9] to borrow from, but we can multiply + * by 19 and borrow from input[0], which is valid mod 2^255-19. */ + { + const s32 mask = input[9] >> 31; + const s32 carry = -((input[9] & mask) >> 25); + input[9] = input[9] + (carry << 25); + input[0] = input[0] - (carry * 19); + } + + /* After the first iteration, input[1..9] are non-negative and fit within + * 25 or 26 bits, depending on position. However, input[0] may be + * negative. */ + } + + /* The first borrow-propagation pass above ended with every limb + except (possibly) input[0] non-negative. + + If input[0] was negative after the first pass, then it was because of a + carry from input[9]. On entry, input[9] < 2^26 so the carry was, at most, + one, since (2**26-1) >> 25 = 1. Thus input[0] >= -19. + + In the second pass, each limb is decreased by at most one. Thus the second + borrow-propagation pass could only have wrapped around to decrease + input[0] again if the first pass left input[0] negative *and* input[1] + through input[9] were all zero. In that case, input[1] is now 2^25 - 1, + and this last borrow-propagation step will leave input[1] non-negative. */ + { + const s32 mask = input[0] >> 31; + const s32 carry = -((input[0] & mask) >> 26); + input[0] = input[0] + (carry << 26); + input[1] = input[1] - carry; + } + + /* All input[i] are now non-negative. However, there might be values between + * 2^25 and 2^26 in a limb which is, nominally, 25 bits wide. */ + for (j = 0; j < 2; j++) { + for (i = 0; i < 9; i++) { + if ((i & 1) == 1) { + const s32 carry = input[i] >> 25; + input[i] &= 0x1ffffff; + input[i+1] += carry; + } else { + const s32 carry = input[i] >> 26; + input[i] &= 0x3ffffff; + input[i+1] += carry; + } + } + + { + const s32 carry = input[9] >> 25; + input[9] &= 0x1ffffff; + input[0] += 19*carry; + } + } + + /* If the first carry-chain pass, just above, ended up with a carry from + * input[9], and that caused input[0] to be out-of-bounds, then input[0] was + * < 2^26 + 2*19, because the carry was, at most, two. + * + * If the second pass carried from input[9] again then input[0] is < 2*19 and + * the input[9] -> input[0] carry didn't push input[0] out of bounds. */ + + /* It still remains the case that input might be between 2^255-19 and 2^255. + * In this case, input[1..9] must take their maximum value and input[0] must + * be >= (2^255-19) & 0x3ffffff, which is 0x3ffffed. */ + mask = s32_gte(input[0], 0x3ffffed); + for (i = 1; i < 10; i++) { + if ((i & 1) == 1) { + mask &= s32_eq(input[i], 0x1ffffff); + } else { + mask &= s32_eq(input[i], 0x3ffffff); + } + } + + /* mask is either 0xffffffff (if input >= 2^255-19) and zero otherwise. Thus + * this conditionally subtracts 2^255-19. */ + input[0] -= mask & 0x3ffffed; + + for (i = 1; i < 10; i++) { + if ((i & 1) == 1) { + input[i] -= mask & 0x1ffffff; + } else { + input[i] -= mask & 0x3ffffff; + } + } + + input[1] <<= 2; + input[2] <<= 3; + input[3] <<= 5; + input[4] <<= 6; + input[6] <<= 1; + input[7] <<= 3; + input[8] <<= 4; + input[9] <<= 6; +#define F(i, s) \ + output[s+0] |= input[i] & 0xff; \ + output[s+1] = (input[i] >> 8) & 0xff; \ + output[s+2] = (input[i] >> 16) & 0xff; \ + output[s+3] = (input[i] >> 24) & 0xff; + output[0] = 0; + output[16] = 0; + F(0,0); + F(1,3); + F(2,6); + F(3,9); + F(4,12); + F(5,16); + F(6,19); + F(7,22); + F(8,25); + F(9,28); +#undef F +} + +/* Input: Q, Q', Q-Q' + * Output: 2Q, Q+Q' + * + * x2 z3: long form + * x3 z3: long form + * x z: short form, destroyed + * xprime zprime: short form, destroyed + * qmqp: short form, preserved + * + * On entry and exit, the absolute value of the limbs of all inputs and outputs + * are < 2^26. */ +static void fmonty(limb *x2, limb *z2, /* output 2Q */ + limb *x3, limb *z3, /* output Q + Q' */ + limb *x, limb *z, /* input Q */ + limb *xprime, limb *zprime, /* input Q' */ + const limb *qmqp /* input Q - Q' */) { + limb origx[10], origxprime[10], zzz[19], xx[19], zz[19], xxprime[19], + zzprime[19], zzzprime[19], xxxprime[19]; + + memcpy(origx, x, 10 * sizeof(limb)); + fsum(x, z); + /* |x[i]| < 2^27 */ + fdifference(z, origx); /* does x - z */ + /* |z[i]| < 2^27 */ + + memcpy(origxprime, xprime, sizeof(limb) * 10); + fsum(xprime, zprime); + /* |xprime[i]| < 2^27 */ + fdifference(zprime, origxprime); + /* |zprime[i]| < 2^27 */ + fproduct(xxprime, xprime, z); + /* |xxprime[i]| < 14*2^54: the largest product of two limbs will be < + * 2^(27+27) and fproduct adds together, at most, 14 of those products. + * (Approximating that to 2^58 doesn't work out.) */ + fproduct(zzprime, x, zprime); + /* |zzprime[i]| < 14*2^54 */ + freduce_degree(xxprime); + freduce_coefficients(xxprime); + /* |xxprime[i]| < 2^26 */ + freduce_degree(zzprime); + freduce_coefficients(zzprime); + /* |zzprime[i]| < 2^26 */ + memcpy(origxprime, xxprime, sizeof(limb) * 10); + fsum(xxprime, zzprime); + /* |xxprime[i]| < 2^27 */ + fdifference(zzprime, origxprime); + /* |zzprime[i]| < 2^27 */ + fsquare(xxxprime, xxprime); + /* |xxxprime[i]| < 2^26 */ + fsquare(zzzprime, zzprime); + /* |zzzprime[i]| < 2^26 */ + fproduct(zzprime, zzzprime, qmqp); + /* |zzprime[i]| < 14*2^52 */ + freduce_degree(zzprime); + freduce_coefficients(zzprime); + /* |zzprime[i]| < 2^26 */ + memcpy(x3, xxxprime, sizeof(limb) * 10); + memcpy(z3, zzprime, sizeof(limb) * 10); + + fsquare(xx, x); + /* |xx[i]| < 2^26 */ + fsquare(zz, z); + /* |zz[i]| < 2^26 */ + fproduct(x2, xx, zz); + /* |x2[i]| < 14*2^52 */ + freduce_degree(x2); + freduce_coefficients(x2); + /* |x2[i]| < 2^26 */ + fdifference(zz, xx); // does zz = xx - zz + /* |zz[i]| < 2^27 */ + memset(zzz + 10, 0, sizeof(limb) * 9); + fscalar_product(zzz, zz, 121665); + /* |zzz[i]| < 2^(27+17) */ + /* No need to call freduce_degree here: + fscalar_product doesn't increase the degree of its input. */ + freduce_coefficients(zzz); + /* |zzz[i]| < 2^26 */ + fsum(zzz, xx); + /* |zzz[i]| < 2^27 */ + fproduct(z2, zz, zzz); + /* |z2[i]| < 14*2^(26+27) */ + freduce_degree(z2); + freduce_coefficients(z2); + /* |z2|i| < 2^26 */ +} + +/* Conditionally swap two reduced-form limb arrays if 'iswap' is 1, but leave + * them unchanged if 'iswap' is 0. Runs in data-invariant time to avoid + * side-channel attacks. + * + * NOTE that this function requires that 'iswap' be 1 or 0; other values give + * wrong results. Also, the two limb arrays must be in reduced-coefficient, + * reduced-degree form: the values in a[10..19] or b[10..19] aren't swapped, + * and all all values in a[0..9],b[0..9] must have magnitude less than + * INT32_MAX. */ +static void +swap_conditional(limb a[19], limb b[19], limb iswap) { + unsigned i; + const s32 swap = (s32) -iswap; + + for (i = 0; i < 10; ++i) { + const s32 x = swap & ( ((s32)a[i]) ^ ((s32)b[i]) ); + a[i] = ((s32)a[i]) ^ x; + b[i] = ((s32)b[i]) ^ x; + } +} + +/* Calculates nQ where Q is the x-coordinate of a point on the curve + * + * resultx/resultz: the x coordinate of the resulting curve point (short form) + * n: a little endian, 32-byte number + * q: a point of the curve (short form) */ +static void +cmult(limb *resultx, limb *resultz, const u8 *n, const limb *q) { + limb a[19] = {0}, b[19] = {1}, c[19] = {1}, d[19] = {0}; + limb *nqpqx = a, *nqpqz = b, *nqx = c, *nqz = d, *t; + limb e[19] = {0}, f[19] = {1}, g[19] = {0}, h[19] = {1}; + limb *nqpqx2 = e, *nqpqz2 = f, *nqx2 = g, *nqz2 = h; + + unsigned i, j; + + memcpy(nqpqx, q, sizeof(limb) * 10); + + for (i = 0; i < 32; ++i) { + u8 byte = n[31 - i]; + for (j = 0; j < 8; ++j) { + const limb bit = byte >> 7; + + swap_conditional(nqx, nqpqx, bit); + swap_conditional(nqz, nqpqz, bit); + fmonty(nqx2, nqz2, + nqpqx2, nqpqz2, + nqx, nqz, + nqpqx, nqpqz, + q); + swap_conditional(nqx2, nqpqx2, bit); + swap_conditional(nqz2, nqpqz2, bit); + + t = nqx; + nqx = nqx2; + nqx2 = t; + t = nqz; + nqz = nqz2; + nqz2 = t; + t = nqpqx; + nqpqx = nqpqx2; + nqpqx2 = t; + t = nqpqz; + nqpqz = nqpqz2; + nqpqz2 = t; + + byte <<= 1; + } + } + + memcpy(resultx, nqx, sizeof(limb) * 10); + memcpy(resultz, nqz, sizeof(limb) * 10); +} + +// ----------------------------------------------------------------------------- +// Shamelessly copied from djb's code +// ----------------------------------------------------------------------------- +static void +crecip(limb *out, const limb *z) { + limb z2[10]; + limb z9[10]; + limb z11[10]; + limb z2_5_0[10]; + limb z2_10_0[10]; + limb z2_20_0[10]; + limb z2_50_0[10]; + limb z2_100_0[10]; + limb t0[10]; + limb t1[10]; + int i; + + /* 2 */ fsquare(z2,z); + /* 4 */ fsquare(t1,z2); + /* 8 */ fsquare(t0,t1); + /* 9 */ fmul(z9,t0,z); + /* 11 */ fmul(z11,z9,z2); + /* 22 */ fsquare(t0,z11); + /* 2^5 - 2^0 = 31 */ fmul(z2_5_0,t0,z9); + + /* 2^6 - 2^1 */ fsquare(t0,z2_5_0); + /* 2^7 - 2^2 */ fsquare(t1,t0); + /* 2^8 - 2^3 */ fsquare(t0,t1); + /* 2^9 - 2^4 */ fsquare(t1,t0); + /* 2^10 - 2^5 */ fsquare(t0,t1); + /* 2^10 - 2^0 */ fmul(z2_10_0,t0,z2_5_0); + + /* 2^11 - 2^1 */ fsquare(t0,z2_10_0); + /* 2^12 - 2^2 */ fsquare(t1,t0); + /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } + /* 2^20 - 2^0 */ fmul(z2_20_0,t1,z2_10_0); + + /* 2^21 - 2^1 */ fsquare(t0,z2_20_0); + /* 2^22 - 2^2 */ fsquare(t1,t0); + /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } + /* 2^40 - 2^0 */ fmul(t0,t1,z2_20_0); + + /* 2^41 - 2^1 */ fsquare(t1,t0); + /* 2^42 - 2^2 */ fsquare(t0,t1); + /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t1,t0); fsquare(t0,t1); } + /* 2^50 - 2^0 */ fmul(z2_50_0,t0,z2_10_0); + + /* 2^51 - 2^1 */ fsquare(t0,z2_50_0); + /* 2^52 - 2^2 */ fsquare(t1,t0); + /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } + /* 2^100 - 2^0 */ fmul(z2_100_0,t1,z2_50_0); + + /* 2^101 - 2^1 */ fsquare(t1,z2_100_0); + /* 2^102 - 2^2 */ fsquare(t0,t1); + /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { fsquare(t1,t0); fsquare(t0,t1); } + /* 2^200 - 2^0 */ fmul(t1,t0,z2_100_0); + + /* 2^201 - 2^1 */ fsquare(t0,t1); + /* 2^202 - 2^2 */ fsquare(t1,t0); + /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } + /* 2^250 - 2^0 */ fmul(t0,t1,z2_50_0); + + /* 2^251 - 2^1 */ fsquare(t1,t0); + /* 2^252 - 2^2 */ fsquare(t0,t1); + /* 2^253 - 2^3 */ fsquare(t1,t0); + /* 2^254 - 2^4 */ fsquare(t0,t1); + /* 2^255 - 2^5 */ fsquare(t1,t0); + /* 2^255 - 21 */ fmul(out,t1,z11); +} + +int +curve25519_donna(u8 *mypublic, const u8 *secret, const u8 *basepoint) { + limb bp[10], x[10], z[11], zmone[10]; + uint8_t e[32]; + int i; + + for (i = 0; i < 32; ++i) e[i] = secret[i]; +// e[0] &= 248; +// e[31] &= 127; +// e[31] |= 64; + + fexpand(bp, basepoint); + cmult(x, z, e, bp); + crecip(zmone, z); + fmul(z, x, zmone); + fcontract(mypublic, z); + return 0; +} diff --git a/jni/curve25519-donna.h b/jni/curve25519-donna.h new file mode 100644 index 000000000..3e4dfcee0 --- /dev/null +++ b/jni/curve25519-donna.h @@ -0,0 +1,6 @@ +#ifndef CURVE25519_DONNA_H +#define CURVE25519_DONNA_H + +extern int curve25519_donna(uint8_t *, const uint8_t *, const uint8_t *); + +#endif diff --git a/jni/curve25519-jni.c b/jni/curve25519-jni.c new file mode 100644 index 000000000..5d058a916 --- /dev/null +++ b/jni/curve25519-jni.c @@ -0,0 +1,109 @@ +/** + * Copyright (C) 2013-2014 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include +#include "curve25519-donna.h" +#include "curve_sigs.h" + +JNIEXPORT jbyteArray JNICALL Java_org_whispersystems_libaxolotl_ecc_Curve25519_generatePrivateKey + (JNIEnv *env, jclass clazz, jbyteArray random) +{ + uint8_t* privateKey = (uint8_t*)(*env)->GetByteArrayElements(env, random, 0); + + privateKey[0] &= 248; + privateKey[31] &= 127; + privateKey[31] |= 64; + + (*env)->ReleaseByteArrayElements(env, random, privateKey, 0); + + return random; +} + +JNIEXPORT jbyteArray JNICALL Java_org_whispersystems_libaxolotl_ecc_Curve25519_generatePublicKey + (JNIEnv *env, jclass clazz, jbyteArray privateKey) +{ + static const uint8_t basepoint[32] = {9}; + + jbyteArray publicKey = (*env)->NewByteArray(env, 32); + uint8_t* publicKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, publicKey, 0); + uint8_t* privateKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, privateKey, 0); + + curve25519_donna(publicKeyBytes, privateKeyBytes, basepoint); + + (*env)->ReleaseByteArrayElements(env, publicKey, publicKeyBytes, 0); + (*env)->ReleaseByteArrayElements(env, privateKey, privateKeyBytes, 0); + + return publicKey; +} + +JNIEXPORT jbyteArray JNICALL Java_org_whispersystems_libaxolotl_ecc_Curve25519_calculateAgreement + (JNIEnv *env, jclass clazz, jbyteArray privateKey, jbyteArray publicKey) +{ + jbyteArray sharedKey = (*env)->NewByteArray(env, 32); + uint8_t* sharedKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, sharedKey, 0); + uint8_t* privateKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, privateKey, 0); + uint8_t* publicKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, publicKey, 0); + + curve25519_donna(sharedKeyBytes, privateKeyBytes, publicKeyBytes); + + (*env)->ReleaseByteArrayElements(env, sharedKey, sharedKeyBytes, 0); + (*env)->ReleaseByteArrayElements(env, publicKey, publicKeyBytes, 0); + (*env)->ReleaseByteArrayElements(env, privateKey, privateKeyBytes, 0); + + return sharedKey; +} + +JNIEXPORT jbyteArray JNICALL Java_org_whispersystems_libaxolotl_ecc_Curve25519_calculateSignature + (JNIEnv *env, jclass clazz, jbyteArray random, jbyteArray privateKey, jbyteArray message) +{ + jbyteArray signature = (*env)->NewByteArray(env, 64); + uint8_t* signatureBytes = (uint8_t*)(*env)->GetByteArrayElements(env, signature, 0); + uint8_t* randomBytes = (uint8_t*)(*env)->GetByteArrayElements(env, random, 0); + uint8_t* privateKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, privateKey, 0); + uint8_t* messageBytes = (uint8_t*)(*env)->GetByteArrayElements(env, message, 0); + jsize messageLength = (*env)->GetArrayLength(env, message); + + int result = curve25519_sign(signatureBytes, privateKeyBytes, messageBytes, messageLength, randomBytes); + + (*env)->ReleaseByteArrayElements(env, signature, signatureBytes, 0); + (*env)->ReleaseByteArrayElements(env, random, randomBytes, 0); + (*env)->ReleaseByteArrayElements(env, privateKey, privateKeyBytes, 0); + (*env)->ReleaseByteArrayElements(env, message, messageBytes, 0); + + if (result == 0) return signature; + else (*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/AssertionError"), "Signature failed!"); +} + +JNIEXPORT jboolean JNICALL Java_org_whispersystems_libaxolotl_ecc_Curve25519_verifySignature + (JNIEnv *env, jclass clazz, jbyteArray publicKey, jbyteArray message, jbyteArray signature) +{ + uint8_t* signatureBytes = (uint8_t*)(*env)->GetByteArrayElements(env, signature, 0); + uint8_t* publicKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, publicKey, 0); + uint8_t* messageBytes = (uint8_t*)(*env)->GetByteArrayElements(env, message, 0); + jsize messageLength = (*env)->GetArrayLength(env, message); + + jboolean result = (curve25519_verify(signatureBytes, publicKeyBytes, messageBytes, messageLength) == 0); + + (*env)->ReleaseByteArrayElements(env, signature, signatureBytes, 0); + (*env)->ReleaseByteArrayElements(env, publicKey, publicKeyBytes, 0); + (*env)->ReleaseByteArrayElements(env, message, messageBytes, 0); + + return result; +} diff --git a/jni/ed25519/additions/compare.c b/jni/ed25519/additions/compare.c new file mode 100644 index 000000000..8b1e31389 --- /dev/null +++ b/jni/ed25519/additions/compare.c @@ -0,0 +1,44 @@ +#include +#include "compare.h" + +/* Const-time comparison from SUPERCOP, but here it's only used for + signature verification, so doesn't need to be const-time. But + copied the nacl version anyways. */ +int crypto_verify_32_ref(const unsigned char *x, const unsigned char *y) +{ + unsigned int differentbits = 0; +#define F(i) differentbits |= x[i] ^ y[i]; + F(0) + F(1) + F(2) + F(3) + F(4) + F(5) + F(6) + F(7) + F(8) + F(9) + F(10) + F(11) + F(12) + F(13) + F(14) + F(15) + F(16) + F(17) + F(18) + F(19) + F(20) + F(21) + F(22) + F(23) + F(24) + F(25) + F(26) + F(27) + F(28) + F(29) + F(30) + F(31) + return (1 & ((differentbits - 1) >> 8)) - 1; +} diff --git a/jni/ed25519/additions/compare.h b/jni/ed25519/additions/compare.h new file mode 100644 index 000000000..5a2fa910d --- /dev/null +++ b/jni/ed25519/additions/compare.h @@ -0,0 +1,6 @@ +#ifndef __COMPARE_H__ +#define __COMPARE_H__ + +int crypto_verify_32_ref(const unsigned char *b1, const unsigned char *b2); + +#endif diff --git a/jni/ed25519/additions/crypto_hash_sha512.h b/jni/ed25519/additions/crypto_hash_sha512.h new file mode 100644 index 000000000..a51a190d2 --- /dev/null +++ b/jni/ed25519/additions/crypto_hash_sha512.h @@ -0,0 +1,6 @@ +#ifndef crypto_hash_sha512_H +#define crypto_hash_sha512_H + +extern int crypto_hash_sha512(unsigned char *,const unsigned char *,unsigned long long); + +#endif diff --git a/jni/ed25519/additions/curve_sigs.c b/jni/ed25519/additions/curve_sigs.c new file mode 100644 index 000000000..51f2052d8 --- /dev/null +++ b/jni/ed25519/additions/curve_sigs.c @@ -0,0 +1,116 @@ +#include +#include "ge.h" +#include "curve_sigs.h" +#include "crypto_sign.h" + +void curve25519_keygen(unsigned char* curve25519_pubkey_out, + const unsigned char* curve25519_privkey_in) +{ + ge_p3 ed; /* Ed25519 pubkey point */ + fe ed_y, ed_y_plus_one, one_minus_ed_y, inv_one_minus_ed_y; + fe mont_x; + + /* Perform a fixed-base multiplication of the Edwards base point, + (which is efficient due to precalculated tables), then convert + to the Curve25519 montgomery-format public key. In particular, + convert Curve25519's "montgomery" x-coordinate into an Ed25519 + "edwards" y-coordinate: + + mont_x = (ed_y + 1) / (1 - ed_y) + + with projective coordinates: + + mont_x = (ed_y + ed_z) / (ed_z - ed_y) + + NOTE: ed_y=1 is converted to mont_x=0 since fe_invert is mod-exp + */ + + ge_scalarmult_base(&ed, curve25519_privkey_in); + fe_add(ed_y_plus_one, ed.Y, ed.Z); + fe_sub(one_minus_ed_y, ed.Z, ed.Y); + fe_invert(inv_one_minus_ed_y, one_minus_ed_y); + fe_mul(mont_x, ed_y_plus_one, inv_one_minus_ed_y); + fe_tobytes(curve25519_pubkey_out, mont_x); +} + +int curve25519_sign(unsigned char* signature_out, + const unsigned char* curve25519_privkey, + const unsigned char* msg, const unsigned long msg_len, + const unsigned char* random) +{ + ge_p3 ed_pubkey_point; /* Ed25519 pubkey point */ + unsigned char ed_pubkey[32]; /* Ed25519 encoded pubkey */ + unsigned char sigbuf[MAX_MSG_LEN + 128]; /* working buffer */ + unsigned char sign_bit = 0; + + if (msg_len > MAX_MSG_LEN) { + memset(signature_out, 0, 64); + return -1; + } + + /* Convert the Curve25519 privkey to an Ed25519 public key */ + ge_scalarmult_base(&ed_pubkey_point, curve25519_privkey); + ge_p3_tobytes(ed_pubkey, &ed_pubkey_point); + sign_bit = ed_pubkey[31] & 0x80; + + /* Perform an Ed25519 signature with explicit private key */ + crypto_sign_modified(sigbuf, msg, msg_len, curve25519_privkey, + ed_pubkey, random); + memmove(signature_out, sigbuf, 64); + + /* Encode the sign bit into signature (in unused high bit of S) */ + signature_out[63] &= 0x7F; /* bit should be zero already, but just in case */ + signature_out[63] |= sign_bit; + return 0; +} + +int curve25519_verify(const unsigned char* signature, + const unsigned char* curve25519_pubkey, + const unsigned char* msg, const unsigned long msg_len) +{ + fe mont_x, mont_x_minus_one, mont_x_plus_one, inv_mont_x_plus_one; + fe one; + fe ed_y; + unsigned char ed_pubkey[32]; + unsigned long long some_retval; + unsigned char verifybuf[MAX_MSG_LEN + 64]; /* working buffer */ + unsigned char verifybuf2[MAX_MSG_LEN + 64]; /* working buffer #2 */ + + if (msg_len > MAX_MSG_LEN) { + return -1; + } + + /* Convert the Curve25519 public key into an Ed25519 public key. In + particular, convert Curve25519's "montgomery" x-coordinate into an + Ed25519 "edwards" y-coordinate: + + ed_y = (mont_x - 1) / (mont_x + 1) + + NOTE: mont_x=-1 is converted to ed_y=0 since fe_invert is mod-exp + + Then move the sign bit into the pubkey from the signature. + */ + fe_frombytes(mont_x, curve25519_pubkey); + fe_1(one); + fe_sub(mont_x_minus_one, mont_x, one); + fe_add(mont_x_plus_one, mont_x, one); + fe_invert(inv_mont_x_plus_one, mont_x_plus_one); + fe_mul(ed_y, mont_x_minus_one, inv_mont_x_plus_one); + fe_tobytes(ed_pubkey, ed_y); + + /* Copy the sign bit, and remove it from signature */ + ed_pubkey[31] &= 0x7F; /* bit should be zero already, but just in case */ + ed_pubkey[31] |= (signature[63] & 0x80); + memmove(verifybuf, signature, 64); + verifybuf[63] &= 0x7F; + + memmove(verifybuf+64, msg, msg_len); + + /* Then perform a normal Ed25519 verification, return 0 on success */ + /* The below call has a strange API: */ + /* verifybuf = R || S || message */ + /* verifybuf2 = internal to next call gets a copy of verifybuf, S gets + replaced with pubkey for hashing, then the whole thing gets zeroized + (if bad sig), or contains a copy of msg (good sig) */ + return crypto_sign_open(verifybuf2, &some_retval, verifybuf, 64 + msg_len, ed_pubkey); +} diff --git a/jni/ed25519/additions/curve_sigs.h b/jni/ed25519/additions/curve_sigs.h new file mode 100644 index 000000000..cc462471b --- /dev/null +++ b/jni/ed25519/additions/curve_sigs.h @@ -0,0 +1,50 @@ + +#ifndef __CURVE_SIGS_H__ +#define __CURVE_SIGS_H__ + +#define MAX_MSG_LEN 256 + +void curve25519_keygen(unsigned char* curve25519_pubkey_out, /* 32 bytes */ + const unsigned char* curve25519_privkey_in); /* 32 bytes */ + +/* returns 0 on success */ +int curve25519_sign(unsigned char* signature_out, /* 64 bytes */ + const unsigned char* curve25519_privkey, /* 32 bytes */ + const unsigned char* msg, const unsigned long msg_len, + const unsigned char* random); /* 64 bytes */ + +/* returns 0 on success */ +int curve25519_verify(const unsigned char* signature, /* 64 bytes */ + const unsigned char* curve25519_pubkey, /* 32 bytes */ + const unsigned char* msg, const unsigned long msg_len); + +/* helper function - modified version of crypto_sign() to use + explicit private key. In particular: + + sk : private key + pk : public key + msg : message + prefix : 0xFE || [0xFF]*31 + random : 64 bytes random + q : main subgroup order + + The prefix is chosen to distinguish the two SHA512 uses below, since + prefix is an invalid encoding for R (it would encode a "field element" + of 2^255 - 2). 0xFF*32 is set aside for use in ECDH protocols, which + is why the first byte here ix 0xFE. + + sig_nonce = SHA512(prefix || sk || msg || random) % q + R = g^sig_nonce + M = SHA512(R || pk || m) + S = sig_nonce + (m * sk) + signature = (R || S) + */ +int crypto_sign_modified( + unsigned char *sm, + const unsigned char *m,unsigned long long mlen, + const unsigned char *sk, /* Curve/Ed25519 private key */ + const unsigned char *pk, /* Ed25519 public key */ + const unsigned char *random /* 64 bytes random to hash into nonce */ + ); + +#endif diff --git a/jni/ed25519/additions/sign_modified.c b/jni/ed25519/additions/sign_modified.c new file mode 100644 index 000000000..61332e70e --- /dev/null +++ b/jni/ed25519/additions/sign_modified.c @@ -0,0 +1,47 @@ +#include +#include "crypto_sign.h" +#include "crypto_hash_sha512.h" +#include "ge.h" +#include "sc.h" +#include "zeroize.h" + +/* NEW: Compare to pristine crypto_sign() + Uses explicit private key for nonce derivation and as scalar, + instead of deriving both from a master key. +*/ +int crypto_sign_modified( + unsigned char *sm, + const unsigned char *m,unsigned long long mlen, + const unsigned char *sk, const unsigned char* pk, + const unsigned char* random +) +{ + unsigned char nonce[64]; + unsigned char hram[64]; + ge_p3 R; + int count=0; + + memmove(sm + 64,m,mlen); + memmove(sm + 32,sk,32); /* NEW: Use privkey directly for nonce derivation */ + + /* NEW : add prefix to separate hash uses - see .h */ + sm[0] = 0xFE; + for (count = 1; count < 32; count++) + sm[count] = 0xFF; + + /* NEW: add suffix of random data */ + memmove(sm + mlen + 64, random, 64); + + crypto_hash_sha512(nonce,sm,mlen + 128); + memmove(sm + 32,pk,32); + + sc_reduce(nonce); + ge_scalarmult_base(&R,nonce); + ge_p3_tobytes(sm,&R); + + crypto_hash_sha512(hram,sm,mlen + 64); + sc_reduce(hram); + sc_muladd(sm + 32,hram,sk,nonce); /* NEW: Use privkey directly */ + + return 0; +} diff --git a/jni/ed25519/additions/zeroize.c b/jni/ed25519/additions/zeroize.c new file mode 100644 index 000000000..37c1f708b --- /dev/null +++ b/jni/ed25519/additions/zeroize.c @@ -0,0 +1,17 @@ +#include "zeroize.h" + +void zeroize(unsigned char* b, size_t len) +{ + size_t count = 0; + unsigned long retval = 0; + volatile unsigned char *p = b; + + for (count = 0; count < len; count++) + p[count] = 0; +} + +void zeroize_stack() +{ + unsigned char m[ZEROIZE_STACK_SIZE]; + zeroize(m, sizeof m); +} diff --git a/jni/ed25519/additions/zeroize.h b/jni/ed25519/additions/zeroize.h new file mode 100644 index 000000000..80fcffb76 --- /dev/null +++ b/jni/ed25519/additions/zeroize.h @@ -0,0 +1,12 @@ +#ifndef __ZEROIZE_H__ +#define __ZEROIZE_H__ + +#include + +#define ZEROIZE_STACK_SIZE 2048 + +void zeroize(unsigned char* b, size_t len); + +void zeroize_stack(); + +#endif diff --git a/jni/ed25519/api.h b/jni/ed25519/api.h new file mode 100644 index 000000000..d88dae0c3 --- /dev/null +++ b/jni/ed25519/api.h @@ -0,0 +1,4 @@ +#define CRYPTO_SECRETKEYBYTES 64 +#define CRYPTO_PUBLICKEYBYTES 32 +#define CRYPTO_BYTES 64 +#define CRYPTO_DETERMINISTIC 1 diff --git a/jni/ed25519/base.h b/jni/ed25519/base.h new file mode 100644 index 000000000..573bd8a05 --- /dev/null +++ b/jni/ed25519/base.h @@ -0,0 +1,1344 @@ +{ + { + { 25967493,-14356035,29566456,3660896,-12694345,4014787,27544626,-11754271,-6079156,2047605 }, + { -12545711,934262,-2722910,3049990,-727428,9406986,12720692,5043384,19500929,-15469378 }, + { -8738181,4489570,9688441,-14785194,10184609,-12363380,29287919,11864899,-24514362,-4438546 }, + }, + { + { -12815894,-12976347,-21581243,11784320,-25355658,-2750717,-11717903,-3814571,-358445,-10211303 }, + { -21703237,6903825,27185491,6451973,-29577724,-9554005,-15616551,11189268,-26829678,-5319081 }, + { 26966642,11152617,32442495,15396054,14353839,-12752335,-3128826,-9541118,-15472047,-4166697 }, + }, + { + { 15636291,-9688557,24204773,-7912398,616977,-16685262,27787600,-14772189,28944400,-1550024 }, + { 16568933,4717097,-11556148,-1102322,15682896,-11807043,16354577,-11775962,7689662,11199574 }, + { 30464156,-5976125,-11779434,-15670865,23220365,15915852,7512774,10017326,-17749093,-9920357 }, + }, + { + { -17036878,13921892,10945806,-6033431,27105052,-16084379,-28926210,15006023,3284568,-6276540 }, + { 23599295,-8306047,-11193664,-7687416,13236774,10506355,7464579,9656445,13059162,10374397 }, + { 7798556,16710257,3033922,2874086,28997861,2835604,32406664,-3839045,-641708,-101325 }, + }, + { + { 10861363,11473154,27284546,1981175,-30064349,12577861,32867885,14515107,-15438304,10819380 }, + { 4708026,6336745,20377586,9066809,-11272109,6594696,-25653668,12483688,-12668491,5581306 }, + { 19563160,16186464,-29386857,4097519,10237984,-4348115,28542350,13850243,-23678021,-15815942 }, + }, + { + { -15371964,-12862754,32573250,4720197,-26436522,5875511,-19188627,-15224819,-9818940,-12085777 }, + { -8549212,109983,15149363,2178705,22900618,4543417,3044240,-15689887,1762328,14866737 }, + { -18199695,-15951423,-10473290,1707278,-17185920,3916101,-28236412,3959421,27914454,4383652 }, + }, + { + { 5153746,9909285,1723747,-2777874,30523605,5516873,19480852,5230134,-23952439,-15175766 }, + { -30269007,-3463509,7665486,10083793,28475525,1649722,20654025,16520125,30598449,7715701 }, + { 28881845,14381568,9657904,3680757,-20181635,7843316,-31400660,1370708,29794553,-1409300 }, + }, + { + { 14499471,-2729599,-33191113,-4254652,28494862,14271267,30290735,10876454,-33154098,2381726 }, + { -7195431,-2655363,-14730155,462251,-27724326,3941372,-6236617,3696005,-32300832,15351955 }, + { 27431194,8222322,16448760,-3907995,-18707002,11938355,-32961401,-2970515,29551813,10109425 }, + }, +}, +{ + { + { -13657040,-13155431,-31283750,11777098,21447386,6519384,-2378284,-1627556,10092783,-4764171 }, + { 27939166,14210322,4677035,16277044,-22964462,-12398139,-32508754,12005538,-17810127,12803510 }, + { 17228999,-15661624,-1233527,300140,-1224870,-11714777,30364213,-9038194,18016357,4397660 }, + }, + { + { -10958843,-7690207,4776341,-14954238,27850028,-15602212,-26619106,14544525,-17477504,982639 }, + { 29253598,15796703,-2863982,-9908884,10057023,3163536,7332899,-4120128,-21047696,9934963 }, + { 5793303,16271923,-24131614,-10116404,29188560,1206517,-14747930,4559895,-30123922,-10897950 }, + }, + { + { -27643952,-11493006,16282657,-11036493,28414021,-15012264,24191034,4541697,-13338309,5500568 }, + { 12650548,-1497113,9052871,11355358,-17680037,-8400164,-17430592,12264343,10874051,13524335 }, + { 25556948,-3045990,714651,2510400,23394682,-10415330,33119038,5080568,-22528059,5376628 }, + }, + { + { -26088264,-4011052,-17013699,-3537628,-6726793,1920897,-22321305,-9447443,4535768,1569007 }, + { -2255422,14606630,-21692440,-8039818,28430649,8775819,-30494562,3044290,31848280,12543772 }, + { -22028579,2943893,-31857513,6777306,13784462,-4292203,-27377195,-2062731,7718482,14474653 }, + }, + { + { 2385315,2454213,-22631320,46603,-4437935,-15680415,656965,-7236665,24316168,-5253567 }, + { 13741529,10911568,-33233417,-8603737,-20177830,-1033297,33040651,-13424532,-20729456,8321686 }, + { 21060490,-2212744,15712757,-4336099,1639040,10656336,23845965,-11874838,-9984458,608372 }, + }, + { + { -13672732,-15087586,-10889693,-7557059,-6036909,11305547,1123968,-6780577,27229399,23887 }, + { -23244140,-294205,-11744728,14712571,-29465699,-2029617,12797024,-6440308,-1633405,16678954 }, + { -29500620,4770662,-16054387,14001338,7830047,9564805,-1508144,-4795045,-17169265,4904953 }, + }, + { + { 24059557,14617003,19037157,-15039908,19766093,-14906429,5169211,16191880,2128236,-4326833 }, + { -16981152,4124966,-8540610,-10653797,30336522,-14105247,-29806336,916033,-6882542,-2986532 }, + { -22630907,12419372,-7134229,-7473371,-16478904,16739175,285431,2763829,15736322,4143876 }, + }, + { + { 2379352,11839345,-4110402,-5988665,11274298,794957,212801,-14594663,23527084,-16458268 }, + { 33431127,-11130478,-17838966,-15626900,8909499,8376530,-32625340,4087881,-15188911,-14416214 }, + { 1767683,7197987,-13205226,-2022635,-13091350,448826,5799055,4357868,-4774191,-16323038 }, + }, +}, +{ + { + { 6721966,13833823,-23523388,-1551314,26354293,-11863321,23365147,-3949732,7390890,2759800 }, + { 4409041,2052381,23373853,10530217,7676779,-12885954,21302353,-4264057,1244380,-12919645 }, + { -4421239,7169619,4982368,-2957590,30256825,-2777540,14086413,9208236,15886429,16489664 }, + }, + { + { 1996075,10375649,14346367,13311202,-6874135,-16438411,-13693198,398369,-30606455,-712933 }, + { -25307465,9795880,-2777414,14878809,-33531835,14780363,13348553,12076947,-30836462,5113182 }, + { -17770784,11797796,31950843,13929123,-25888302,12288344,-30341101,-7336386,13847711,5387222 }, + }, + { + { -18582163,-3416217,17824843,-2340966,22744343,-10442611,8763061,3617786,-19600662,10370991 }, + { 20246567,-14369378,22358229,-543712,18507283,-10413996,14554437,-8746092,32232924,16763880 }, + { 9648505,10094563,26416693,14745928,-30374318,-6472621,11094161,15689506,3140038,-16510092 }, + }, + { + { -16160072,5472695,31895588,4744994,8823515,10365685,-27224800,9448613,-28774454,366295 }, + { 19153450,11523972,-11096490,-6503142,-24647631,5420647,28344573,8041113,719605,11671788 }, + { 8678025,2694440,-6808014,2517372,4964326,11152271,-15432916,-15266516,27000813,-10195553 }, + }, + { + { -15157904,7134312,8639287,-2814877,-7235688,10421742,564065,5336097,6750977,-14521026 }, + { 11836410,-3979488,26297894,16080799,23455045,15735944,1695823,-8819122,8169720,16220347 }, + { -18115838,8653647,17578566,-6092619,-8025777,-16012763,-11144307,-2627664,-5990708,-14166033 }, + }, + { + { -23308498,-10968312,15213228,-10081214,-30853605,-11050004,27884329,2847284,2655861,1738395 }, + { -27537433,-14253021,-25336301,-8002780,-9370762,8129821,21651608,-3239336,-19087449,-11005278 }, + { 1533110,3437855,23735889,459276,29970501,11335377,26030092,5821408,10478196,8544890 }, + }, + { + { 32173121,-16129311,24896207,3921497,22579056,-3410854,19270449,12217473,17789017,-3395995 }, + { -30552961,-2228401,-15578829,-10147201,13243889,517024,15479401,-3853233,30460520,1052596 }, + { -11614875,13323618,32618793,8175907,-15230173,12596687,27491595,-4612359,3179268,-9478891 }, + }, + { + { 31947069,-14366651,-4640583,-15339921,-15125977,-6039709,-14756777,-16411740,19072640,-9511060 }, + { 11685058,11822410,3158003,-13952594,33402194,-4165066,5977896,-5215017,473099,5040608 }, + { -20290863,8198642,-27410132,11602123,1290375,-2799760,28326862,1721092,-19558642,-3131606 }, + }, +}, +{ + { + { 7881532,10687937,7578723,7738378,-18951012,-2553952,21820786,8076149,-27868496,11538389 }, + { -19935666,3899861,18283497,-6801568,-15728660,-11249211,8754525,7446702,-5676054,5797016 }, + { -11295600,-3793569,-15782110,-7964573,12708869,-8456199,2014099,-9050574,-2369172,-5877341 }, + }, + { + { -22472376,-11568741,-27682020,1146375,18956691,16640559,1192730,-3714199,15123619,10811505 }, + { 14352098,-3419715,-18942044,10822655,32750596,4699007,-70363,15776356,-28886779,-11974553 }, + { -28241164,-8072475,-4978962,-5315317,29416931,1847569,-20654173,-16484855,4714547,-9600655 }, + }, + { + { 15200332,8368572,19679101,15970074,-31872674,1959451,24611599,-4543832,-11745876,12340220 }, + { 12876937,-10480056,33134381,6590940,-6307776,14872440,9613953,8241152,15370987,9608631 }, + { -4143277,-12014408,8446281,-391603,4407738,13629032,-7724868,15866074,-28210621,-8814099 }, + }, + { + { 26660628,-15677655,8393734,358047,-7401291,992988,-23904233,858697,20571223,8420556 }, + { 14620715,13067227,-15447274,8264467,14106269,15080814,33531827,12516406,-21574435,-12476749 }, + { 236881,10476226,57258,-14677024,6472998,2466984,17258519,7256740,8791136,15069930 }, + }, + { + { 1276410,-9371918,22949635,-16322807,-23493039,-5702186,14711875,4874229,-30663140,-2331391 }, + { 5855666,4990204,-13711848,7294284,-7804282,1924647,-1423175,-7912378,-33069337,9234253 }, + { 20590503,-9018988,31529744,-7352666,-2706834,10650548,31559055,-11609587,18979186,13396066 }, + }, + { + { 24474287,4968103,22267082,4407354,24063882,-8325180,-18816887,13594782,33514650,7021958 }, + { -11566906,-6565505,-21365085,15928892,-26158305,4315421,-25948728,-3916677,-21480480,12868082 }, + { -28635013,13504661,19988037,-2132761,21078225,6443208,-21446107,2244500,-12455797,-8089383 }, + }, + { + { -30595528,13793479,-5852820,319136,-25723172,-6263899,33086546,8957937,-15233648,5540521 }, + { -11630176,-11503902,-8119500,-7643073,2620056,1022908,-23710744,-1568984,-16128528,-14962807 }, + { 23152971,775386,27395463,14006635,-9701118,4649512,1689819,892185,-11513277,-15205948 }, + }, + { + { 9770129,9586738,26496094,4324120,1556511,-3550024,27453819,4763127,-19179614,5867134 }, + { -32765025,1927590,31726409,-4753295,23962434,-16019500,27846559,5931263,-29749703,-16108455 }, + { 27461885,-2977536,22380810,1815854,-23033753,-3031938,7283490,-15148073,-19526700,7734629 }, + }, +}, +{ + { + { -8010264,-9590817,-11120403,6196038,29344158,-13430885,7585295,-3176626,18549497,15302069 }, + { -32658337,-6171222,-7672793,-11051681,6258878,13504381,10458790,-6418461,-8872242,8424746 }, + { 24687205,8613276,-30667046,-3233545,1863892,-1830544,19206234,7134917,-11284482,-828919 }, + }, + { + { 11334899,-9218022,8025293,12707519,17523892,-10476071,10243738,-14685461,-5066034,16498837 }, + { 8911542,6887158,-9584260,-6958590,11145641,-9543680,17303925,-14124238,6536641,10543906 }, + { -28946384,15479763,-17466835,568876,-1497683,11223454,-2669190,-16625574,-27235709,8876771 }, + }, + { + { -25742899,-12566864,-15649966,-846607,-33026686,-796288,-33481822,15824474,-604426,-9039817 }, + { 10330056,70051,7957388,-9002667,9764902,15609756,27698697,-4890037,1657394,3084098 }, + { 10477963,-7470260,12119566,-13250805,29016247,-5365589,31280319,14396151,-30233575,15272409 }, + }, + { + { -12288309,3169463,28813183,16658753,25116432,-5630466,-25173957,-12636138,-25014757,1950504 }, + { -26180358,9489187,11053416,-14746161,-31053720,5825630,-8384306,-8767532,15341279,8373727 }, + { 28685821,7759505,-14378516,-12002860,-31971820,4079242,298136,-10232602,-2878207,15190420 }, + }, + { + { -32932876,13806336,-14337485,-15794431,-24004620,10940928,8669718,2742393,-26033313,-6875003 }, + { -1580388,-11729417,-25979658,-11445023,-17411874,-10912854,9291594,-16247779,-12154742,6048605 }, + { -30305315,14843444,1539301,11864366,20201677,1900163,13934231,5128323,11213262,9168384 }, + }, + { + { -26280513,11007847,19408960,-940758,-18592965,-4328580,-5088060,-11105150,20470157,-16398701 }, + { -23136053,9282192,14855179,-15390078,-7362815,-14408560,-22783952,14461608,14042978,5230683 }, + { 29969567,-2741594,-16711867,-8552442,9175486,-2468974,21556951,3506042,-5933891,-12449708 }, + }, + { + { -3144746,8744661,19704003,4581278,-20430686,6830683,-21284170,8971513,-28539189,15326563 }, + { -19464629,10110288,-17262528,-3503892,-23500387,1355669,-15523050,15300988,-20514118,9168260 }, + { -5353335,4488613,-23803248,16314347,7780487,-15638939,-28948358,9601605,33087103,-9011387 }, + }, + { + { -19443170,-15512900,-20797467,-12445323,-29824447,10229461,-27444329,-15000531,-5996870,15664672 }, + { 23294591,-16632613,-22650781,-8470978,27844204,11461195,13099750,-2460356,18151676,13417686 }, + { -24722913,-4176517,-31150679,5988919,-26858785,6685065,1661597,-12551441,15271676,-15452665 }, + }, +}, +{ + { + { 11433042,-13228665,8239631,-5279517,-1985436,-725718,-18698764,2167544,-6921301,-13440182 }, + { -31436171,15575146,30436815,12192228,-22463353,9395379,-9917708,-8638997,12215110,12028277 }, + { 14098400,6555944,23007258,5757252,-15427832,-12950502,30123440,4617780,-16900089,-655628 }, + }, + { + { -4026201,-15240835,11893168,13718664,-14809462,1847385,-15819999,10154009,23973261,-12684474 }, + { -26531820,-3695990,-1908898,2534301,-31870557,-16550355,18341390,-11419951,32013174,-10103539 }, + { -25479301,10876443,-11771086,-14625140,-12369567,1838104,21911214,6354752,4425632,-837822 }, + }, + { + { -10433389,-14612966,22229858,-3091047,-13191166,776729,-17415375,-12020462,4725005,14044970 }, + { 19268650,-7304421,1555349,8692754,-21474059,-9910664,6347390,-1411784,-19522291,-16109756 }, + { -24864089,12986008,-10898878,-5558584,-11312371,-148526,19541418,8180106,9282262,10282508 }, + }, + { + { -26205082,4428547,-8661196,-13194263,4098402,-14165257,15522535,8372215,5542595,-10702683 }, + { -10562541,14895633,26814552,-16673850,-17480754,-2489360,-2781891,6993761,-18093885,10114655 }, + { -20107055,-929418,31422704,10427861,-7110749,6150669,-29091755,-11529146,25953725,-106158 }, + }, + { + { -4234397,-8039292,-9119125,3046000,2101609,-12607294,19390020,6094296,-3315279,12831125 }, + { -15998678,7578152,5310217,14408357,-33548620,-224739,31575954,6326196,7381791,-2421839 }, + { -20902779,3296811,24736065,-16328389,18374254,7318640,6295303,8082724,-15362489,12339664 }, + }, + { + { 27724736,2291157,6088201,-14184798,1792727,5857634,13848414,15768922,25091167,14856294 }, + { -18866652,8331043,24373479,8541013,-701998,-9269457,12927300,-12695493,-22182473,-9012899 }, + { -11423429,-5421590,11632845,3405020,30536730,-11674039,-27260765,13866390,30146206,9142070 }, + }, + { + { 3924129,-15307516,-13817122,-10054960,12291820,-668366,-27702774,9326384,-8237858,4171294 }, + { -15921940,16037937,6713787,16606682,-21612135,2790944,26396185,3731949,345228,-5462949 }, + { -21327538,13448259,25284571,1143661,20614966,-8849387,2031539,-12391231,-16253183,-13582083 }, + }, + { + { 31016211,-16722429,26371392,-14451233,-5027349,14854137,17477601,3842657,28012650,-16405420 }, + { -5075835,9368966,-8562079,-4600902,-15249953,6970560,-9189873,16292057,-8867157,3507940 }, + { 29439664,3537914,23333589,6997794,-17555561,-11018068,-15209202,-15051267,-9164929,6580396 }, + }, +}, +{ + { + { -12185861,-7679788,16438269,10826160,-8696817,-6235611,17860444,-9273846,-2095802,9304567 }, + { 20714564,-4336911,29088195,7406487,11426967,-5095705,14792667,-14608617,5289421,-477127 }, + { -16665533,-10650790,-6160345,-13305760,9192020,-1802462,17271490,12349094,26939669,-3752294 }, + }, + { + { -12889898,9373458,31595848,16374215,21471720,13221525,-27283495,-12348559,-3698806,117887 }, + { 22263325,-6560050,3984570,-11174646,-15114008,-566785,28311253,5358056,-23319780,541964 }, + { 16259219,3261970,2309254,-15534474,-16885711,-4581916,24134070,-16705829,-13337066,-13552195 }, + }, + { + { 9378160,-13140186,-22845982,-12745264,28198281,-7244098,-2399684,-717351,690426,14876244 }, + { 24977353,-314384,-8223969,-13465086,28432343,-1176353,-13068804,-12297348,-22380984,6618999 }, + { -1538174,11685646,12944378,13682314,-24389511,-14413193,8044829,-13817328,32239829,-5652762 }, + }, + { + { -18603066,4762990,-926250,8885304,-28412480,-3187315,9781647,-10350059,32779359,5095274 }, + { -33008130,-5214506,-32264887,-3685216,9460461,-9327423,-24601656,14506724,21639561,-2630236 }, + { -16400943,-13112215,25239338,15531969,3987758,-4499318,-1289502,-6863535,17874574,558605 }, + }, + { + { -13600129,10240081,9171883,16131053,-20869254,9599700,33499487,5080151,2085892,5119761 }, + { -22205145,-2519528,-16381601,414691,-25019550,2170430,30634760,-8363614,-31999993,-5759884 }, + { -6845704,15791202,8550074,-1312654,29928809,-12092256,27534430,-7192145,-22351378,12961482 }, + }, + { + { -24492060,-9570771,10368194,11582341,-23397293,-2245287,16533930,8206996,-30194652,-5159638 }, + { -11121496,-3382234,2307366,6362031,-135455,8868177,-16835630,7031275,7589640,8945490 }, + { -32152748,8917967,6661220,-11677616,-1192060,-15793393,7251489,-11182180,24099109,-14456170 }, + }, + { + { 5019558,-7907470,4244127,-14714356,-26933272,6453165,-19118182,-13289025,-6231896,-10280736 }, + { 10853594,10721687,26480089,5861829,-22995819,1972175,-1866647,-10557898,-3363451,-6441124 }, + { -17002408,5906790,221599,-6563147,7828208,-13248918,24362661,-2008168,-13866408,7421392 }, + }, + { + { 8139927,-6546497,32257646,-5890546,30375719,1886181,-21175108,15441252,28826358,-4123029 }, + { 6267086,9695052,7709135,-16603597,-32869068,-1886135,14795160,-7840124,13746021,-1742048 }, + { 28584902,7787108,-6732942,-15050729,22846041,-7571236,-3181936,-363524,4771362,-8419958 }, + }, +}, +{ + { + { 24949256,6376279,-27466481,-8174608,-18646154,-9930606,33543569,-12141695,3569627,11342593 }, + { 26514989,4740088,27912651,3697550,19331575,-11472339,6809886,4608608,7325975,-14801071 }, + { -11618399,-14554430,-24321212,7655128,-1369274,5214312,-27400540,10258390,-17646694,-8186692 }, + }, + { + { 11431204,15823007,26570245,14329124,18029990,4796082,-31446179,15580664,9280358,-3973687 }, + { -160783,-10326257,-22855316,-4304997,-20861367,-13621002,-32810901,-11181622,-15545091,4387441 }, + { -20799378,12194512,3937617,-5805892,-27154820,9340370,-24513992,8548137,20617071,-7482001 }, + }, + { + { -938825,-3930586,-8714311,16124718,24603125,-6225393,-13775352,-11875822,24345683,10325460 }, + { -19855277,-1568885,-22202708,8714034,14007766,6928528,16318175,-1010689,4766743,3552007 }, + { -21751364,-16730916,1351763,-803421,-4009670,3950935,3217514,14481909,10988822,-3994762 }, + }, + { + { 15564307,-14311570,3101243,5684148,30446780,-8051356,12677127,-6505343,-8295852,13296005 }, + { -9442290,6624296,-30298964,-11913677,-4670981,-2057379,31521204,9614054,-30000824,12074674 }, + { 4771191,-135239,14290749,-13089852,27992298,14998318,-1413936,-1556716,29832613,-16391035 }, + }, + { + { 7064884,-7541174,-19161962,-5067537,-18891269,-2912736,25825242,5293297,-27122660,13101590 }, + { -2298563,2439670,-7466610,1719965,-27267541,-16328445,32512469,-5317593,-30356070,-4190957 }, + { -30006540,10162316,-33180176,3981723,-16482138,-13070044,14413974,9515896,19568978,9628812 }, + }, + { + { 33053803,199357,15894591,1583059,27380243,-4580435,-17838894,-6106839,-6291786,3437740 }, + { -18978877,3884493,19469877,12726490,15913552,13614290,-22961733,70104,7463304,4176122 }, + { -27124001,10659917,11482427,-16070381,12771467,-6635117,-32719404,-5322751,24216882,5944158 }, + }, + { + { 8894125,7450974,-2664149,-9765752,-28080517,-12389115,19345746,14680796,11632993,5847885 }, + { 26942781,-2315317,9129564,-4906607,26024105,11769399,-11518837,6367194,-9727230,4782140 }, + { 19916461,-4828410,-22910704,-11414391,25606324,-5972441,33253853,8220911,6358847,-1873857 }, + }, + { + { 801428,-2081702,16569428,11065167,29875704,96627,7908388,-4480480,-13538503,1387155 }, + { 19646058,5720633,-11416706,12814209,11607948,12749789,14147075,15156355,-21866831,11835260 }, + { 19299512,1155910,28703737,14890794,2925026,7269399,26121523,15467869,-26560550,5052483 }, + }, +}, +{ + { + { -3017432,10058206,1980837,3964243,22160966,12322533,-6431123,-12618185,12228557,-7003677 }, + { 32944382,14922211,-22844894,5188528,21913450,-8719943,4001465,13238564,-6114803,8653815 }, + { 22865569,-4652735,27603668,-12545395,14348958,8234005,24808405,5719875,28483275,2841751 }, + }, + { + { -16420968,-1113305,-327719,-12107856,21886282,-15552774,-1887966,-315658,19932058,-12739203 }, + { -11656086,10087521,-8864888,-5536143,-19278573,-3055912,3999228,13239134,-4777469,-13910208 }, + { 1382174,-11694719,17266790,9194690,-13324356,9720081,20403944,11284705,-14013818,3093230 }, + }, + { + { 16650921,-11037932,-1064178,1570629,-8329746,7352753,-302424,16271225,-24049421,-6691850 }, + { -21911077,-5927941,-4611316,-5560156,-31744103,-10785293,24123614,15193618,-21652117,-16739389 }, + { -9935934,-4289447,-25279823,4372842,2087473,10399484,31870908,14690798,17361620,11864968 }, + }, + { + { -11307610,6210372,13206574,5806320,-29017692,-13967200,-12331205,-7486601,-25578460,-16240689 }, + { 14668462,-12270235,26039039,15305210,25515617,4542480,10453892,6577524,9145645,-6443880 }, + { 5974874,3053895,-9433049,-10385191,-31865124,3225009,-7972642,3936128,-5652273,-3050304 }, + }, + { + { 30625386,-4729400,-25555961,-12792866,-20484575,7695099,17097188,-16303496,-27999779,1803632 }, + { -3553091,9865099,-5228566,4272701,-5673832,-16689700,14911344,12196514,-21405489,7047412 }, + { 20093277,9920966,-11138194,-5343857,13161587,12044805,-32856851,4124601,-32343828,-10257566 }, + }, + { + { -20788824,14084654,-13531713,7842147,19119038,-13822605,4752377,-8714640,-21679658,2288038 }, + { -26819236,-3283715,29965059,3039786,-14473765,2540457,29457502,14625692,-24819617,12570232 }, + { -1063558,-11551823,16920318,12494842,1278292,-5869109,-21159943,-3498680,-11974704,4724943 }, + }, + { + { 17960970,-11775534,-4140968,-9702530,-8876562,-1410617,-12907383,-8659932,-29576300,1903856 }, + { 23134274,-14279132,-10681997,-1611936,20684485,15770816,-12989750,3190296,26955097,14109738 }, + { 15308788,5320727,-30113809,-14318877,22902008,7767164,29425325,-11277562,31960942,11934971 }, + }, + { + { -27395711,8435796,4109644,12222639,-24627868,14818669,20638173,4875028,10491392,1379718 }, + { -13159415,9197841,3875503,-8936108,-1383712,-5879801,33518459,16176658,21432314,12180697 }, + { -11787308,11500838,13787581,-13832590,-22430679,10140205,1465425,12689540,-10301319,-13872883 }, + }, +}, +{ + { + { 5414091,-15386041,-21007664,9643570,12834970,1186149,-2622916,-1342231,26128231,6032912 }, + { -26337395,-13766162,32496025,-13653919,17847801,-12669156,3604025,8316894,-25875034,-10437358 }, + { 3296484,6223048,24680646,-12246460,-23052020,5903205,-8862297,-4639164,12376617,3188849 }, + }, + { + { 29190488,-14659046,27549113,-1183516,3520066,-10697301,32049515,-7309113,-16109234,-9852307 }, + { -14744486,-9309156,735818,-598978,-20407687,-5057904,25246078,-15795669,18640741,-960977 }, + { -6928835,-16430795,10361374,5642961,4910474,12345252,-31638386,-494430,10530747,1053335 }, + }, + { + { -29265967,-14186805,-13538216,-12117373,-19457059,-10655384,-31462369,-2948985,24018831,15026644 }, + { -22592535,-3145277,-2289276,5953843,-13440189,9425631,25310643,13003497,-2314791,-15145616 }, + { -27419985,-603321,-8043984,-1669117,-26092265,13987819,-27297622,187899,-23166419,-2531735 }, + }, + { + { -21744398,-13810475,1844840,5021428,-10434399,-15911473,9716667,16266922,-5070217,726099 }, + { 29370922,-6053998,7334071,-15342259,9385287,2247707,-13661962,-4839461,30007388,-15823341 }, + { -936379,16086691,23751945,-543318,-1167538,-5189036,9137109,730663,9835848,4555336 }, + }, + { + { -23376435,1410446,-22253753,-12899614,30867635,15826977,17693930,544696,-11985298,12422646 }, + { 31117226,-12215734,-13502838,6561947,-9876867,-12757670,-5118685,-4096706,29120153,13924425 }, + { -17400879,-14233209,19675799,-2734756,-11006962,-5858820,-9383939,-11317700,7240931,-237388 }, + }, + { + { -31361739,-11346780,-15007447,-5856218,-22453340,-12152771,1222336,4389483,3293637,-15551743 }, + { -16684801,-14444245,11038544,11054958,-13801175,-3338533,-24319580,7733547,12796905,-6335822 }, + { -8759414,-10817836,-25418864,10783769,-30615557,-9746811,-28253339,3647836,3222231,-11160462 }, + }, + { + { 18606113,1693100,-25448386,-15170272,4112353,10045021,23603893,-2048234,-7550776,2484985 }, + { 9255317,-3131197,-12156162,-1004256,13098013,-9214866,16377220,-2102812,-19802075,-3034702 }, + { -22729289,7496160,-5742199,11329249,19991973,-3347502,-31718148,9936966,-30097688,-10618797 }, + }, + { + { 21878590,-5001297,4338336,13643897,-3036865,13160960,19708896,5415497,-7360503,-4109293 }, + { 27736861,10103576,12500508,8502413,-3413016,-9633558,10436918,-1550276,-23659143,-8132100 }, + { 19492550,-12104365,-29681976,-852630,-3208171,12403437,30066266,8367329,13243957,8709688 }, + }, +}, +{ + { + { 12015105,2801261,28198131,10151021,24818120,-4743133,-11194191,-5645734,5150968,7274186 }, + { 2831366,-12492146,1478975,6122054,23825128,-12733586,31097299,6083058,31021603,-9793610 }, + { -2529932,-2229646,445613,10720828,-13849527,-11505937,-23507731,16354465,15067285,-14147707 }, + }, + { + { 7840942,14037873,-33364863,15934016,-728213,-3642706,21403988,1057586,-19379462,-12403220 }, + { 915865,-16469274,15608285,-8789130,-24357026,6060030,-17371319,8410997,-7220461,16527025 }, + { 32922597,-556987,20336074,-16184568,10903705,-5384487,16957574,52992,23834301,6588044 }, + }, + { + { 32752030,11232950,3381995,-8714866,22652988,-10744103,17159699,16689107,-20314580,-1305992 }, + { -4689649,9166776,-25710296,-10847306,11576752,12733943,7924251,-2752281,1976123,-7249027 }, + { 21251222,16309901,-2983015,-6783122,30810597,12967303,156041,-3371252,12331345,-8237197 }, + }, + { + { 8651614,-4477032,-16085636,-4996994,13002507,2950805,29054427,-5106970,10008136,-4667901 }, + { 31486080,15114593,-14261250,12951354,14369431,-7387845,16347321,-13662089,8684155,-10532952 }, + { 19443825,11385320,24468943,-9659068,-23919258,2187569,-26263207,-6086921,31316348,14219878 }, + }, + { + { -28594490,1193785,32245219,11392485,31092169,15722801,27146014,6992409,29126555,9207390 }, + { 32382935,1110093,18477781,11028262,-27411763,-7548111,-4980517,10843782,-7957600,-14435730 }, + { 2814918,7836403,27519878,-7868156,-20894015,-11553689,-21494559,8550130,28346258,1994730 }, + }, + { + { -19578299,8085545,-14000519,-3948622,2785838,-16231307,-19516951,7174894,22628102,8115180 }, + { -30405132,955511,-11133838,-15078069,-32447087,-13278079,-25651578,3317160,-9943017,930272 }, + { -15303681,-6833769,28856490,1357446,23421993,1057177,24091212,-1388970,-22765376,-10650715 }, + }, + { + { -22751231,-5303997,-12907607,-12768866,-15811511,-7797053,-14839018,-16554220,-1867018,8398970 }, + { -31969310,2106403,-4736360,1362501,12813763,16200670,22981545,-6291273,18009408,-15772772 }, + { -17220923,-9545221,-27784654,14166835,29815394,7444469,29551787,-3727419,19288549,1325865 }, + }, + { + { 15100157,-15835752,-23923978,-1005098,-26450192,15509408,12376730,-3479146,33166107,-8042750 }, + { 20909231,13023121,-9209752,16251778,-5778415,-8094914,12412151,10018715,2213263,-13878373 }, + { 32529814,-11074689,30361439,-16689753,-9135940,1513226,22922121,6382134,-5766928,8371348 }, + }, +}, +{ + { + { 9923462,11271500,12616794,3544722,-29998368,-1721626,12891687,-8193132,-26442943,10486144 }, + { -22597207,-7012665,8587003,-8257861,4084309,-12970062,361726,2610596,-23921530,-11455195 }, + { 5408411,-1136691,-4969122,10561668,24145918,14240566,31319731,-4235541,19985175,-3436086 }, + }, + { + { -13994457,16616821,14549246,3341099,32155958,13648976,-17577068,8849297,65030,8370684 }, + { -8320926,-12049626,31204563,5839400,-20627288,-1057277,-19442942,6922164,12743482,-9800518 }, + { -2361371,12678785,28815050,4759974,-23893047,4884717,23783145,11038569,18800704,255233 }, + }, + { + { -5269658,-1773886,13957886,7990715,23132995,728773,13393847,9066957,19258688,-14753793 }, + { -2936654,-10827535,-10432089,14516793,-3640786,4372541,-31934921,2209390,-1524053,2055794 }, + { 580882,16705327,5468415,-2683018,-30926419,-14696000,-7203346,-8994389,-30021019,7394435 }, + }, + { + { 23838809,1822728,-15738443,15242727,8318092,-3733104,-21672180,-3492205,-4821741,14799921 }, + { 13345610,9759151,3371034,-16137791,16353039,8577942,31129804,13496856,-9056018,7402518 }, + { 2286874,-4435931,-20042458,-2008336,-13696227,5038122,11006906,-15760352,8205061,1607563 }, + }, + { + { 14414086,-8002132,3331830,-3208217,22249151,-5594188,18364661,-2906958,30019587,-9029278 }, + { -27688051,1585953,-10775053,931069,-29120221,-11002319,-14410829,12029093,9944378,8024 }, + { 4368715,-3709630,29874200,-15022983,-20230386,-11410704,-16114594,-999085,-8142388,5640030 }, + }, + { + { 10299610,13746483,11661824,16234854,7630238,5998374,9809887,-16694564,15219798,-14327783 }, + { 27425505,-5719081,3055006,10660664,23458024,595578,-15398605,-1173195,-18342183,9742717 }, + { 6744077,2427284,26042789,2720740,-847906,1118974,32324614,7406442,12420155,1994844 }, + }, + { + { 14012521,-5024720,-18384453,-9578469,-26485342,-3936439,-13033478,-10909803,24319929,-6446333 }, + { 16412690,-4507367,10772641,15929391,-17068788,-4658621,10555945,-10484049,-30102368,-4739048 }, + { 22397382,-7767684,-9293161,-12792868,17166287,-9755136,-27333065,6199366,21880021,-12250760 }, + }, + { + { -4283307,5368523,-31117018,8163389,-30323063,3209128,16557151,8890729,8840445,4957760 }, + { -15447727,709327,-6919446,-10870178,-29777922,6522332,-21720181,12130072,-14796503,5005757 }, + { -2114751,-14308128,23019042,15765735,-25269683,6002752,10183197,-13239326,-16395286,-2176112 }, + }, +}, +{ + { + { -19025756,1632005,13466291,-7995100,-23640451,16573537,-32013908,-3057104,22208662,2000468 }, + { 3065073,-1412761,-25598674,-361432,-17683065,-5703415,-8164212,11248527,-3691214,-7414184 }, + { 10379208,-6045554,8877319,1473647,-29291284,-12507580,16690915,2553332,-3132688,16400289 }, + }, + { + { 15716668,1254266,-18472690,7446274,-8448918,6344164,-22097271,-7285580,26894937,9132066 }, + { 24158887,12938817,11085297,-8177598,-28063478,-4457083,-30576463,64452,-6817084,-2692882 }, + { 13488534,7794716,22236231,5989356,25426474,-12578208,2350710,-3418511,-4688006,2364226 }, + }, + { + { 16335052,9132434,25640582,6678888,1725628,8517937,-11807024,-11697457,15445875,-7798101 }, + { 29004207,-7867081,28661402,-640412,-12794003,-7943086,31863255,-4135540,-278050,-15759279 }, + { -6122061,-14866665,-28614905,14569919,-10857999,-3591829,10343412,-6976290,-29828287,-10815811 }, + }, + { + { 27081650,3463984,14099042,-4517604,1616303,-6205604,29542636,15372179,17293797,960709 }, + { 20263915,11434237,-5765435,11236810,13505955,-10857102,-16111345,6493122,-19384511,7639714 }, + { -2830798,-14839232,25403038,-8215196,-8317012,-16173699,18006287,-16043750,29994677,-15808121 }, + }, + { + { 9769828,5202651,-24157398,-13631392,-28051003,-11561624,-24613141,-13860782,-31184575,709464 }, + { 12286395,13076066,-21775189,-1176622,-25003198,4057652,-32018128,-8890874,16102007,13205847 }, + { 13733362,5599946,10557076,3195751,-5557991,8536970,-25540170,8525972,10151379,10394400 }, + }, + { + { 4024660,-16137551,22436262,12276534,-9099015,-2686099,19698229,11743039,-33302334,8934414 }, + { -15879800,-4525240,-8580747,-2934061,14634845,-698278,-9449077,3137094,-11536886,11721158 }, + { 17555939,-5013938,8268606,2331751,-22738815,9761013,9319229,8835153,-9205489,-1280045 }, + }, + { + { -461409,-7830014,20614118,16688288,-7514766,-4807119,22300304,505429,6108462,-6183415 }, + { -5070281,12367917,-30663534,3234473,32617080,-8422642,29880583,-13483331,-26898490,-7867459 }, + { -31975283,5726539,26934134,10237677,-3173717,-605053,24199304,3795095,7592688,-14992079 }, + }, + { + { 21594432,-14964228,17466408,-4077222,32537084,2739898,6407723,12018833,-28256052,4298412 }, + { -20650503,-11961496,-27236275,570498,3767144,-1717540,13891942,-1569194,13717174,10805743 }, + { -14676630,-15644296,15287174,11927123,24177847,-8175568,-796431,14860609,-26938930,-5863836 }, + }, +}, +{ + { + { 12962541,5311799,-10060768,11658280,18855286,-7954201,13286263,-12808704,-4381056,9882022 }, + { 18512079,11319350,-20123124,15090309,18818594,5271736,-22727904,3666879,-23967430,-3299429 }, + { -6789020,-3146043,16192429,13241070,15898607,-14206114,-10084880,-6661110,-2403099,5276065 }, + }, + { + { 30169808,-5317648,26306206,-11750859,27814964,7069267,7152851,3684982,1449224,13082861 }, + { 10342826,3098505,2119311,193222,25702612,12233820,23697382,15056736,-21016438,-8202000 }, + { -33150110,3261608,22745853,7948688,19370557,-15177665,-26171976,6482814,-10300080,-11060101 }, + }, + { + { 32869458,-5408545,25609743,15678670,-10687769,-15471071,26112421,2521008,-22664288,6904815 }, + { 29506923,4457497,3377935,-9796444,-30510046,12935080,1561737,3841096,-29003639,-6657642 }, + { 10340844,-6630377,-18656632,-2278430,12621151,-13339055,30878497,-11824370,-25584551,5181966 }, + }, + { + { 25940115,-12658025,17324188,-10307374,-8671468,15029094,24396252,-16450922,-2322852,-12388574 }, + { -21765684,9916823,-1300409,4079498,-1028346,11909559,1782390,12641087,20603771,-6561742 }, + { -18882287,-11673380,24849422,11501709,13161720,-4768874,1925523,11914390,4662781,7820689 }, + }, + { + { 12241050,-425982,8132691,9393934,32846760,-1599620,29749456,12172924,16136752,15264020 }, + { -10349955,-14680563,-8211979,2330220,-17662549,-14545780,10658213,6671822,19012087,3772772 }, + { 3753511,-3421066,10617074,2028709,14841030,-6721664,28718732,-15762884,20527771,12988982 }, + }, + { + { -14822485,-5797269,-3707987,12689773,-898983,-10914866,-24183046,-10564943,3299665,-12424953 }, + { -16777703,-15253301,-9642417,4978983,3308785,8755439,6943197,6461331,-25583147,8991218 }, + { -17226263,1816362,-1673288,-6086439,31783888,-8175991,-32948145,7417950,-30242287,1507265 }, + }, + { + { 29692663,6829891,-10498800,4334896,20945975,-11906496,-28887608,8209391,14606362,-10647073 }, + { -3481570,8707081,32188102,5672294,22096700,1711240,-33020695,9761487,4170404,-2085325 }, + { -11587470,14855945,-4127778,-1531857,-26649089,15084046,22186522,16002000,-14276837,-8400798 }, + }, + { + { -4811456,13761029,-31703877,-2483919,-3312471,7869047,-7113572,-9620092,13240845,10965870 }, + { -7742563,-8256762,-14768334,-13656260,-23232383,12387166,4498947,14147411,29514390,4302863 }, + { -13413405,-12407859,20757302,-13801832,14785143,8976368,-5061276,-2144373,17846988,-13971927 }, + }, +}, +{ + { + { -2244452,-754728,-4597030,-1066309,-6247172,1455299,-21647728,-9214789,-5222701,12650267 }, + { -9906797,-16070310,21134160,12198166,-27064575,708126,387813,13770293,-19134326,10958663 }, + { 22470984,12369526,23446014,-5441109,-21520802,-9698723,-11772496,-11574455,-25083830,4271862 }, + }, + { + { -25169565,-10053642,-19909332,15361595,-5984358,2159192,75375,-4278529,-32526221,8469673 }, + { 15854970,4148314,-8893890,7259002,11666551,13824734,-30531198,2697372,24154791,-9460943 }, + { 15446137,-15806644,29759747,14019369,30811221,-9610191,-31582008,12840104,24913809,9815020 }, + }, + { + { -4709286,-5614269,-31841498,-12288893,-14443537,10799414,-9103676,13438769,18735128,9466238 }, + { 11933045,9281483,5081055,-5183824,-2628162,-4905629,-7727821,-10896103,-22728655,16199064 }, + { 14576810,379472,-26786533,-8317236,-29426508,-10812974,-102766,1876699,30801119,2164795 }, + }, + { + { 15995086,3199873,13672555,13712240,-19378835,-4647646,-13081610,-15496269,-13492807,1268052 }, + { -10290614,-3659039,-3286592,10948818,23037027,3794475,-3470338,-12600221,-17055369,3565904 }, + { 29210088,-9419337,-5919792,-4952785,10834811,-13327726,-16512102,-10820713,-27162222,-14030531 }, + }, + { + { -13161890,15508588,16663704,-8156150,-28349942,9019123,-29183421,-3769423,2244111,-14001979 }, + { -5152875,-3800936,-9306475,-6071583,16243069,14684434,-25673088,-16180800,13491506,4641841 }, + { 10813417,643330,-19188515,-728916,30292062,-16600078,27548447,-7721242,14476989,-12767431 }, + }, + { + { 10292079,9984945,6481436,8279905,-7251514,7032743,27282937,-1644259,-27912810,12651324 }, + { -31185513,-813383,22271204,11835308,10201545,15351028,17099662,3988035,21721536,-3148940 }, + { 10202177,-6545839,-31373232,-9574638,-32150642,-8119683,-12906320,3852694,13216206,14842320 }, + }, + { + { -15815640,-10601066,-6538952,-7258995,-6984659,-6581778,-31500847,13765824,-27434397,9900184 }, + { 14465505,-13833331,-32133984,-14738873,-27443187,12990492,33046193,15796406,-7051866,-8040114 }, + { 30924417,-8279620,6359016,-12816335,16508377,9071735,-25488601,15413635,9524356,-7018878 }, + }, + { + { 12274201,-13175547,32627641,-1785326,6736625,13267305,5237659,-5109483,15663516,4035784 }, + { -2951309,8903985,17349946,601635,-16432815,-4612556,-13732739,-15889334,-22258478,4659091 }, + { -16916263,-4952973,-30393711,-15158821,20774812,15897498,5736189,15026997,-2178256,-13455585 }, + }, +}, +{ + { + { -8858980,-2219056,28571666,-10155518,-474467,-10105698,-3801496,278095,23440562,-290208 }, + { 10226241,-5928702,15139956,120818,-14867693,5218603,32937275,11551483,-16571960,-7442864 }, + { 17932739,-12437276,-24039557,10749060,11316803,7535897,22503767,5561594,-3646624,3898661 }, + }, + { + { 7749907,-969567,-16339731,-16464,-25018111,15122143,-1573531,7152530,21831162,1245233 }, + { 26958459,-14658026,4314586,8346991,-5677764,11960072,-32589295,-620035,-30402091,-16716212 }, + { -12165896,9166947,33491384,13673479,29787085,13096535,6280834,14587357,-22338025,13987525 }, + }, + { + { -24349909,7778775,21116000,15572597,-4833266,-5357778,-4300898,-5124639,-7469781,-2858068 }, + { 9681908,-6737123,-31951644,13591838,-6883821,386950,31622781,6439245,-14581012,4091397 }, + { -8426427,1470727,-28109679,-1596990,3978627,-5123623,-19622683,12092163,29077877,-14741988 }, + }, + { + { 5269168,-6859726,-13230211,-8020715,25932563,1763552,-5606110,-5505881,-20017847,2357889 }, + { 32264008,-15407652,-5387735,-1160093,-2091322,-3946900,23104804,-12869908,5727338,189038 }, + { 14609123,-8954470,-6000566,-16622781,-14577387,-7743898,-26745169,10942115,-25888931,-14884697 }, + }, + { + { 20513500,5557931,-15604613,7829531,26413943,-2019404,-21378968,7471781,13913677,-5137875 }, + { -25574376,11967826,29233242,12948236,-6754465,4713227,-8940970,14059180,12878652,8511905 }, + { -25656801,3393631,-2955415,-7075526,-2250709,9366908,-30223418,6812974,5568676,-3127656 }, + }, + { + { 11630004,12144454,2116339,13606037,27378885,15676917,-17408753,-13504373,-14395196,8070818 }, + { 27117696,-10007378,-31282771,-5570088,1127282,12772488,-29845906,10483306,-11552749,-1028714 }, + { 10637467,-5688064,5674781,1072708,-26343588,-6982302,-1683975,9177853,-27493162,15431203 }, + }, + { + { 20525145,10892566,-12742472,12779443,-29493034,16150075,-28240519,14943142,-15056790,-7935931 }, + { -30024462,5626926,-551567,-9981087,753598,11981191,25244767,-3239766,-3356550,9594024 }, + { -23752644,2636870,-5163910,-10103818,585134,7877383,11345683,-6492290,13352335,-10977084 }, + }, + { + { -1931799,-5407458,3304649,-12884869,17015806,-4877091,-29783850,-7752482,-13215537,-319204 }, + { 20239939,6607058,6203985,3483793,-18386976,-779229,-20723742,15077870,-22750759,14523817 }, + { 27406042,-6041657,27423596,-4497394,4996214,10002360,-28842031,-4545494,-30172742,-4805667 }, + }, +}, +{ + { + { 11374242,12660715,17861383,-12540833,10935568,1099227,-13886076,-9091740,-27727044,11358504 }, + { -12730809,10311867,1510375,10778093,-2119455,-9145702,32676003,11149336,-26123651,4985768 }, + { -19096303,341147,-6197485,-239033,15756973,-8796662,-983043,13794114,-19414307,-15621255 }, + }, + { + { 6490081,11940286,25495923,-7726360,8668373,-8751316,3367603,6970005,-1691065,-9004790 }, + { 1656497,13457317,15370807,6364910,13605745,8362338,-19174622,-5475723,-16796596,-5031438 }, + { -22273315,-13524424,-64685,-4334223,-18605636,-10921968,-20571065,-7007978,-99853,-10237333 }, + }, + { + { 17747465,10039260,19368299,-4050591,-20630635,-16041286,31992683,-15857976,-29260363,-5511971 }, + { 31932027,-4986141,-19612382,16366580,22023614,88450,11371999,-3744247,4882242,-10626905 }, + { 29796507,37186,19818052,10115756,-11829032,3352736,18551198,3272828,-5190932,-4162409 }, + }, + { + { 12501286,4044383,-8612957,-13392385,-32430052,5136599,-19230378,-3529697,330070,-3659409 }, + { 6384877,2899513,17807477,7663917,-2358888,12363165,25366522,-8573892,-271295,12071499 }, + { -8365515,-4042521,25133448,-4517355,-6211027,2265927,-32769618,1936675,-5159697,3829363 }, + }, + { + { 28425966,-5835433,-577090,-4697198,-14217555,6870930,7921550,-6567787,26333140,14267664 }, + { -11067219,11871231,27385719,-10559544,-4585914,-11189312,10004786,-8709488,-21761224,8930324 }, + { -21197785,-16396035,25654216,-1725397,12282012,11008919,1541940,4757911,-26491501,-16408940 }, + }, + { + { 13537262,-7759490,-20604840,10961927,-5922820,-13218065,-13156584,6217254,-15943699,13814990 }, + { -17422573,15157790,18705543,29619,24409717,-260476,27361681,9257833,-1956526,-1776914 }, + { -25045300,-10191966,15366585,15166509,-13105086,8423556,-29171540,12361135,-18685978,4578290 }, + }, + { + { 24579768,3711570,1342322,-11180126,-27005135,14124956,-22544529,14074919,21964432,8235257 }, + { -6528613,-2411497,9442966,-5925588,12025640,-1487420,-2981514,-1669206,13006806,2355433 }, + { -16304899,-13605259,-6632427,-5142349,16974359,-10911083,27202044,1719366,1141648,-12796236 }, + }, + { + { -12863944,-13219986,-8318266,-11018091,-6810145,-4843894,13475066,-3133972,32674895,13715045 }, + { 11423335,-5468059,32344216,8962751,24989809,9241752,-13265253,16086212,-28740881,-15642093 }, + { -1409668,12530728,-6368726,10847387,19531186,-14132160,-11709148,7791794,-27245943,4383347 }, + }, +}, +{ + { + { -28970898,5271447,-1266009,-9736989,-12455236,16732599,-4862407,-4906449,27193557,6245191 }, + { -15193956,5362278,-1783893,2695834,4960227,12840725,23061898,3260492,22510453,8577507 }, + { -12632451,11257346,-32692994,13548177,-721004,10879011,31168030,13952092,-29571492,-3635906 }, + }, + { + { 3877321,-9572739,32416692,5405324,-11004407,-13656635,3759769,11935320,5611860,8164018 }, + { -16275802,14667797,15906460,12155291,-22111149,-9039718,32003002,-8832289,5773085,-8422109 }, + { -23788118,-8254300,1950875,8937633,18686727,16459170,-905725,12376320,31632953,190926 }, + }, + { + { -24593607,-16138885,-8423991,13378746,14162407,6901328,-8288749,4508564,-25341555,-3627528 }, + { 8884438,-5884009,6023974,10104341,-6881569,-4941533,18722941,-14786005,-1672488,827625 }, + { -32720583,-16289296,-32503547,7101210,13354605,2659080,-1800575,-14108036,-24878478,1541286 }, + }, + { + { 2901347,-1117687,3880376,-10059388,-17620940,-3612781,-21802117,-3567481,20456845,-1885033 }, + { 27019610,12299467,-13658288,-1603234,-12861660,-4861471,-19540150,-5016058,29439641,15138866 }, + { 21536104,-6626420,-32447818,-10690208,-22408077,5175814,-5420040,-16361163,7779328,109896 }, + }, + { + { 30279744,14648750,-8044871,6425558,13639621,-743509,28698390,12180118,23177719,-554075 }, + { 26572847,3405927,-31701700,12890905,-19265668,5335866,-6493768,2378492,4439158,-13279347 }, + { -22716706,3489070,-9225266,-332753,18875722,-1140095,14819434,-12731527,-17717757,-5461437 }, + }, + { + { -5056483,16566551,15953661,3767752,-10436499,15627060,-820954,2177225,8550082,-15114165 }, + { -18473302,16596775,-381660,15663611,22860960,15585581,-27844109,-3582739,-23260460,-8428588 }, + { -32480551,15707275,-8205912,-5652081,29464558,2713815,-22725137,15860482,-21902570,1494193 }, + }, + { + { -19562091,-14087393,-25583872,-9299552,13127842,759709,21923482,16529112,8742704,12967017 }, + { -28464899,1553205,32536856,-10473729,-24691605,-406174,-8914625,-2933896,-29903758,15553883 }, + { 21877909,3230008,9881174,10539357,-4797115,2841332,11543572,14513274,19375923,-12647961 }, + }, + { + { 8832269,-14495485,13253511,5137575,5037871,4078777,24880818,-6222716,2862653,9455043 }, + { 29306751,5123106,20245049,-14149889,9592566,8447059,-2077124,-2990080,15511449,4789663 }, + { -20679756,7004547,8824831,-9434977,-4045704,-3750736,-5754762,108893,23513200,16652362 }, + }, +}, +{ + { + { -33256173,4144782,-4476029,-6579123,10770039,-7155542,-6650416,-12936300,-18319198,10212860 }, + { 2756081,8598110,7383731,-6859892,22312759,-1105012,21179801,2600940,-9988298,-12506466 }, + { -24645692,13317462,-30449259,-15653928,21365574,-10869657,11344424,864440,-2499677,-16710063 }, + }, + { + { -26432803,6148329,-17184412,-14474154,18782929,-275997,-22561534,211300,2719757,4940997 }, + { -1323882,3911313,-6948744,14759765,-30027150,7851207,21690126,8518463,26699843,5276295 }, + { -13149873,-6429067,9396249,365013,24703301,-10488939,1321586,149635,-15452774,7159369 }, + }, + { + { 9987780,-3404759,17507962,9505530,9731535,-2165514,22356009,8312176,22477218,-8403385 }, + { 18155857,-16504990,19744716,9006923,15154154,-10538976,24256460,-4864995,-22548173,9334109 }, + { 2986088,-4911893,10776628,-3473844,10620590,-7083203,-21413845,14253545,-22587149,536906 }, + }, + { + { 4377756,8115836,24567078,15495314,11625074,13064599,7390551,10589625,10838060,-15420424 }, + { -19342404,867880,9277171,-3218459,-14431572,-1986443,19295826,-15796950,6378260,699185 }, + { 7895026,4057113,-7081772,-13077756,-17886831,-323126,-716039,15693155,-5045064,-13373962 }, + }, + { + { -7737563,-5869402,-14566319,-7406919,11385654,13201616,31730678,-10962840,-3918636,-9669325 }, + { 10188286,-15770834,-7336361,13427543,22223443,14896287,30743455,7116568,-21786507,5427593 }, + { 696102,13206899,27047647,-10632082,15285305,-9853179,10798490,-4578720,19236243,12477404 }, + }, + { + { -11229439,11243796,-17054270,-8040865,-788228,-8167967,-3897669,11180504,-23169516,7733644 }, + { 17800790,-14036179,-27000429,-11766671,23887827,3149671,23466177,-10538171,10322027,15313801 }, + { 26246234,11968874,32263343,-5468728,6830755,-13323031,-15794704,-101982,-24449242,10890804 }, + }, + { + { -31365647,10271363,-12660625,-6267268,16690207,-13062544,-14982212,16484931,25180797,-5334884 }, + { -586574,10376444,-32586414,-11286356,19801893,10997610,2276632,9482883,316878,13820577 }, + { -9882808,-4510367,-2115506,16457136,-11100081,11674996,30756178,-7515054,30696930,-3712849 }, + }, + { + { 32988917,-9603412,12499366,7910787,-10617257,-11931514,-7342816,-9985397,-32349517,7392473 }, + { -8855661,15927861,9866406,-3649411,-2396914,-16655781,-30409476,-9134995,25112947,-2926644 }, + { -2504044,-436966,25621774,-5678772,15085042,-5479877,-24884878,-13526194,5537438,-13914319 }, + }, +}, +{ + { + { -11225584,2320285,-9584280,10149187,-33444663,5808648,-14876251,-1729667,31234590,6090599 }, + { -9633316,116426,26083934,2897444,-6364437,-2688086,609721,15878753,-6970405,-9034768 }, + { -27757857,247744,-15194774,-9002551,23288161,-10011936,-23869595,6503646,20650474,1804084 }, + }, + { + { -27589786,15456424,8972517,8469608,15640622,4439847,3121995,-10329713,27842616,-202328 }, + { -15306973,2839644,22530074,10026331,4602058,5048462,28248656,5031932,-11375082,12714369 }, + { 20807691,-7270825,29286141,11421711,-27876523,-13868230,-21227475,1035546,-19733229,12796920 }, + }, + { + { 12076899,-14301286,-8785001,-11848922,-25012791,16400684,-17591495,-12899438,3480665,-15182815 }, + { -32361549,5457597,28548107,7833186,7303070,-11953545,-24363064,-15921875,-33374054,2771025 }, + { -21389266,421932,26597266,6860826,22486084,-6737172,-17137485,-4210226,-24552282,15673397 }, + }, + { + { -20184622,2338216,19788685,-9620956,-4001265,-8740893,-20271184,4733254,3727144,-12934448 }, + { 6120119,814863,-11794402,-622716,6812205,-15747771,2019594,7975683,31123697,-10958981 }, + { 30069250,-11435332,30434654,2958439,18399564,-976289,12296869,9204260,-16432438,9648165 }, + }, + { + { 32705432,-1550977,30705658,7451065,-11805606,9631813,3305266,5248604,-26008332,-11377501 }, + { 17219865,2375039,-31570947,-5575615,-19459679,9219903,294711,15298639,2662509,-16297073 }, + { -1172927,-7558695,-4366770,-4287744,-21346413,-8434326,32087529,-1222777,32247248,-14389861 }, + }, + { + { 14312628,1221556,17395390,-8700143,-4945741,-8684635,-28197744,-9637817,-16027623,-13378845 }, + { -1428825,-9678990,-9235681,6549687,-7383069,-468664,23046502,9803137,17597934,2346211 }, + { 18510800,15337574,26171504,981392,-22241552,7827556,-23491134,-11323352,3059833,-11782870 }, + }, + { + { 10141598,6082907,17829293,-1947643,9830092,13613136,-25556636,-5544586,-33502212,3592096 }, + { 33114168,-15889352,-26525686,-13343397,33076705,8716171,1151462,1521897,-982665,-6837803 }, + { -32939165,-4255815,23947181,-324178,-33072974,-12305637,-16637686,3891704,26353178,693168 }, + }, + { + { 30374239,1595580,-16884039,13186931,4600344,406904,9585294,-400668,31375464,14369965 }, + { -14370654,-7772529,1510301,6434173,-18784789,-6262728,32732230,-13108839,17901441,16011505 }, + { 18171223,-11934626,-12500402,15197122,-11038147,-15230035,-19172240,-16046376,8764035,12309598 }, + }, +}, +{ + { + { 5975908,-5243188,-19459362,-9681747,-11541277,14015782,-23665757,1228319,17544096,-10593782 }, + { 5811932,-1715293,3442887,-2269310,-18367348,-8359541,-18044043,-15410127,-5565381,12348900 }, + { -31399660,11407555,25755363,6891399,-3256938,14872274,-24849353,8141295,-10632534,-585479 }, + }, + { + { -12675304,694026,-5076145,13300344,14015258,-14451394,-9698672,-11329050,30944593,1130208 }, + { 8247766,-6710942,-26562381,-7709309,-14401939,-14648910,4652152,2488540,23550156,-271232 }, + { 17294316,-3788438,7026748,15626851,22990044,113481,2267737,-5908146,-408818,-137719 }, + }, + { + { 16091085,-16253926,18599252,7340678,2137637,-1221657,-3364161,14550936,3260525,-7166271 }, + { -4910104,-13332887,18550887,10864893,-16459325,-7291596,-23028869,-13204905,-12748722,2701326 }, + { -8574695,16099415,4629974,-16340524,-20786213,-6005432,-10018363,9276971,11329923,1862132 }, + }, + { + { 14763076,-15903608,-30918270,3689867,3511892,10313526,-21951088,12219231,-9037963,-940300 }, + { 8894987,-3446094,6150753,3013931,301220,15693451,-31981216,-2909717,-15438168,11595570 }, + { 15214962,3537601,-26238722,-14058872,4418657,-15230761,13947276,10730794,-13489462,-4363670 }, + }, + { + { -2538306,7682793,32759013,263109,-29984731,-7955452,-22332124,-10188635,977108,699994 }, + { -12466472,4195084,-9211532,550904,-15565337,12917920,19118110,-439841,-30534533,-14337913 }, + { 31788461,-14507657,4799989,7372237,8808585,-14747943,9408237,-10051775,12493932,-5409317 }, + }, + { + { -25680606,5260744,-19235809,-6284470,-3695942,16566087,27218280,2607121,29375955,6024730 }, + { 842132,-2794693,-4763381,-8722815,26332018,-12405641,11831880,6985184,-9940361,2854096 }, + { -4847262,-7969331,2516242,-5847713,9695691,-7221186,16512645,960770,12121869,16648078 }, + }, + { + { -15218652,14667096,-13336229,2013717,30598287,-464137,-31504922,-7882064,20237806,2838411 }, + { -19288047,4453152,15298546,-16178388,22115043,-15972604,12544294,-13470457,1068881,-12499905 }, + { -9558883,-16518835,33238498,13506958,30505848,-1114596,-8486907,-2630053,12521378,4845654 }, + }, + { + { -28198521,10744108,-2958380,10199664,7759311,-13088600,3409348,-873400,-6482306,-12885870 }, + { -23561822,6230156,-20382013,10655314,-24040585,-11621172,10477734,-1240216,-3113227,13974498 }, + { 12966261,15550616,-32038948,-1615346,21025980,-629444,5642325,7188737,18895762,12629579 }, + }, +}, +{ + { + { 14741879,-14946887,22177208,-11721237,1279741,8058600,11758140,789443,32195181,3895677 }, + { 10758205,15755439,-4509950,9243698,-4879422,6879879,-2204575,-3566119,-8982069,4429647 }, + { -2453894,15725973,-20436342,-10410672,-5803908,-11040220,-7135870,-11642895,18047436,-15281743 }, + }, + { + { -25173001,-11307165,29759956,11776784,-22262383,-15820455,10993114,-12850837,-17620701,-9408468 }, + { 21987233,700364,-24505048,14972008,-7774265,-5718395,32155026,2581431,-29958985,8773375 }, + { -25568350,454463,-13211935,16126715,25240068,8594567,20656846,12017935,-7874389,-13920155 }, + }, + { + { 6028182,6263078,-31011806,-11301710,-818919,2461772,-31841174,-5468042,-1721788,-2776725 }, + { -12278994,16624277,987579,-5922598,32908203,1248608,7719845,-4166698,28408820,6816612 }, + { -10358094,-8237829,19549651,-12169222,22082623,16147817,20613181,13982702,-10339570,5067943 }, + }, + { + { -30505967,-3821767,12074681,13582412,-19877972,2443951,-19719286,12746132,5331210,-10105944 }, + { 30528811,3601899,-1957090,4619785,-27361822,-15436388,24180793,-12570394,27679908,-1648928 }, + { 9402404,-13957065,32834043,10838634,-26580150,-13237195,26653274,-8685565,22611444,-12715406 }, + }, + { + { 22190590,1118029,22736441,15130463,-30460692,-5991321,19189625,-4648942,4854859,6622139 }, + { -8310738,-2953450,-8262579,-3388049,-10401731,-271929,13424426,-3567227,26404409,13001963 }, + { -31241838,-15415700,-2994250,8939346,11562230,-12840670,-26064365,-11621720,-15405155,11020693 }, + }, + { + { 1866042,-7949489,-7898649,-10301010,12483315,13477547,3175636,-12424163,28761762,1406734 }, + { -448555,-1777666,13018551,3194501,-9580420,-11161737,24760585,-4347088,25577411,-13378680 }, + { -24290378,4759345,-690653,-1852816,2066747,10693769,-29595790,9884936,-9368926,4745410 }, + }, + { + { -9141284,6049714,-19531061,-4341411,-31260798,9944276,-15462008,-11311852,10931924,-11931931 }, + { -16561513,14112680,-8012645,4817318,-8040464,-11414606,-22853429,10856641,-20470770,13434654 }, + { 22759489,-10073434,-16766264,-1871422,13637442,-10168091,1765144,-12654326,28445307,-5364710 }, + }, + { + { 29875063,12493613,2795536,-3786330,1710620,15181182,-10195717,-8788675,9074234,1167180 }, + { -26205683,11014233,-9842651,-2635485,-26908120,7532294,-18716888,-9535498,3843903,9367684 }, + { -10969595,-6403711,9591134,9582310,11349256,108879,16235123,8601684,-139197,4242895 }, + }, +}, +{ + { + { 22092954,-13191123,-2042793,-11968512,32186753,-11517388,-6574341,2470660,-27417366,16625501 }, + { -11057722,3042016,13770083,-9257922,584236,-544855,-7770857,2602725,-27351616,14247413 }, + { 6314175,-10264892,-32772502,15957557,-10157730,168750,-8618807,14290061,27108877,-1180880 }, + }, + { + { -8586597,-7170966,13241782,10960156,-32991015,-13794596,33547976,-11058889,-27148451,981874 }, + { 22833440,9293594,-32649448,-13618667,-9136966,14756819,-22928859,-13970780,-10479804,-16197962 }, + { -7768587,3326786,-28111797,10783824,19178761,14905060,22680049,13906969,-15933690,3797899 }, + }, + { + { 21721356,-4212746,-12206123,9310182,-3882239,-13653110,23740224,-2709232,20491983,-8042152 }, + { 9209270,-15135055,-13256557,-6167798,-731016,15289673,25947805,15286587,30997318,-6703063 }, + { 7392032,16618386,23946583,-8039892,-13265164,-1533858,-14197445,-2321576,17649998,-250080 }, + }, + { + { -9301088,-14193827,30609526,-3049543,-25175069,-1283752,-15241566,-9525724,-2233253,7662146 }, + { -17558673,1763594,-33114336,15908610,-30040870,-12174295,7335080,-8472199,-3174674,3440183 }, + { -19889700,-5977008,-24111293,-9688870,10799743,-16571957,40450,-4431835,4862400,1133 }, + }, + { + { -32856209,-7873957,-5422389,14860950,-16319031,7956142,7258061,311861,-30594991,-7379421 }, + { -3773428,-1565936,28985340,7499440,24445838,9325937,29727763,16527196,18278453,15405622 }, + { -4381906,8508652,-19898366,-3674424,-5984453,15149970,-13313598,843523,-21875062,13626197 }, + }, + { + { 2281448,-13487055,-10915418,-2609910,1879358,16164207,-10783882,3953792,13340839,15928663 }, + { 31727126,-7179855,-18437503,-8283652,2875793,-16390330,-25269894,-7014826,-23452306,5964753 }, + { 4100420,-5959452,-17179337,6017714,-18705837,12227141,-26684835,11344144,2538215,-7570755 }, + }, + { + { -9433605,6123113,11159803,-2156608,30016280,14966241,-20474983,1485421,-629256,-15958862 }, + { -26804558,4260919,11851389,9658551,-32017107,16367492,-20205425,-13191288,11659922,-11115118 }, + { 26180396,10015009,-30844224,-8581293,5418197,9480663,2231568,-10170080,33100372,-1306171 }, + }, + { + { 15121113,-5201871,-10389905,15427821,-27509937,-15992507,21670947,4486675,-5931810,-14466380 }, + { 16166486,-9483733,-11104130,6023908,-31926798,-1364923,2340060,-16254968,-10735770,-10039824 }, + { 28042865,-3557089,-12126526,12259706,-3717498,-6945899,6766453,-8689599,18036436,5803270 }, + }, +}, +{ + { + { -817581,6763912,11803561,1585585,10958447,-2671165,23855391,4598332,-6159431,-14117438 }, + { -31031306,-14256194,17332029,-2383520,31312682,-5967183,696309,50292,-20095739,11763584 }, + { -594563,-2514283,-32234153,12643980,12650761,14811489,665117,-12613632,-19773211,-10713562 }, + }, + { + { 30464590,-11262872,-4127476,-12734478,19835327,-7105613,-24396175,2075773,-17020157,992471 }, + { 18357185,-6994433,7766382,16342475,-29324918,411174,14578841,8080033,-11574335,-10601610 }, + { 19598397,10334610,12555054,2555664,18821899,-10339780,21873263,16014234,26224780,16452269 }, + }, + { + { -30223925,5145196,5944548,16385966,3976735,2009897,-11377804,-7618186,-20533829,3698650 }, + { 14187449,3448569,-10636236,-10810935,-22663880,-3433596,7268410,-10890444,27394301,12015369 }, + { 19695761,16087646,28032085,12999827,6817792,11427614,20244189,-1312777,-13259127,-3402461 }, + }, + { + { 30860103,12735208,-1888245,-4699734,-16974906,2256940,-8166013,12298312,-8550524,-10393462 }, + { -5719826,-11245325,-1910649,15569035,26642876,-7587760,-5789354,-15118654,-4976164,12651793 }, + { -2848395,9953421,11531313,-5282879,26895123,-12697089,-13118820,-16517902,9768698,-2533218 }, + }, + { + { -24719459,1894651,-287698,-4704085,15348719,-8156530,32767513,12765450,4940095,10678226 }, + { 18860224,15980149,-18987240,-1562570,-26233012,-11071856,-7843882,13944024,-24372348,16582019 }, + { -15504260,4970268,-29893044,4175593,-20993212,-2199756,-11704054,15444560,-11003761,7989037 }, + }, + { + { 31490452,5568061,-2412803,2182383,-32336847,4531686,-32078269,6200206,-19686113,-14800171 }, + { -17308668,-15879940,-31522777,-2831,-32887382,16375549,8680158,-16371713,28550068,-6857132 }, + { -28126887,-5688091,16837845,-1820458,-6850681,12700016,-30039981,4364038,1155602,5988841 }, + }, + { + { 21890435,-13272907,-12624011,12154349,-7831873,15300496,23148983,-4470481,24618407,8283181 }, + { -33136107,-10512751,9975416,6841041,-31559793,16356536,3070187,-7025928,1466169,10740210 }, + { -1509399,-15488185,-13503385,-10655916,32799044,909394,-13938903,-5779719,-32164649,-15327040 }, + }, + { + { 3960823,-14267803,-28026090,-15918051,-19404858,13146868,15567327,951507,-3260321,-573935 }, + { 24740841,5052253,-30094131,8961361,25877428,6165135,-24368180,14397372,-7380369,-6144105 }, + { -28888365,3510803,-28103278,-1158478,-11238128,-10631454,-15441463,-14453128,-1625486,-6494814 }, + }, +}, +{ + { + { 793299,-9230478,8836302,-6235707,-27360908,-2369593,33152843,-4885251,-9906200,-621852 }, + { 5666233,525582,20782575,-8038419,-24538499,14657740,16099374,1468826,-6171428,-15186581 }, + { -4859255,-3779343,-2917758,-6748019,7778750,11688288,-30404353,-9871238,-1558923,-9863646 }, + }, + { + { 10896332,-7719704,824275,472601,-19460308,3009587,25248958,14783338,-30581476,-15757844 }, + { 10566929,12612572,-31944212,11118703,-12633376,12362879,21752402,8822496,24003793,14264025 }, + { 27713862,-7355973,-11008240,9227530,27050101,2504721,23886875,-13117525,13958495,-5732453 }, + }, + { + { -23481610,4867226,-27247128,3900521,29838369,-8212291,-31889399,-10041781,7340521,-15410068 }, + { 4646514,-8011124,-22766023,-11532654,23184553,8566613,31366726,-1381061,-15066784,-10375192 }, + { -17270517,12723032,-16993061,14878794,21619651,-6197576,27584817,3093888,-8843694,3849921 }, + }, + { + { -9064912,2103172,25561640,-15125738,-5239824,9582958,32477045,-9017955,5002294,-15550259 }, + { -12057553,-11177906,21115585,-13365155,8808712,-12030708,16489530,13378448,-25845716,12741426 }, + { -5946367,10645103,-30911586,15390284,-3286982,-7118677,24306472,15852464,28834118,-7646072 }, + }, + { + { -17335748,-9107057,-24531279,9434953,-8472084,-583362,-13090771,455841,20461858,5491305 }, + { 13669248,-16095482,-12481974,-10203039,-14569770,-11893198,-24995986,11293807,-28588204,-9421832 }, + { 28497928,6272777,-33022994,14470570,8906179,-1225630,18504674,-14165166,29867745,-8795943 }, + }, + { + { -16207023,13517196,-27799630,-13697798,24009064,-6373891,-6367600,-13175392,22853429,-4012011 }, + { 24191378,16712145,-13931797,15217831,14542237,1646131,18603514,-11037887,12876623,-2112447 }, + { 17902668,4518229,-411702,-2829247,26878217,5258055,-12860753,608397,16031844,3723494 }, + }, + { + { -28632773,12763728,-20446446,7577504,33001348,-13017745,17558842,-7872890,23896954,-4314245 }, + { -20005381,-12011952,31520464,605201,2543521,5991821,-2945064,7229064,-9919646,-8826859 }, + { 28816045,298879,-28165016,-15920938,19000928,-1665890,-12680833,-2949325,-18051778,-2082915 }, + }, + { + { 16000882,-344896,3493092,-11447198,-29504595,-13159789,12577740,16041268,-19715240,7847707 }, + { 10151868,10572098,27312476,7922682,14825339,4723128,-32855931,-6519018,-10020567,3852848 }, + { -11430470,15697596,-21121557,-4420647,5386314,15063598,16514493,-15932110,29330899,-15076224 }, + }, +}, +{ + { + { -25499735,-4378794,-15222908,-6901211,16615731,2051784,3303702,15490,-27548796,12314391 }, + { 15683520,-6003043,18109120,-9980648,15337968,-5997823,-16717435,15921866,16103996,-3731215 }, + { -23169824,-10781249,13588192,-1628807,-3798557,-1074929,-19273607,5402699,-29815713,-9841101 }, + }, + { + { 23190676,2384583,-32714340,3462154,-29903655,-1529132,-11266856,8911517,-25205859,2739713 }, + { 21374101,-3554250,-33524649,9874411,15377179,11831242,-33529904,6134907,4931255,11987849 }, + { -7732,-2978858,-16223486,7277597,105524,-322051,-31480539,13861388,-30076310,10117930 }, + }, + { + { -29501170,-10744872,-26163768,13051539,-25625564,5089643,-6325503,6704079,12890019,15728940 }, + { -21972360,-11771379,-951059,-4418840,14704840,2695116,903376,-10428139,12885167,8311031 }, + { -17516482,5352194,10384213,-13811658,7506451,13453191,26423267,4384730,1888765,-5435404 }, + }, + { + { -25817338,-3107312,-13494599,-3182506,30896459,-13921729,-32251644,-12707869,-19464434,-3340243 }, + { -23607977,-2665774,-526091,4651136,5765089,4618330,6092245,14845197,17151279,-9854116 }, + { -24830458,-12733720,-15165978,10367250,-29530908,-265356,22825805,-7087279,-16866484,16176525 }, + }, + { + { -23583256,6564961,20063689,3798228,-4740178,7359225,2006182,-10363426,-28746253,-10197509 }, + { -10626600,-4486402,-13320562,-5125317,3432136,-6393229,23632037,-1940610,32808310,1099883 }, + { 15030977,5768825,-27451236,-2887299,-6427378,-15361371,-15277896,-6809350,2051441,-15225865 }, + }, + { + { -3362323,-7239372,7517890,9824992,23555850,295369,5148398,-14154188,-22686354,16633660 }, + { 4577086,-16752288,13249841,-15304328,19958763,-14537274,18559670,-10759549,8402478,-9864273 }, + { -28406330,-1051581,-26790155,-907698,-17212414,-11030789,9453451,-14980072,17983010,9967138 }, + }, + { + { -25762494,6524722,26585488,9969270,24709298,1220360,-1677990,7806337,17507396,3651560 }, + { -10420457,-4118111,14584639,15971087,-15768321,8861010,26556809,-5574557,-18553322,-11357135 }, + { 2839101,14284142,4029895,3472686,14402957,12689363,-26642121,8459447,-5605463,-7621941 }, + }, + { + { -4839289,-3535444,9744961,2871048,25113978,3187018,-25110813,-849066,17258084,-7977739 }, + { 18164541,-10595176,-17154882,-1542417,19237078,-9745295,23357533,-15217008,26908270,12150756 }, + { -30264870,-7647865,5112249,-7036672,-1499807,-6974257,43168,-5537701,-32302074,16215819 }, + }, +}, +{ + { + { -6898905,9824394,-12304779,-4401089,-31397141,-6276835,32574489,12532905,-7503072,-8675347 }, + { -27343522,-16515468,-27151524,-10722951,946346,16291093,254968,7168080,21676107,-1943028 }, + { 21260961,-8424752,-16831886,-11920822,-23677961,3968121,-3651949,-6215466,-3556191,-7913075 }, + }, + { + { 16544754,13250366,-16804428,15546242,-4583003,12757258,-2462308,-8680336,-18907032,-9662799 }, + { -2415239,-15577728,18312303,4964443,-15272530,-12653564,26820651,16690659,25459437,-4564609 }, + { -25144690,11425020,28423002,-11020557,-6144921,-15826224,9142795,-2391602,-6432418,-1644817 }, + }, + { + { -23104652,6253476,16964147,-3768872,-25113972,-12296437,-27457225,-16344658,6335692,7249989 }, + { -30333227,13979675,7503222,-12368314,-11956721,-4621693,-30272269,2682242,25993170,-12478523 }, + { 4364628,5930691,32304656,-10044554,-8054781,15091131,22857016,-10598955,31820368,15075278 }, + }, + { + { 31879134,-8918693,17258761,90626,-8041836,-4917709,24162788,-9650886,-17970238,12833045 }, + { 19073683,14851414,-24403169,-11860168,7625278,11091125,-19619190,2074449,-9413939,14905377 }, + { 24483667,-11935567,-2518866,-11547418,-1553130,15355506,-25282080,9253129,27628530,-7555480 }, + }, + { + { 17597607,8340603,19355617,552187,26198470,-3176583,4593324,-9157582,-14110875,15297016 }, + { 510886,14337390,-31785257,16638632,6328095,2713355,-20217417,-11864220,8683221,2921426 }, + { 18606791,11874196,27155355,-5281482,-24031742,6265446,-25178240,-1278924,4674690,13890525 }, + }, + { + { 13609624,13069022,-27372361,-13055908,24360586,9592974,14977157,9835105,4389687,288396 }, + { 9922506,-519394,13613107,5883594,-18758345,-434263,-12304062,8317628,23388070,16052080 }, + { 12720016,11937594,-31970060,-5028689,26900120,8561328,-20155687,-11632979,-14754271,-10812892 }, + }, + { + { 15961858,14150409,26716931,-665832,-22794328,13603569,11829573,7467844,-28822128,929275 }, + { 11038231,-11582396,-27310482,-7316562,-10498527,-16307831,-23479533,-9371869,-21393143,2465074 }, + { 20017163,-4323226,27915242,1529148,12396362,15675764,13817261,-9658066,2463391,-4622140 }, + }, + { + { -16358878,-12663911,-12065183,4996454,-1256422,1073572,9583558,12851107,4003896,12673717 }, + { -1731589,-15155870,-3262930,16143082,19294135,13385325,14741514,-9103726,7903886,2348101 }, + { 24536016,-16515207,12715592,-3862155,1511293,10047386,-3842346,-7129159,-28377538,10048127 }, + }, +}, +{ + { + { -12622226,-6204820,30718825,2591312,-10617028,12192840,18873298,-7297090,-32297756,15221632 }, + { -26478122,-11103864,11546244,-1852483,9180880,7656409,-21343950,2095755,29769758,6593415 }, + { -31994208,-2907461,4176912,3264766,12538965,-868111,26312345,-6118678,30958054,8292160 }, + }, + { + { 31429822,-13959116,29173532,15632448,12174511,-2760094,32808831,3977186,26143136,-3148876 }, + { 22648901,1402143,-22799984,13746059,7936347,365344,-8668633,-1674433,-3758243,-2304625 }, + { -15491917,8012313,-2514730,-12702462,-23965846,-10254029,-1612713,-1535569,-16664475,8194478 }, + }, + { + { 27338066,-7507420,-7414224,10140405,-19026427,-6589889,27277191,8855376,28572286,3005164 }, + { 26287124,4821776,25476601,-4145903,-3764513,-15788984,-18008582,1182479,-26094821,-13079595 }, + { -7171154,3178080,23970071,6201893,-17195577,-4489192,-21876275,-13982627,32208683,-1198248 }, + }, + { + { -16657702,2817643,-10286362,14811298,6024667,13349505,-27315504,-10497842,-27672585,-11539858 }, + { 15941029,-9405932,-21367050,8062055,31876073,-238629,-15278393,-1444429,15397331,-4130193 }, + { 8934485,-13485467,-23286397,-13423241,-32446090,14047986,31170398,-1441021,-27505566,15087184 }, + }, + { + { -18357243,-2156491,24524913,-16677868,15520427,-6360776,-15502406,11461896,16788528,-5868942 }, + { -1947386,16013773,21750665,3714552,-17401782,-16055433,-3770287,-10323320,31322514,-11615635 }, + { 21426655,-5650218,-13648287,-5347537,-28812189,-4920970,-18275391,-14621414,13040862,-12112948 }, + }, + { + { 11293895,12478086,-27136401,15083750,-29307421,14748872,14555558,-13417103,1613711,4896935 }, + { -25894883,15323294,-8489791,-8057900,25967126,-13425460,2825960,-4897045,-23971776,-11267415 }, + { -15924766,-5229880,-17443532,6410664,3622847,10243618,20615400,12405433,-23753030,-8436416 }, + }, + { + { -7091295,12556208,-20191352,9025187,-17072479,4333801,4378436,2432030,23097949,-566018 }, + { 4565804,-16025654,20084412,-7842817,1724999,189254,24767264,10103221,-18512313,2424778 }, + { 366633,-11976806,8173090,-6890119,30788634,5745705,-7168678,1344109,-3642553,12412659 }, + }, + { + { -24001791,7690286,14929416,-168257,-32210835,-13412986,24162697,-15326504,-3141501,11179385 }, + { 18289522,-14724954,8056945,16430056,-21729724,7842514,-6001441,-1486897,-18684645,-11443503 }, + { 476239,6601091,-6152790,-9723375,17503545,-4863900,27672959,13403813,11052904,5219329 }, + }, +}, +{ + { + { 20678546,-8375738,-32671898,8849123,-5009758,14574752,31186971,-3973730,9014762,-8579056 }, + { -13644050,-10350239,-15962508,5075808,-1514661,-11534600,-33102500,9160280,8473550,-3256838 }, + { 24900749,14435722,17209120,-15292541,-22592275,9878983,-7689309,-16335821,-24568481,11788948 }, + }, + { + { -3118155,-11395194,-13802089,14797441,9652448,-6845904,-20037437,10410733,-24568470,-1458691 }, + { -15659161,16736706,-22467150,10215878,-9097177,7563911,11871841,-12505194,-18513325,8464118 }, + { -23400612,8348507,-14585951,-861714,-3950205,-6373419,14325289,8628612,33313881,-8370517 }, + }, + { + { -20186973,-4967935,22367356,5271547,-1097117,-4788838,-24805667,-10236854,-8940735,-5818269 }, + { -6948785,-1795212,-32625683,-16021179,32635414,-7374245,15989197,-12838188,28358192,-4253904 }, + { -23561781,-2799059,-32351682,-1661963,-9147719,10429267,-16637684,4072016,-5351664,5596589 }, + }, + { + { -28236598,-3390048,12312896,6213178,3117142,16078565,29266239,2557221,1768301,15373193 }, + { -7243358,-3246960,-4593467,-7553353,-127927,-912245,-1090902,-4504991,-24660491,3442910 }, + { -30210571,5124043,14181784,8197961,18964734,-11939093,22597931,7176455,-18585478,13365930 }, + }, + { + { -7877390,-1499958,8324673,4690079,6261860,890446,24538107,-8570186,-9689599,-3031667 }, + { 25008904,-10771599,-4305031,-9638010,16265036,15721635,683793,-11823784,15723479,-15163481 }, + { -9660625,12374379,-27006999,-7026148,-7724114,-12314514,11879682,5400171,519526,-1235876 }, + }, + { + { 22258397,-16332233,-7869817,14613016,-22520255,-2950923,-20353881,7315967,16648397,7605640 }, + { -8081308,-8464597,-8223311,9719710,19259459,-15348212,23994942,-5281555,-9468848,4763278 }, + { -21699244,9220969,-15730624,1084137,-25476107,-2852390,31088447,-7764523,-11356529,728112 }, + }, + { + { 26047220,-11751471,-6900323,-16521798,24092068,9158119,-4273545,-12555558,-29365436,-5498272 }, + { 17510331,-322857,5854289,8403524,17133918,-3112612,-28111007,12327945,10750447,10014012 }, + { -10312768,3936952,9156313,-8897683,16498692,-994647,-27481051,-666732,3424691,7540221 }, + }, + { + { 30322361,-6964110,11361005,-4143317,7433304,4989748,-7071422,-16317219,-9244265,15258046 }, + { 13054562,-2779497,19155474,469045,-12482797,4566042,5631406,2711395,1062915,-5136345 }, + { -19240248,-11254599,-29509029,-7499965,-5835763,13005411,-6066489,12194497,32960380,1459310 }, + }, +}, +{ + { + { 19852034,7027924,23669353,10020366,8586503,-6657907,394197,-6101885,18638003,-11174937 }, + { 31395534,15098109,26581030,8030562,-16527914,-5007134,9012486,-7584354,-6643087,-5442636 }, + { -9192165,-2347377,-1997099,4529534,25766844,607986,-13222,9677543,-32294889,-6456008 }, + }, + { + { -2444496,-149937,29348902,8186665,1873760,12489863,-30934579,-7839692,-7852844,-8138429 }, + { -15236356,-15433509,7766470,746860,26346930,-10221762,-27333451,10754588,-9431476,5203576 }, + { 31834314,14135496,-770007,5159118,20917671,-16768096,-7467973,-7337524,31809243,7347066 }, + }, + { + { -9606723,-11874240,20414459,13033986,13716524,-11691881,19797970,-12211255,15192876,-2087490 }, + { -12663563,-2181719,1168162,-3804809,26747877,-14138091,10609330,12694420,33473243,-13382104 }, + { 33184999,11180355,15832085,-11385430,-1633671,225884,15089336,-11023903,-6135662,14480053 }, + }, + { + { 31308717,-5619998,31030840,-1897099,15674547,-6582883,5496208,13685227,27595050,8737275 }, + { -20318852,-15150239,10933843,-16178022,8335352,-7546022,-31008351,-12610604,26498114,66511 }, + { 22644454,-8761729,-16671776,4884562,-3105614,-13559366,30540766,-4286747,-13327787,-7515095 }, + }, + { + { -28017847,9834845,18617207,-2681312,-3401956,-13307506,8205540,13585437,-17127465,15115439 }, + { 23711543,-672915,31206561,-8362711,6164647,-9709987,-33535882,-1426096,8236921,16492939 }, + { -23910559,-13515526,-26299483,-4503841,25005590,-7687270,19574902,10071562,6708380,-6222424 }, + }, + { + { 2101391,-4930054,19702731,2367575,-15427167,1047675,5301017,9328700,29955601,-11678310 }, + { 3096359,9271816,-21620864,-15521844,-14847996,-7592937,-25892142,-12635595,-9917575,6216608 }, + { -32615849,338663,-25195611,2510422,-29213566,-13820213,24822830,-6146567,-26767480,7525079 }, + }, + { + { -23066649,-13985623,16133487,-7896178,-3389565,778788,-910336,-2782495,-19386633,11994101 }, + { 21691500,-13624626,-641331,-14367021,3285881,-3483596,-25064666,9718258,-7477437,13381418 }, + { 18445390,-4202236,14979846,11622458,-1727110,-3582980,23111648,-6375247,28535282,15779576 }, + }, + { + { 30098053,3089662,-9234387,16662135,-21306940,11308411,-14068454,12021730,9955285,-16303356 }, + { 9734894,-14576830,-7473633,-9138735,2060392,11313496,-18426029,9924399,20194861,13380996 }, + { -26378102,-7965207,-22167821,15789297,-18055342,-6168792,-1984914,15707771,26342023,10146099 }, + }, +}, +{ + { + { -26016874,-219943,21339191,-41388,19745256,-2878700,-29637280,2227040,21612326,-545728 }, + { -13077387,1184228,23562814,-5970442,-20351244,-6348714,25764461,12243797,-20856566,11649658 }, + { -10031494,11262626,27384172,2271902,26947504,-15997771,39944,6114064,33514190,2333242 }, + }, + { + { -21433588,-12421821,8119782,7219913,-21830522,-9016134,-6679750,-12670638,24350578,-13450001 }, + { -4116307,-11271533,-23886186,4843615,-30088339,690623,-31536088,-10406836,8317860,12352766 }, + { 18200138,-14475911,-33087759,-2696619,-23702521,-9102511,-23552096,-2287550,20712163,6719373 }, + }, + { + { 26656208,6075253,-7858556,1886072,-28344043,4262326,11117530,-3763210,26224235,-3297458 }, + { -17168938,-14854097,-3395676,-16369877,-19954045,14050420,21728352,9493610,18620611,-16428628 }, + { -13323321,13325349,11432106,5964811,18609221,6062965,-5269471,-9725556,-30701573,-16479657 }, + }, + { + { -23860538,-11233159,26961357,1640861,-32413112,-16737940,12248509,-5240639,13735342,1934062 }, + { 25089769,6742589,17081145,-13406266,21909293,-16067981,-15136294,-3765346,-21277997,5473616 }, + { 31883677,-7961101,1083432,-11572403,22828471,13290673,-7125085,12469656,29111212,-5451014 }, + }, + { + { 24244947,-15050407,-26262976,2791540,-14997599,16666678,24367466,6388839,-10295587,452383 }, + { -25640782,-3417841,5217916,16224624,19987036,-4082269,-24236251,-5915248,15766062,8407814 }, + { -20406999,13990231,15495425,16395525,5377168,15166495,-8917023,-4388953,-8067909,2276718 }, + }, + { + { 30157918,12924066,-17712050,9245753,19895028,3368142,-23827587,5096219,22740376,-7303417 }, + { 2041139,-14256350,7783687,13876377,-25946985,-13352459,24051124,13742383,-15637599,13295222 }, + { 33338237,-8505733,12532113,7977527,9106186,-1715251,-17720195,-4612972,-4451357,-14669444 }, + }, + { + { -20045281,5454097,-14346548,6447146,28862071,1883651,-2469266,-4141880,7770569,9620597 }, + { 23208068,7979712,33071466,8149229,1758231,-10834995,30945528,-1694323,-33502340,-14767970 }, + { 1439958,-16270480,-1079989,-793782,4625402,10647766,-5043801,1220118,30494170,-11440799 }, + }, + { + { -5037580,-13028295,-2970559,-3061767,15640974,-6701666,-26739026,926050,-1684339,-13333647 }, + { 13908495,-3549272,30919928,-6273825,-21521863,7989039,9021034,9078865,3353509,4033511 }, + { -29663431,-15113610,32259991,-344482,24295849,-12912123,23161163,8839127,27485041,7356032 }, + }, +}, +{ + { + { 9661027,705443,11980065,-5370154,-1628543,14661173,-6346142,2625015,28431036,-16771834 }, + { -23839233,-8311415,-25945511,7480958,-17681669,-8354183,-22545972,14150565,15970762,4099461 }, + { 29262576,16756590,26350592,-8793563,8529671,-11208050,13617293,-9937143,11465739,8317062 }, + }, + { + { -25493081,-6962928,32500200,-9419051,-23038724,-2302222,14898637,3848455,20969334,-5157516 }, + { -20384450,-14347713,-18336405,13884722,-33039454,2842114,-21610826,-3649888,11177095,14989547 }, + { -24496721,-11716016,16959896,2278463,12066309,10137771,13515641,2581286,-28487508,9930240 }, + }, + { + { -17751622,-2097826,16544300,-13009300,-15914807,-14949081,18345767,-13403753,16291481,-5314038 }, + { -33229194,2553288,32678213,9875984,8534129,6889387,-9676774,6957617,4368891,9788741 }, + { 16660756,7281060,-10830758,12911820,20108584,-8101676,-21722536,-8613148,16250552,-11111103 }, + }, + { + { -19765507,2390526,-16551031,14161980,1905286,6414907,4689584,10604807,-30190403,4782747 }, + { -1354539,14736941,-7367442,-13292886,7710542,-14155590,-9981571,4383045,22546403,437323 }, + { 31665577,-12180464,-16186830,1491339,-18368625,3294682,27343084,2786261,-30633590,-14097016 }, + }, + { + { -14467279,-683715,-33374107,7448552,19294360,14334329,-19690631,2355319,-19284671,-6114373 }, + { 15121312,-15796162,6377020,-6031361,-10798111,-12957845,18952177,15496498,-29380133,11754228 }, + { -2637277,-13483075,8488727,-14303896,12728761,-1622493,7141596,11724556,22761615,-10134141 }, + }, + { + { 16918416,11729663,-18083579,3022987,-31015732,-13339659,-28741185,-12227393,32851222,11717399 }, + { 11166634,7338049,-6722523,4531520,-29468672,-7302055,31474879,3483633,-1193175,-4030831 }, + { -185635,9921305,31456609,-13536438,-12013818,13348923,33142652,6546660,-19985279,-3948376 }, + }, + { + { -32460596,11266712,-11197107,-7899103,31703694,3855903,-8537131,-12833048,-30772034,-15486313 }, + { -18006477,12709068,3991746,-6479188,-21491523,-10550425,-31135347,-16049879,10928917,3011958 }, + { -6957757,-15594337,31696059,334240,29576716,14796075,-30831056,-12805180,18008031,10258577 }, + }, + { + { -22448644,15655569,7018479,-4410003,-30314266,-1201591,-1853465,1367120,25127874,6671743 }, + { 29701166,-14373934,-10878120,9279288,-17568,13127210,21382910,11042292,25838796,4642684 }, + { -20430234,14955537,-24126347,8124619,-5369288,-5990470,30468147,-13900640,18423289,4177476 }, + }, +}, diff --git a/jni/ed25519/base2.h b/jni/ed25519/base2.h new file mode 100644 index 000000000..8c538440f --- /dev/null +++ b/jni/ed25519/base2.h @@ -0,0 +1,40 @@ + { + { 25967493,-14356035,29566456,3660896,-12694345,4014787,27544626,-11754271,-6079156,2047605 }, + { -12545711,934262,-2722910,3049990,-727428,9406986,12720692,5043384,19500929,-15469378 }, + { -8738181,4489570,9688441,-14785194,10184609,-12363380,29287919,11864899,-24514362,-4438546 }, + }, + { + { 15636291,-9688557,24204773,-7912398,616977,-16685262,27787600,-14772189,28944400,-1550024 }, + { 16568933,4717097,-11556148,-1102322,15682896,-11807043,16354577,-11775962,7689662,11199574 }, + { 30464156,-5976125,-11779434,-15670865,23220365,15915852,7512774,10017326,-17749093,-9920357 }, + }, + { + { 10861363,11473154,27284546,1981175,-30064349,12577861,32867885,14515107,-15438304,10819380 }, + { 4708026,6336745,20377586,9066809,-11272109,6594696,-25653668,12483688,-12668491,5581306 }, + { 19563160,16186464,-29386857,4097519,10237984,-4348115,28542350,13850243,-23678021,-15815942 }, + }, + { + { 5153746,9909285,1723747,-2777874,30523605,5516873,19480852,5230134,-23952439,-15175766 }, + { -30269007,-3463509,7665486,10083793,28475525,1649722,20654025,16520125,30598449,7715701 }, + { 28881845,14381568,9657904,3680757,-20181635,7843316,-31400660,1370708,29794553,-1409300 }, + }, + { + { -22518993,-6692182,14201702,-8745502,-23510406,8844726,18474211,-1361450,-13062696,13821877 }, + { -6455177,-7839871,3374702,-4740862,-27098617,-10571707,31655028,-7212327,18853322,-14220951 }, + { 4566830,-12963868,-28974889,-12240689,-7602672,-2830569,-8514358,-10431137,2207753,-3209784 }, + }, + { + { -25154831,-4185821,29681144,7868801,-6854661,-9423865,-12437364,-663000,-31111463,-16132436 }, + { 25576264,-2703214,7349804,-11814844,16472782,9300885,3844789,15725684,171356,6466918 }, + { 23103977,13316479,9739013,-16149481,817875,-15038942,8965339,-14088058,-30714912,16193877 }, + }, + { + { -33521811,3180713,-2394130,14003687,-16903474,-16270840,17238398,4729455,-18074513,9256800 }, + { -25182317,-4174131,32336398,5036987,-21236817,11360617,22616405,9761698,-19827198,630305 }, + { -13720693,2639453,-24237460,-7406481,9494427,-5774029,-6554551,-15960994,-2449256,-14291300 }, + }, + { + { -3151181,-5046075,9282714,6866145,-31907062,-863023,-18940575,15033784,25105118,-7894876 }, + { -24326370,15950226,-31801215,-14592823,-11662737,-5090925,1573892,-2625887,2198790,-15804619 }, + { -3099351,10324967,-2241613,7453183,-5446979,-2735503,-13812022,-16236442,-32461234,-12290683 }, + }, diff --git a/jni/ed25519/d.h b/jni/ed25519/d.h new file mode 100644 index 000000000..e25f57835 --- /dev/null +++ b/jni/ed25519/d.h @@ -0,0 +1 @@ +-10913610,13857413,-15372611,6949391,114729,-8787816,-6275908,-3247719,-18696448,-12055116 diff --git a/jni/ed25519/d2.h b/jni/ed25519/d2.h new file mode 100644 index 000000000..01aaec751 --- /dev/null +++ b/jni/ed25519/d2.h @@ -0,0 +1 @@ +-21827239,-5839606,-30745221,13898782,229458,15978800,-12551817,-6495438,29715968,9444199 diff --git a/jni/ed25519/fe.h b/jni/ed25519/fe.h new file mode 100644 index 000000000..60c308ba4 --- /dev/null +++ b/jni/ed25519/fe.h @@ -0,0 +1,56 @@ +#ifndef FE_H +#define FE_H + +#include "crypto_int32.h" + +typedef crypto_int32 fe[10]; + +/* +fe means field element. +Here the field is \Z/(2^255-19). +An element t, entries t[0]...t[9], represents the integer +t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9]. +Bounds on each t[i] vary depending on context. +*/ + +#define fe_frombytes crypto_sign_ed25519_ref10_fe_frombytes +#define fe_tobytes crypto_sign_ed25519_ref10_fe_tobytes +#define fe_copy crypto_sign_ed25519_ref10_fe_copy +#define fe_isnonzero crypto_sign_ed25519_ref10_fe_isnonzero +#define fe_isnegative crypto_sign_ed25519_ref10_fe_isnegative +#define fe_0 crypto_sign_ed25519_ref10_fe_0 +#define fe_1 crypto_sign_ed25519_ref10_fe_1 +#define fe_cswap crypto_sign_ed25519_ref10_fe_cswap +#define fe_cmov crypto_sign_ed25519_ref10_fe_cmov +#define fe_add crypto_sign_ed25519_ref10_fe_add +#define fe_sub crypto_sign_ed25519_ref10_fe_sub +#define fe_neg crypto_sign_ed25519_ref10_fe_neg +#define fe_mul crypto_sign_ed25519_ref10_fe_mul +#define fe_sq crypto_sign_ed25519_ref10_fe_sq +#define fe_sq2 crypto_sign_ed25519_ref10_fe_sq2 +#define fe_mul121666 crypto_sign_ed25519_ref10_fe_mul121666 +#define fe_invert crypto_sign_ed25519_ref10_fe_invert +#define fe_pow22523 crypto_sign_ed25519_ref10_fe_pow22523 + +extern void fe_frombytes(fe,const unsigned char *); +extern void fe_tobytes(unsigned char *,const fe); + +extern void fe_copy(fe,const fe); +extern int fe_isnonzero(const fe); +extern int fe_isnegative(const fe); +extern void fe_0(fe); +extern void fe_1(fe); +extern void fe_cswap(fe,fe,unsigned int); +extern void fe_cmov(fe,const fe,unsigned int); + +extern void fe_add(fe,const fe,const fe); +extern void fe_sub(fe,const fe,const fe); +extern void fe_neg(fe,const fe); +extern void fe_mul(fe,const fe,const fe); +extern void fe_sq(fe,const fe); +extern void fe_sq2(fe,const fe); +extern void fe_mul121666(fe,const fe); +extern void fe_invert(fe,const fe); +extern void fe_pow22523(fe,const fe); + +#endif diff --git a/jni/ed25519/fe_0.c b/jni/ed25519/fe_0.c new file mode 100644 index 000000000..ec879d733 --- /dev/null +++ b/jni/ed25519/fe_0.c @@ -0,0 +1,19 @@ +#include "fe.h" + +/* +h = 0 +*/ + +void fe_0(fe h) +{ + h[0] = 0; + h[1] = 0; + h[2] = 0; + h[3] = 0; + h[4] = 0; + h[5] = 0; + h[6] = 0; + h[7] = 0; + h[8] = 0; + h[9] = 0; +} diff --git a/jni/ed25519/fe_1.c b/jni/ed25519/fe_1.c new file mode 100644 index 000000000..8cf778484 --- /dev/null +++ b/jni/ed25519/fe_1.c @@ -0,0 +1,19 @@ +#include "fe.h" + +/* +h = 1 +*/ + +void fe_1(fe h) +{ + h[0] = 1; + h[1] = 0; + h[2] = 0; + h[3] = 0; + h[4] = 0; + h[5] = 0; + h[6] = 0; + h[7] = 0; + h[8] = 0; + h[9] = 0; +} diff --git a/jni/ed25519/fe_add.c b/jni/ed25519/fe_add.c new file mode 100644 index 000000000..e6a81da20 --- /dev/null +++ b/jni/ed25519/fe_add.c @@ -0,0 +1,57 @@ +#include "fe.h" + +/* +h = f + g +Can overlap h with f or g. + +Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + +Postconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +void fe_add(fe h,const fe f,const fe g) +{ + crypto_int32 f0 = f[0]; + crypto_int32 f1 = f[1]; + crypto_int32 f2 = f[2]; + crypto_int32 f3 = f[3]; + crypto_int32 f4 = f[4]; + crypto_int32 f5 = f[5]; + crypto_int32 f6 = f[6]; + crypto_int32 f7 = f[7]; + crypto_int32 f8 = f[8]; + crypto_int32 f9 = f[9]; + crypto_int32 g0 = g[0]; + crypto_int32 g1 = g[1]; + crypto_int32 g2 = g[2]; + crypto_int32 g3 = g[3]; + crypto_int32 g4 = g[4]; + crypto_int32 g5 = g[5]; + crypto_int32 g6 = g[6]; + crypto_int32 g7 = g[7]; + crypto_int32 g8 = g[8]; + crypto_int32 g9 = g[9]; + crypto_int32 h0 = f0 + g0; + crypto_int32 h1 = f1 + g1; + crypto_int32 h2 = f2 + g2; + crypto_int32 h3 = f3 + g3; + crypto_int32 h4 = f4 + g4; + crypto_int32 h5 = f5 + g5; + crypto_int32 h6 = f6 + g6; + crypto_int32 h7 = f7 + g7; + crypto_int32 h8 = f8 + g8; + crypto_int32 h9 = f9 + g9; + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} diff --git a/jni/ed25519/fe_cmov.c b/jni/ed25519/fe_cmov.c new file mode 100644 index 000000000..8ca584fb1 --- /dev/null +++ b/jni/ed25519/fe_cmov.c @@ -0,0 +1,63 @@ +#include "fe.h" + +/* +Replace (f,g) with (g,g) if b == 1; +replace (f,g) with (f,g) if b == 0. + +Preconditions: b in {0,1}. +*/ + +void fe_cmov(fe f,const fe g,unsigned int b) +{ + crypto_int32 f0 = f[0]; + crypto_int32 f1 = f[1]; + crypto_int32 f2 = f[2]; + crypto_int32 f3 = f[3]; + crypto_int32 f4 = f[4]; + crypto_int32 f5 = f[5]; + crypto_int32 f6 = f[6]; + crypto_int32 f7 = f[7]; + crypto_int32 f8 = f[8]; + crypto_int32 f9 = f[9]; + crypto_int32 g0 = g[0]; + crypto_int32 g1 = g[1]; + crypto_int32 g2 = g[2]; + crypto_int32 g3 = g[3]; + crypto_int32 g4 = g[4]; + crypto_int32 g5 = g[5]; + crypto_int32 g6 = g[6]; + crypto_int32 g7 = g[7]; + crypto_int32 g8 = g[8]; + crypto_int32 g9 = g[9]; + crypto_int32 x0 = f0 ^ g0; + crypto_int32 x1 = f1 ^ g1; + crypto_int32 x2 = f2 ^ g2; + crypto_int32 x3 = f3 ^ g3; + crypto_int32 x4 = f4 ^ g4; + crypto_int32 x5 = f5 ^ g5; + crypto_int32 x6 = f6 ^ g6; + crypto_int32 x7 = f7 ^ g7; + crypto_int32 x8 = f8 ^ g8; + crypto_int32 x9 = f9 ^ g9; + b = -b; + x0 &= b; + x1 &= b; + x2 &= b; + x3 &= b; + x4 &= b; + x5 &= b; + x6 &= b; + x7 &= b; + x8 &= b; + x9 &= b; + f[0] = f0 ^ x0; + f[1] = f1 ^ x1; + f[2] = f2 ^ x2; + f[3] = f3 ^ x3; + f[4] = f4 ^ x4; + f[5] = f5 ^ x5; + f[6] = f6 ^ x6; + f[7] = f7 ^ x7; + f[8] = f8 ^ x8; + f[9] = f9 ^ x9; +} diff --git a/jni/ed25519/fe_copy.c b/jni/ed25519/fe_copy.c new file mode 100644 index 000000000..9c5bf865a --- /dev/null +++ b/jni/ed25519/fe_copy.c @@ -0,0 +1,29 @@ +#include "fe.h" + +/* +h = f +*/ + +void fe_copy(fe h,const fe f) +{ + crypto_int32 f0 = f[0]; + crypto_int32 f1 = f[1]; + crypto_int32 f2 = f[2]; + crypto_int32 f3 = f[3]; + crypto_int32 f4 = f[4]; + crypto_int32 f5 = f[5]; + crypto_int32 f6 = f[6]; + crypto_int32 f7 = f[7]; + crypto_int32 f8 = f[8]; + crypto_int32 f9 = f[9]; + h[0] = f0; + h[1] = f1; + h[2] = f2; + h[3] = f3; + h[4] = f4; + h[5] = f5; + h[6] = f6; + h[7] = f7; + h[8] = f8; + h[9] = f9; +} diff --git a/jni/ed25519/fe_frombytes.c b/jni/ed25519/fe_frombytes.c new file mode 100644 index 000000000..5c1791748 --- /dev/null +++ b/jni/ed25519/fe_frombytes.c @@ -0,0 +1,73 @@ +#include "fe.h" +#include "crypto_int64.h" +#include "crypto_uint64.h" + +static crypto_uint64 load_3(const unsigned char *in) +{ + crypto_uint64 result; + result = (crypto_uint64) in[0]; + result |= ((crypto_uint64) in[1]) << 8; + result |= ((crypto_uint64) in[2]) << 16; + return result; +} + +static crypto_uint64 load_4(const unsigned char *in) +{ + crypto_uint64 result; + result = (crypto_uint64) in[0]; + result |= ((crypto_uint64) in[1]) << 8; + result |= ((crypto_uint64) in[2]) << 16; + result |= ((crypto_uint64) in[3]) << 24; + return result; +} + +/* +Ignores top bit of h. +*/ + +void fe_frombytes(fe h,const unsigned char *s) +{ + crypto_int64 h0 = load_4(s); + crypto_int64 h1 = load_3(s + 4) << 6; + crypto_int64 h2 = load_3(s + 7) << 5; + crypto_int64 h3 = load_3(s + 10) << 3; + crypto_int64 h4 = load_3(s + 13) << 2; + crypto_int64 h5 = load_4(s + 16); + crypto_int64 h6 = load_3(s + 20) << 7; + crypto_int64 h7 = load_3(s + 23) << 5; + crypto_int64 h8 = load_3(s + 26) << 4; + crypto_int64 h9 = (load_3(s + 29) & 8388607) << 2; + crypto_int64 carry0; + crypto_int64 carry1; + crypto_int64 carry2; + crypto_int64 carry3; + crypto_int64 carry4; + crypto_int64 carry5; + crypto_int64 carry6; + crypto_int64 carry7; + crypto_int64 carry8; + crypto_int64 carry9; + + carry9 = (h9 + (crypto_int64) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + carry1 = (h1 + (crypto_int64) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry3 = (h3 + (crypto_int64) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry5 = (h5 + (crypto_int64) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + carry7 = (h7 + (crypto_int64) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry2 = (h2 + (crypto_int64) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry6 = (h6 + (crypto_int64) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + carry8 = (h8 + (crypto_int64) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} diff --git a/jni/ed25519/fe_invert.c b/jni/ed25519/fe_invert.c new file mode 100644 index 000000000..bcfdb8ff8 --- /dev/null +++ b/jni/ed25519/fe_invert.c @@ -0,0 +1,14 @@ +#include "fe.h" + +void fe_invert(fe out,const fe z) +{ + fe t0; + fe t1; + fe t2; + fe t3; + int i; + +#include "pow225521.h" + + return; +} diff --git a/jni/ed25519/fe_isnegative.c b/jni/ed25519/fe_isnegative.c new file mode 100644 index 000000000..3b2c8b8d5 --- /dev/null +++ b/jni/ed25519/fe_isnegative.c @@ -0,0 +1,16 @@ +#include "fe.h" + +/* +return 1 if f is in {1,3,5,...,q-2} +return 0 if f is in {0,2,4,...,q-1} + +Preconditions: + |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +int fe_isnegative(const fe f) +{ + unsigned char s[32]; + fe_tobytes(s,f); + return s[0] & 1; +} diff --git a/jni/ed25519/fe_isnonzero.c b/jni/ed25519/fe_isnonzero.c new file mode 100644 index 000000000..47568001c --- /dev/null +++ b/jni/ed25519/fe_isnonzero.c @@ -0,0 +1,19 @@ +#include "fe.h" +#include "crypto_verify_32.h" + +/* +return 1 if f == 0 +return 0 if f != 0 + +Preconditions: + |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +static const unsigned char zero[32]; + +int fe_isnonzero(const fe f) +{ + unsigned char s[32]; + fe_tobytes(s,f); + return crypto_verify_32(s,zero); +} diff --git a/jni/ed25519/fe_mul.c b/jni/ed25519/fe_mul.c new file mode 100644 index 000000000..26ca8b368 --- /dev/null +++ b/jni/ed25519/fe_mul.c @@ -0,0 +1,253 @@ +#include "fe.h" +#include "crypto_int64.h" + +/* +h = f * g +Can overlap h with f or g. + +Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + |g| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + +Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. +*/ + +/* +Notes on implementation strategy: + +Using schoolbook multiplication. +Karatsuba would save a little in some cost models. + +Most multiplications by 2 and 19 are 32-bit precomputations; +cheaper than 64-bit postcomputations. + +There is one remaining multiplication by 19 in the carry chain; +one *19 precomputation can be merged into this, +but the resulting data flow is considerably less clean. + +There are 12 carries below. +10 of them are 2-way parallelizable and vectorizable. +Can get away with 11 carries, but then data flow is much deeper. + +With tighter constraints on inputs can squeeze carries into int32. +*/ + +void fe_mul(fe h,const fe f,const fe g) +{ + crypto_int32 f0 = f[0]; + crypto_int32 f1 = f[1]; + crypto_int32 f2 = f[2]; + crypto_int32 f3 = f[3]; + crypto_int32 f4 = f[4]; + crypto_int32 f5 = f[5]; + crypto_int32 f6 = f[6]; + crypto_int32 f7 = f[7]; + crypto_int32 f8 = f[8]; + crypto_int32 f9 = f[9]; + crypto_int32 g0 = g[0]; + crypto_int32 g1 = g[1]; + crypto_int32 g2 = g[2]; + crypto_int32 g3 = g[3]; + crypto_int32 g4 = g[4]; + crypto_int32 g5 = g[5]; + crypto_int32 g6 = g[6]; + crypto_int32 g7 = g[7]; + crypto_int32 g8 = g[8]; + crypto_int32 g9 = g[9]; + crypto_int32 g1_19 = 19 * g1; /* 1.959375*2^29 */ + crypto_int32 g2_19 = 19 * g2; /* 1.959375*2^30; still ok */ + crypto_int32 g3_19 = 19 * g3; + crypto_int32 g4_19 = 19 * g4; + crypto_int32 g5_19 = 19 * g5; + crypto_int32 g6_19 = 19 * g6; + crypto_int32 g7_19 = 19 * g7; + crypto_int32 g8_19 = 19 * g8; + crypto_int32 g9_19 = 19 * g9; + crypto_int32 f1_2 = 2 * f1; + crypto_int32 f3_2 = 2 * f3; + crypto_int32 f5_2 = 2 * f5; + crypto_int32 f7_2 = 2 * f7; + crypto_int32 f9_2 = 2 * f9; + crypto_int64 f0g0 = f0 * (crypto_int64) g0; + crypto_int64 f0g1 = f0 * (crypto_int64) g1; + crypto_int64 f0g2 = f0 * (crypto_int64) g2; + crypto_int64 f0g3 = f0 * (crypto_int64) g3; + crypto_int64 f0g4 = f0 * (crypto_int64) g4; + crypto_int64 f0g5 = f0 * (crypto_int64) g5; + crypto_int64 f0g6 = f0 * (crypto_int64) g6; + crypto_int64 f0g7 = f0 * (crypto_int64) g7; + crypto_int64 f0g8 = f0 * (crypto_int64) g8; + crypto_int64 f0g9 = f0 * (crypto_int64) g9; + crypto_int64 f1g0 = f1 * (crypto_int64) g0; + crypto_int64 f1g1_2 = f1_2 * (crypto_int64) g1; + crypto_int64 f1g2 = f1 * (crypto_int64) g2; + crypto_int64 f1g3_2 = f1_2 * (crypto_int64) g3; + crypto_int64 f1g4 = f1 * (crypto_int64) g4; + crypto_int64 f1g5_2 = f1_2 * (crypto_int64) g5; + crypto_int64 f1g6 = f1 * (crypto_int64) g6; + crypto_int64 f1g7_2 = f1_2 * (crypto_int64) g7; + crypto_int64 f1g8 = f1 * (crypto_int64) g8; + crypto_int64 f1g9_38 = f1_2 * (crypto_int64) g9_19; + crypto_int64 f2g0 = f2 * (crypto_int64) g0; + crypto_int64 f2g1 = f2 * (crypto_int64) g1; + crypto_int64 f2g2 = f2 * (crypto_int64) g2; + crypto_int64 f2g3 = f2 * (crypto_int64) g3; + crypto_int64 f2g4 = f2 * (crypto_int64) g4; + crypto_int64 f2g5 = f2 * (crypto_int64) g5; + crypto_int64 f2g6 = f2 * (crypto_int64) g6; + crypto_int64 f2g7 = f2 * (crypto_int64) g7; + crypto_int64 f2g8_19 = f2 * (crypto_int64) g8_19; + crypto_int64 f2g9_19 = f2 * (crypto_int64) g9_19; + crypto_int64 f3g0 = f3 * (crypto_int64) g0; + crypto_int64 f3g1_2 = f3_2 * (crypto_int64) g1; + crypto_int64 f3g2 = f3 * (crypto_int64) g2; + crypto_int64 f3g3_2 = f3_2 * (crypto_int64) g3; + crypto_int64 f3g4 = f3 * (crypto_int64) g4; + crypto_int64 f3g5_2 = f3_2 * (crypto_int64) g5; + crypto_int64 f3g6 = f3 * (crypto_int64) g6; + crypto_int64 f3g7_38 = f3_2 * (crypto_int64) g7_19; + crypto_int64 f3g8_19 = f3 * (crypto_int64) g8_19; + crypto_int64 f3g9_38 = f3_2 * (crypto_int64) g9_19; + crypto_int64 f4g0 = f4 * (crypto_int64) g0; + crypto_int64 f4g1 = f4 * (crypto_int64) g1; + crypto_int64 f4g2 = f4 * (crypto_int64) g2; + crypto_int64 f4g3 = f4 * (crypto_int64) g3; + crypto_int64 f4g4 = f4 * (crypto_int64) g4; + crypto_int64 f4g5 = f4 * (crypto_int64) g5; + crypto_int64 f4g6_19 = f4 * (crypto_int64) g6_19; + crypto_int64 f4g7_19 = f4 * (crypto_int64) g7_19; + crypto_int64 f4g8_19 = f4 * (crypto_int64) g8_19; + crypto_int64 f4g9_19 = f4 * (crypto_int64) g9_19; + crypto_int64 f5g0 = f5 * (crypto_int64) g0; + crypto_int64 f5g1_2 = f5_2 * (crypto_int64) g1; + crypto_int64 f5g2 = f5 * (crypto_int64) g2; + crypto_int64 f5g3_2 = f5_2 * (crypto_int64) g3; + crypto_int64 f5g4 = f5 * (crypto_int64) g4; + crypto_int64 f5g5_38 = f5_2 * (crypto_int64) g5_19; + crypto_int64 f5g6_19 = f5 * (crypto_int64) g6_19; + crypto_int64 f5g7_38 = f5_2 * (crypto_int64) g7_19; + crypto_int64 f5g8_19 = f5 * (crypto_int64) g8_19; + crypto_int64 f5g9_38 = f5_2 * (crypto_int64) g9_19; + crypto_int64 f6g0 = f6 * (crypto_int64) g0; + crypto_int64 f6g1 = f6 * (crypto_int64) g1; + crypto_int64 f6g2 = f6 * (crypto_int64) g2; + crypto_int64 f6g3 = f6 * (crypto_int64) g3; + crypto_int64 f6g4_19 = f6 * (crypto_int64) g4_19; + crypto_int64 f6g5_19 = f6 * (crypto_int64) g5_19; + crypto_int64 f6g6_19 = f6 * (crypto_int64) g6_19; + crypto_int64 f6g7_19 = f6 * (crypto_int64) g7_19; + crypto_int64 f6g8_19 = f6 * (crypto_int64) g8_19; + crypto_int64 f6g9_19 = f6 * (crypto_int64) g9_19; + crypto_int64 f7g0 = f7 * (crypto_int64) g0; + crypto_int64 f7g1_2 = f7_2 * (crypto_int64) g1; + crypto_int64 f7g2 = f7 * (crypto_int64) g2; + crypto_int64 f7g3_38 = f7_2 * (crypto_int64) g3_19; + crypto_int64 f7g4_19 = f7 * (crypto_int64) g4_19; + crypto_int64 f7g5_38 = f7_2 * (crypto_int64) g5_19; + crypto_int64 f7g6_19 = f7 * (crypto_int64) g6_19; + crypto_int64 f7g7_38 = f7_2 * (crypto_int64) g7_19; + crypto_int64 f7g8_19 = f7 * (crypto_int64) g8_19; + crypto_int64 f7g9_38 = f7_2 * (crypto_int64) g9_19; + crypto_int64 f8g0 = f8 * (crypto_int64) g0; + crypto_int64 f8g1 = f8 * (crypto_int64) g1; + crypto_int64 f8g2_19 = f8 * (crypto_int64) g2_19; + crypto_int64 f8g3_19 = f8 * (crypto_int64) g3_19; + crypto_int64 f8g4_19 = f8 * (crypto_int64) g4_19; + crypto_int64 f8g5_19 = f8 * (crypto_int64) g5_19; + crypto_int64 f8g6_19 = f8 * (crypto_int64) g6_19; + crypto_int64 f8g7_19 = f8 * (crypto_int64) g7_19; + crypto_int64 f8g8_19 = f8 * (crypto_int64) g8_19; + crypto_int64 f8g9_19 = f8 * (crypto_int64) g9_19; + crypto_int64 f9g0 = f9 * (crypto_int64) g0; + crypto_int64 f9g1_38 = f9_2 * (crypto_int64) g1_19; + crypto_int64 f9g2_19 = f9 * (crypto_int64) g2_19; + crypto_int64 f9g3_38 = f9_2 * (crypto_int64) g3_19; + crypto_int64 f9g4_19 = f9 * (crypto_int64) g4_19; + crypto_int64 f9g5_38 = f9_2 * (crypto_int64) g5_19; + crypto_int64 f9g6_19 = f9 * (crypto_int64) g6_19; + crypto_int64 f9g7_38 = f9_2 * (crypto_int64) g7_19; + crypto_int64 f9g8_19 = f9 * (crypto_int64) g8_19; + crypto_int64 f9g9_38 = f9_2 * (crypto_int64) g9_19; + crypto_int64 h0 = f0g0+f1g9_38+f2g8_19+f3g7_38+f4g6_19+f5g5_38+f6g4_19+f7g3_38+f8g2_19+f9g1_38; + crypto_int64 h1 = f0g1+f1g0 +f2g9_19+f3g8_19+f4g7_19+f5g6_19+f6g5_19+f7g4_19+f8g3_19+f9g2_19; + crypto_int64 h2 = f0g2+f1g1_2 +f2g0 +f3g9_38+f4g8_19+f5g7_38+f6g6_19+f7g5_38+f8g4_19+f9g3_38; + crypto_int64 h3 = f0g3+f1g2 +f2g1 +f3g0 +f4g9_19+f5g8_19+f6g7_19+f7g6_19+f8g5_19+f9g4_19; + crypto_int64 h4 = f0g4+f1g3_2 +f2g2 +f3g1_2 +f4g0 +f5g9_38+f6g8_19+f7g7_38+f8g6_19+f9g5_38; + crypto_int64 h5 = f0g5+f1g4 +f2g3 +f3g2 +f4g1 +f5g0 +f6g9_19+f7g8_19+f8g7_19+f9g6_19; + crypto_int64 h6 = f0g6+f1g5_2 +f2g4 +f3g3_2 +f4g2 +f5g1_2 +f6g0 +f7g9_38+f8g8_19+f9g7_38; + crypto_int64 h7 = f0g7+f1g6 +f2g5 +f3g4 +f4g3 +f5g2 +f6g1 +f7g0 +f8g9_19+f9g8_19; + crypto_int64 h8 = f0g8+f1g7_2 +f2g6 +f3g5_2 +f4g4 +f5g3_2 +f6g2 +f7g1_2 +f8g0 +f9g9_38; + crypto_int64 h9 = f0g9+f1g8 +f2g7 +f3g6 +f4g5 +f5g4 +f6g3 +f7g2 +f8g1 +f9g0 ; + crypto_int64 carry0; + crypto_int64 carry1; + crypto_int64 carry2; + crypto_int64 carry3; + crypto_int64 carry4; + crypto_int64 carry5; + crypto_int64 carry6; + crypto_int64 carry7; + crypto_int64 carry8; + crypto_int64 carry9; + + /* + |h0| <= (1.65*1.65*2^52*(1+19+19+19+19)+1.65*1.65*2^50*(38+38+38+38+38)) + i.e. |h0| <= 1.4*2^60; narrower ranges for h2, h4, h6, h8 + |h1| <= (1.65*1.65*2^51*(1+1+19+19+19+19+19+19+19+19)) + i.e. |h1| <= 1.7*2^59; narrower ranges for h3, h5, h7, h9 + */ + + carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + /* |h0| <= 2^25 */ + /* |h4| <= 2^25 */ + /* |h1| <= 1.71*2^59 */ + /* |h5| <= 1.71*2^59 */ + + carry1 = (h1 + (crypto_int64) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry5 = (h5 + (crypto_int64) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + /* |h1| <= 2^24; from now on fits into int32 */ + /* |h5| <= 2^24; from now on fits into int32 */ + /* |h2| <= 1.41*2^60 */ + /* |h6| <= 1.41*2^60 */ + + carry2 = (h2 + (crypto_int64) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry6 = (h6 + (crypto_int64) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + /* |h2| <= 2^25; from now on fits into int32 unchanged */ + /* |h6| <= 2^25; from now on fits into int32 unchanged */ + /* |h3| <= 1.71*2^59 */ + /* |h7| <= 1.71*2^59 */ + + carry3 = (h3 + (crypto_int64) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry7 = (h7 + (crypto_int64) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + /* |h3| <= 2^24; from now on fits into int32 unchanged */ + /* |h7| <= 2^24; from now on fits into int32 unchanged */ + /* |h4| <= 1.72*2^34 */ + /* |h8| <= 1.41*2^60 */ + + carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry8 = (h8 + (crypto_int64) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + /* |h4| <= 2^25; from now on fits into int32 unchanged */ + /* |h8| <= 2^25; from now on fits into int32 unchanged */ + /* |h5| <= 1.01*2^24 */ + /* |h9| <= 1.71*2^59 */ + + carry9 = (h9 + (crypto_int64) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + /* |h9| <= 2^24; from now on fits into int32 unchanged */ + /* |h0| <= 1.1*2^39 */ + + carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + /* |h0| <= 2^25; from now on fits into int32 unchanged */ + /* |h1| <= 1.01*2^24 */ + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} diff --git a/jni/ed25519/fe_neg.c b/jni/ed25519/fe_neg.c new file mode 100644 index 000000000..2078ce528 --- /dev/null +++ b/jni/ed25519/fe_neg.c @@ -0,0 +1,45 @@ +#include "fe.h" + +/* +h = -f + +Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + +Postconditions: + |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +*/ + +void fe_neg(fe h,const fe f) +{ + crypto_int32 f0 = f[0]; + crypto_int32 f1 = f[1]; + crypto_int32 f2 = f[2]; + crypto_int32 f3 = f[3]; + crypto_int32 f4 = f[4]; + crypto_int32 f5 = f[5]; + crypto_int32 f6 = f[6]; + crypto_int32 f7 = f[7]; + crypto_int32 f8 = f[8]; + crypto_int32 f9 = f[9]; + crypto_int32 h0 = -f0; + crypto_int32 h1 = -f1; + crypto_int32 h2 = -f2; + crypto_int32 h3 = -f3; + crypto_int32 h4 = -f4; + crypto_int32 h5 = -f5; + crypto_int32 h6 = -f6; + crypto_int32 h7 = -f7; + crypto_int32 h8 = -f8; + crypto_int32 h9 = -f9; + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} diff --git a/jni/ed25519/fe_pow22523.c b/jni/ed25519/fe_pow22523.c new file mode 100644 index 000000000..56675a590 --- /dev/null +++ b/jni/ed25519/fe_pow22523.c @@ -0,0 +1,13 @@ +#include "fe.h" + +void fe_pow22523(fe out,const fe z) +{ + fe t0; + fe t1; + fe t2; + int i; + +#include "pow22523.h" + + return; +} diff --git a/jni/ed25519/fe_sq.c b/jni/ed25519/fe_sq.c new file mode 100644 index 000000000..8dd119841 --- /dev/null +++ b/jni/ed25519/fe_sq.c @@ -0,0 +1,149 @@ +#include "fe.h" +#include "crypto_int64.h" + +/* +h = f * f +Can overlap h with f. + +Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + +Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. +*/ + +/* +See fe_mul.c for discussion of implementation strategy. +*/ + +void fe_sq(fe h,const fe f) +{ + crypto_int32 f0 = f[0]; + crypto_int32 f1 = f[1]; + crypto_int32 f2 = f[2]; + crypto_int32 f3 = f[3]; + crypto_int32 f4 = f[4]; + crypto_int32 f5 = f[5]; + crypto_int32 f6 = f[6]; + crypto_int32 f7 = f[7]; + crypto_int32 f8 = f[8]; + crypto_int32 f9 = f[9]; + crypto_int32 f0_2 = 2 * f0; + crypto_int32 f1_2 = 2 * f1; + crypto_int32 f2_2 = 2 * f2; + crypto_int32 f3_2 = 2 * f3; + crypto_int32 f4_2 = 2 * f4; + crypto_int32 f5_2 = 2 * f5; + crypto_int32 f6_2 = 2 * f6; + crypto_int32 f7_2 = 2 * f7; + crypto_int32 f5_38 = 38 * f5; /* 1.959375*2^30 */ + crypto_int32 f6_19 = 19 * f6; /* 1.959375*2^30 */ + crypto_int32 f7_38 = 38 * f7; /* 1.959375*2^30 */ + crypto_int32 f8_19 = 19 * f8; /* 1.959375*2^30 */ + crypto_int32 f9_38 = 38 * f9; /* 1.959375*2^30 */ + crypto_int64 f0f0 = f0 * (crypto_int64) f0; + crypto_int64 f0f1_2 = f0_2 * (crypto_int64) f1; + crypto_int64 f0f2_2 = f0_2 * (crypto_int64) f2; + crypto_int64 f0f3_2 = f0_2 * (crypto_int64) f3; + crypto_int64 f0f4_2 = f0_2 * (crypto_int64) f4; + crypto_int64 f0f5_2 = f0_2 * (crypto_int64) f5; + crypto_int64 f0f6_2 = f0_2 * (crypto_int64) f6; + crypto_int64 f0f7_2 = f0_2 * (crypto_int64) f7; + crypto_int64 f0f8_2 = f0_2 * (crypto_int64) f8; + crypto_int64 f0f9_2 = f0_2 * (crypto_int64) f9; + crypto_int64 f1f1_2 = f1_2 * (crypto_int64) f1; + crypto_int64 f1f2_2 = f1_2 * (crypto_int64) f2; + crypto_int64 f1f3_4 = f1_2 * (crypto_int64) f3_2; + crypto_int64 f1f4_2 = f1_2 * (crypto_int64) f4; + crypto_int64 f1f5_4 = f1_2 * (crypto_int64) f5_2; + crypto_int64 f1f6_2 = f1_2 * (crypto_int64) f6; + crypto_int64 f1f7_4 = f1_2 * (crypto_int64) f7_2; + crypto_int64 f1f8_2 = f1_2 * (crypto_int64) f8; + crypto_int64 f1f9_76 = f1_2 * (crypto_int64) f9_38; + crypto_int64 f2f2 = f2 * (crypto_int64) f2; + crypto_int64 f2f3_2 = f2_2 * (crypto_int64) f3; + crypto_int64 f2f4_2 = f2_2 * (crypto_int64) f4; + crypto_int64 f2f5_2 = f2_2 * (crypto_int64) f5; + crypto_int64 f2f6_2 = f2_2 * (crypto_int64) f6; + crypto_int64 f2f7_2 = f2_2 * (crypto_int64) f7; + crypto_int64 f2f8_38 = f2_2 * (crypto_int64) f8_19; + crypto_int64 f2f9_38 = f2 * (crypto_int64) f9_38; + crypto_int64 f3f3_2 = f3_2 * (crypto_int64) f3; + crypto_int64 f3f4_2 = f3_2 * (crypto_int64) f4; + crypto_int64 f3f5_4 = f3_2 * (crypto_int64) f5_2; + crypto_int64 f3f6_2 = f3_2 * (crypto_int64) f6; + crypto_int64 f3f7_76 = f3_2 * (crypto_int64) f7_38; + crypto_int64 f3f8_38 = f3_2 * (crypto_int64) f8_19; + crypto_int64 f3f9_76 = f3_2 * (crypto_int64) f9_38; + crypto_int64 f4f4 = f4 * (crypto_int64) f4; + crypto_int64 f4f5_2 = f4_2 * (crypto_int64) f5; + crypto_int64 f4f6_38 = f4_2 * (crypto_int64) f6_19; + crypto_int64 f4f7_38 = f4 * (crypto_int64) f7_38; + crypto_int64 f4f8_38 = f4_2 * (crypto_int64) f8_19; + crypto_int64 f4f9_38 = f4 * (crypto_int64) f9_38; + crypto_int64 f5f5_38 = f5 * (crypto_int64) f5_38; + crypto_int64 f5f6_38 = f5_2 * (crypto_int64) f6_19; + crypto_int64 f5f7_76 = f5_2 * (crypto_int64) f7_38; + crypto_int64 f5f8_38 = f5_2 * (crypto_int64) f8_19; + crypto_int64 f5f9_76 = f5_2 * (crypto_int64) f9_38; + crypto_int64 f6f6_19 = f6 * (crypto_int64) f6_19; + crypto_int64 f6f7_38 = f6 * (crypto_int64) f7_38; + crypto_int64 f6f8_38 = f6_2 * (crypto_int64) f8_19; + crypto_int64 f6f9_38 = f6 * (crypto_int64) f9_38; + crypto_int64 f7f7_38 = f7 * (crypto_int64) f7_38; + crypto_int64 f7f8_38 = f7_2 * (crypto_int64) f8_19; + crypto_int64 f7f9_76 = f7_2 * (crypto_int64) f9_38; + crypto_int64 f8f8_19 = f8 * (crypto_int64) f8_19; + crypto_int64 f8f9_38 = f8 * (crypto_int64) f9_38; + crypto_int64 f9f9_38 = f9 * (crypto_int64) f9_38; + crypto_int64 h0 = f0f0 +f1f9_76+f2f8_38+f3f7_76+f4f6_38+f5f5_38; + crypto_int64 h1 = f0f1_2+f2f9_38+f3f8_38+f4f7_38+f5f6_38; + crypto_int64 h2 = f0f2_2+f1f1_2 +f3f9_76+f4f8_38+f5f7_76+f6f6_19; + crypto_int64 h3 = f0f3_2+f1f2_2 +f4f9_38+f5f8_38+f6f7_38; + crypto_int64 h4 = f0f4_2+f1f3_4 +f2f2 +f5f9_76+f6f8_38+f7f7_38; + crypto_int64 h5 = f0f5_2+f1f4_2 +f2f3_2 +f6f9_38+f7f8_38; + crypto_int64 h6 = f0f6_2+f1f5_4 +f2f4_2 +f3f3_2 +f7f9_76+f8f8_19; + crypto_int64 h7 = f0f7_2+f1f6_2 +f2f5_2 +f3f4_2 +f8f9_38; + crypto_int64 h8 = f0f8_2+f1f7_4 +f2f6_2 +f3f5_4 +f4f4 +f9f9_38; + crypto_int64 h9 = f0f9_2+f1f8_2 +f2f7_2 +f3f6_2 +f4f5_2; + crypto_int64 carry0; + crypto_int64 carry1; + crypto_int64 carry2; + crypto_int64 carry3; + crypto_int64 carry4; + crypto_int64 carry5; + crypto_int64 carry6; + crypto_int64 carry7; + crypto_int64 carry8; + crypto_int64 carry9; + + carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + + carry1 = (h1 + (crypto_int64) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry5 = (h5 + (crypto_int64) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + + carry2 = (h2 + (crypto_int64) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry6 = (h6 + (crypto_int64) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + + carry3 = (h3 + (crypto_int64) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry7 = (h7 + (crypto_int64) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry8 = (h8 + (crypto_int64) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + carry9 = (h9 + (crypto_int64) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + + carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} diff --git a/jni/ed25519/fe_sq2.c b/jni/ed25519/fe_sq2.c new file mode 100644 index 000000000..026ed3aac --- /dev/null +++ b/jni/ed25519/fe_sq2.c @@ -0,0 +1,160 @@ +#include "fe.h" +#include "crypto_int64.h" + +/* +h = 2 * f * f +Can overlap h with f. + +Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + +Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. +*/ + +/* +See fe_mul.c for discussion of implementation strategy. +*/ + +void fe_sq2(fe h,const fe f) +{ + crypto_int32 f0 = f[0]; + crypto_int32 f1 = f[1]; + crypto_int32 f2 = f[2]; + crypto_int32 f3 = f[3]; + crypto_int32 f4 = f[4]; + crypto_int32 f5 = f[5]; + crypto_int32 f6 = f[6]; + crypto_int32 f7 = f[7]; + crypto_int32 f8 = f[8]; + crypto_int32 f9 = f[9]; + crypto_int32 f0_2 = 2 * f0; + crypto_int32 f1_2 = 2 * f1; + crypto_int32 f2_2 = 2 * f2; + crypto_int32 f3_2 = 2 * f3; + crypto_int32 f4_2 = 2 * f4; + crypto_int32 f5_2 = 2 * f5; + crypto_int32 f6_2 = 2 * f6; + crypto_int32 f7_2 = 2 * f7; + crypto_int32 f5_38 = 38 * f5; /* 1.959375*2^30 */ + crypto_int32 f6_19 = 19 * f6; /* 1.959375*2^30 */ + crypto_int32 f7_38 = 38 * f7; /* 1.959375*2^30 */ + crypto_int32 f8_19 = 19 * f8; /* 1.959375*2^30 */ + crypto_int32 f9_38 = 38 * f9; /* 1.959375*2^30 */ + crypto_int64 f0f0 = f0 * (crypto_int64) f0; + crypto_int64 f0f1_2 = f0_2 * (crypto_int64) f1; + crypto_int64 f0f2_2 = f0_2 * (crypto_int64) f2; + crypto_int64 f0f3_2 = f0_2 * (crypto_int64) f3; + crypto_int64 f0f4_2 = f0_2 * (crypto_int64) f4; + crypto_int64 f0f5_2 = f0_2 * (crypto_int64) f5; + crypto_int64 f0f6_2 = f0_2 * (crypto_int64) f6; + crypto_int64 f0f7_2 = f0_2 * (crypto_int64) f7; + crypto_int64 f0f8_2 = f0_2 * (crypto_int64) f8; + crypto_int64 f0f9_2 = f0_2 * (crypto_int64) f9; + crypto_int64 f1f1_2 = f1_2 * (crypto_int64) f1; + crypto_int64 f1f2_2 = f1_2 * (crypto_int64) f2; + crypto_int64 f1f3_4 = f1_2 * (crypto_int64) f3_2; + crypto_int64 f1f4_2 = f1_2 * (crypto_int64) f4; + crypto_int64 f1f5_4 = f1_2 * (crypto_int64) f5_2; + crypto_int64 f1f6_2 = f1_2 * (crypto_int64) f6; + crypto_int64 f1f7_4 = f1_2 * (crypto_int64) f7_2; + crypto_int64 f1f8_2 = f1_2 * (crypto_int64) f8; + crypto_int64 f1f9_76 = f1_2 * (crypto_int64) f9_38; + crypto_int64 f2f2 = f2 * (crypto_int64) f2; + crypto_int64 f2f3_2 = f2_2 * (crypto_int64) f3; + crypto_int64 f2f4_2 = f2_2 * (crypto_int64) f4; + crypto_int64 f2f5_2 = f2_2 * (crypto_int64) f5; + crypto_int64 f2f6_2 = f2_2 * (crypto_int64) f6; + crypto_int64 f2f7_2 = f2_2 * (crypto_int64) f7; + crypto_int64 f2f8_38 = f2_2 * (crypto_int64) f8_19; + crypto_int64 f2f9_38 = f2 * (crypto_int64) f9_38; + crypto_int64 f3f3_2 = f3_2 * (crypto_int64) f3; + crypto_int64 f3f4_2 = f3_2 * (crypto_int64) f4; + crypto_int64 f3f5_4 = f3_2 * (crypto_int64) f5_2; + crypto_int64 f3f6_2 = f3_2 * (crypto_int64) f6; + crypto_int64 f3f7_76 = f3_2 * (crypto_int64) f7_38; + crypto_int64 f3f8_38 = f3_2 * (crypto_int64) f8_19; + crypto_int64 f3f9_76 = f3_2 * (crypto_int64) f9_38; + crypto_int64 f4f4 = f4 * (crypto_int64) f4; + crypto_int64 f4f5_2 = f4_2 * (crypto_int64) f5; + crypto_int64 f4f6_38 = f4_2 * (crypto_int64) f6_19; + crypto_int64 f4f7_38 = f4 * (crypto_int64) f7_38; + crypto_int64 f4f8_38 = f4_2 * (crypto_int64) f8_19; + crypto_int64 f4f9_38 = f4 * (crypto_int64) f9_38; + crypto_int64 f5f5_38 = f5 * (crypto_int64) f5_38; + crypto_int64 f5f6_38 = f5_2 * (crypto_int64) f6_19; + crypto_int64 f5f7_76 = f5_2 * (crypto_int64) f7_38; + crypto_int64 f5f8_38 = f5_2 * (crypto_int64) f8_19; + crypto_int64 f5f9_76 = f5_2 * (crypto_int64) f9_38; + crypto_int64 f6f6_19 = f6 * (crypto_int64) f6_19; + crypto_int64 f6f7_38 = f6 * (crypto_int64) f7_38; + crypto_int64 f6f8_38 = f6_2 * (crypto_int64) f8_19; + crypto_int64 f6f9_38 = f6 * (crypto_int64) f9_38; + crypto_int64 f7f7_38 = f7 * (crypto_int64) f7_38; + crypto_int64 f7f8_38 = f7_2 * (crypto_int64) f8_19; + crypto_int64 f7f9_76 = f7_2 * (crypto_int64) f9_38; + crypto_int64 f8f8_19 = f8 * (crypto_int64) f8_19; + crypto_int64 f8f9_38 = f8 * (crypto_int64) f9_38; + crypto_int64 f9f9_38 = f9 * (crypto_int64) f9_38; + crypto_int64 h0 = f0f0 +f1f9_76+f2f8_38+f3f7_76+f4f6_38+f5f5_38; + crypto_int64 h1 = f0f1_2+f2f9_38+f3f8_38+f4f7_38+f5f6_38; + crypto_int64 h2 = f0f2_2+f1f1_2 +f3f9_76+f4f8_38+f5f7_76+f6f6_19; + crypto_int64 h3 = f0f3_2+f1f2_2 +f4f9_38+f5f8_38+f6f7_38; + crypto_int64 h4 = f0f4_2+f1f3_4 +f2f2 +f5f9_76+f6f8_38+f7f7_38; + crypto_int64 h5 = f0f5_2+f1f4_2 +f2f3_2 +f6f9_38+f7f8_38; + crypto_int64 h6 = f0f6_2+f1f5_4 +f2f4_2 +f3f3_2 +f7f9_76+f8f8_19; + crypto_int64 h7 = f0f7_2+f1f6_2 +f2f5_2 +f3f4_2 +f8f9_38; + crypto_int64 h8 = f0f8_2+f1f7_4 +f2f6_2 +f3f5_4 +f4f4 +f9f9_38; + crypto_int64 h9 = f0f9_2+f1f8_2 +f2f7_2 +f3f6_2 +f4f5_2; + crypto_int64 carry0; + crypto_int64 carry1; + crypto_int64 carry2; + crypto_int64 carry3; + crypto_int64 carry4; + crypto_int64 carry5; + crypto_int64 carry6; + crypto_int64 carry7; + crypto_int64 carry8; + crypto_int64 carry9; + + h0 += h0; + h1 += h1; + h2 += h2; + h3 += h3; + h4 += h4; + h5 += h5; + h6 += h6; + h7 += h7; + h8 += h8; + h9 += h9; + + carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + + carry1 = (h1 + (crypto_int64) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry5 = (h5 + (crypto_int64) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + + carry2 = (h2 + (crypto_int64) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry6 = (h6 + (crypto_int64) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + + carry3 = (h3 + (crypto_int64) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry7 = (h7 + (crypto_int64) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry8 = (h8 + (crypto_int64) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + carry9 = (h9 + (crypto_int64) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + + carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} diff --git a/jni/ed25519/fe_sub.c b/jni/ed25519/fe_sub.c new file mode 100644 index 000000000..6e26b7df8 --- /dev/null +++ b/jni/ed25519/fe_sub.c @@ -0,0 +1,57 @@ +#include "fe.h" + +/* +h = f - g +Can overlap h with f or g. + +Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + +Postconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +void fe_sub(fe h,const fe f,const fe g) +{ + crypto_int32 f0 = f[0]; + crypto_int32 f1 = f[1]; + crypto_int32 f2 = f[2]; + crypto_int32 f3 = f[3]; + crypto_int32 f4 = f[4]; + crypto_int32 f5 = f[5]; + crypto_int32 f6 = f[6]; + crypto_int32 f7 = f[7]; + crypto_int32 f8 = f[8]; + crypto_int32 f9 = f[9]; + crypto_int32 g0 = g[0]; + crypto_int32 g1 = g[1]; + crypto_int32 g2 = g[2]; + crypto_int32 g3 = g[3]; + crypto_int32 g4 = g[4]; + crypto_int32 g5 = g[5]; + crypto_int32 g6 = g[6]; + crypto_int32 g7 = g[7]; + crypto_int32 g8 = g[8]; + crypto_int32 g9 = g[9]; + crypto_int32 h0 = f0 - g0; + crypto_int32 h1 = f1 - g1; + crypto_int32 h2 = f2 - g2; + crypto_int32 h3 = f3 - g3; + crypto_int32 h4 = f4 - g4; + crypto_int32 h5 = f5 - g5; + crypto_int32 h6 = f6 - g6; + crypto_int32 h7 = f7 - g7; + crypto_int32 h8 = f8 - g8; + crypto_int32 h9 = f9 - g9; + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} diff --git a/jni/ed25519/fe_tobytes.c b/jni/ed25519/fe_tobytes.c new file mode 100644 index 000000000..0a63baf9c --- /dev/null +++ b/jni/ed25519/fe_tobytes.c @@ -0,0 +1,119 @@ +#include "fe.h" + +/* +Preconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + +Write p=2^255-19; q=floor(h/p). +Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). + +Proof: + Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. + Also have |h-2^230 h9|<2^231 so |19 2^(-255)(h-2^230 h9)|<1/4. + + Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). + Then 0> 25; + q = (h0 + q) >> 26; + q = (h1 + q) >> 25; + q = (h2 + q) >> 26; + q = (h3 + q) >> 25; + q = (h4 + q) >> 26; + q = (h5 + q) >> 25; + q = (h6 + q) >> 26; + q = (h7 + q) >> 25; + q = (h8 + q) >> 26; + q = (h9 + q) >> 25; + + /* Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. */ + h0 += 19 * q; + /* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */ + + carry0 = h0 >> 26; h1 += carry0; h0 -= carry0 << 26; + carry1 = h1 >> 25; h2 += carry1; h1 -= carry1 << 25; + carry2 = h2 >> 26; h3 += carry2; h2 -= carry2 << 26; + carry3 = h3 >> 25; h4 += carry3; h3 -= carry3 << 25; + carry4 = h4 >> 26; h5 += carry4; h4 -= carry4 << 26; + carry5 = h5 >> 25; h6 += carry5; h5 -= carry5 << 25; + carry6 = h6 >> 26; h7 += carry6; h6 -= carry6 << 26; + carry7 = h7 >> 25; h8 += carry7; h7 -= carry7 << 25; + carry8 = h8 >> 26; h9 += carry8; h8 -= carry8 << 26; + carry9 = h9 >> 25; h9 -= carry9 << 25; + /* h10 = carry9 */ + + /* + Goal: Output h0+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. + Have h0+...+2^230 h9 between 0 and 2^255-1; + evidently 2^255 h10-2^255 q = 0. + Goal: Output h0+...+2^230 h9. + */ + + s[0] = h0 >> 0; + s[1] = h0 >> 8; + s[2] = h0 >> 16; + s[3] = (h0 >> 24) | (h1 << 2); + s[4] = h1 >> 6; + s[5] = h1 >> 14; + s[6] = (h1 >> 22) | (h2 << 3); + s[7] = h2 >> 5; + s[8] = h2 >> 13; + s[9] = (h2 >> 21) | (h3 << 5); + s[10] = h3 >> 3; + s[11] = h3 >> 11; + s[12] = (h3 >> 19) | (h4 << 6); + s[13] = h4 >> 2; + s[14] = h4 >> 10; + s[15] = h4 >> 18; + s[16] = h5 >> 0; + s[17] = h5 >> 8; + s[18] = h5 >> 16; + s[19] = (h5 >> 24) | (h6 << 1); + s[20] = h6 >> 7; + s[21] = h6 >> 15; + s[22] = (h6 >> 23) | (h7 << 3); + s[23] = h7 >> 5; + s[24] = h7 >> 13; + s[25] = (h7 >> 21) | (h8 << 4); + s[26] = h8 >> 4; + s[27] = h8 >> 12; + s[28] = (h8 >> 20) | (h9 << 6); + s[29] = h9 >> 2; + s[30] = h9 >> 10; + s[31] = h9 >> 18; +} diff --git a/jni/ed25519/ge.h b/jni/ed25519/ge.h new file mode 100644 index 000000000..55e95f95b --- /dev/null +++ b/jni/ed25519/ge.h @@ -0,0 +1,95 @@ +#ifndef GE_H +#define GE_H + +/* +ge means group element. + +Here the group is the set of pairs (x,y) of field elements (see fe.h) +satisfying -x^2 + y^2 = 1 + d x^2y^2 +where d = -121665/121666. + +Representations: + ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z + ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT + ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T + ge_precomp (Duif): (y+x,y-x,2dxy) +*/ + +#include "fe.h" + +typedef struct { + fe X; + fe Y; + fe Z; +} ge_p2; + +typedef struct { + fe X; + fe Y; + fe Z; + fe T; +} ge_p3; + +typedef struct { + fe X; + fe Y; + fe Z; + fe T; +} ge_p1p1; + +typedef struct { + fe yplusx; + fe yminusx; + fe xy2d; +} ge_precomp; + +typedef struct { + fe YplusX; + fe YminusX; + fe Z; + fe T2d; +} ge_cached; + +#define ge_frombytes_negate_vartime crypto_sign_ed25519_ref10_ge_frombytes_negate_vartime +#define ge_tobytes crypto_sign_ed25519_ref10_ge_tobytes +#define ge_p3_tobytes crypto_sign_ed25519_ref10_ge_p3_tobytes + +#define ge_p2_0 crypto_sign_ed25519_ref10_ge_p2_0 +#define ge_p3_0 crypto_sign_ed25519_ref10_ge_p3_0 +#define ge_precomp_0 crypto_sign_ed25519_ref10_ge_precomp_0 +#define ge_p3_to_p2 crypto_sign_ed25519_ref10_ge_p3_to_p2 +#define ge_p3_to_cached crypto_sign_ed25519_ref10_ge_p3_to_cached +#define ge_p1p1_to_p2 crypto_sign_ed25519_ref10_ge_p1p1_to_p2 +#define ge_p1p1_to_p3 crypto_sign_ed25519_ref10_ge_p1p1_to_p3 +#define ge_p2_dbl crypto_sign_ed25519_ref10_ge_p2_dbl +#define ge_p3_dbl crypto_sign_ed25519_ref10_ge_p3_dbl + +#define ge_madd crypto_sign_ed25519_ref10_ge_madd +#define ge_msub crypto_sign_ed25519_ref10_ge_msub +#define ge_add crypto_sign_ed25519_ref10_ge_add +#define ge_sub crypto_sign_ed25519_ref10_ge_sub +#define ge_scalarmult_base crypto_sign_ed25519_ref10_ge_scalarmult_base +#define ge_double_scalarmult_vartime crypto_sign_ed25519_ref10_ge_double_scalarmult_vartime + +extern void ge_tobytes(unsigned char *,const ge_p2 *); +extern void ge_p3_tobytes(unsigned char *,const ge_p3 *); +extern int ge_frombytes_negate_vartime(ge_p3 *,const unsigned char *); + +extern void ge_p2_0(ge_p2 *); +extern void ge_p3_0(ge_p3 *); +extern void ge_precomp_0(ge_precomp *); +extern void ge_p3_to_p2(ge_p2 *,const ge_p3 *); +extern void ge_p3_to_cached(ge_cached *,const ge_p3 *); +extern void ge_p1p1_to_p2(ge_p2 *,const ge_p1p1 *); +extern void ge_p1p1_to_p3(ge_p3 *,const ge_p1p1 *); +extern void ge_p2_dbl(ge_p1p1 *,const ge_p2 *); +extern void ge_p3_dbl(ge_p1p1 *,const ge_p3 *); + +extern void ge_madd(ge_p1p1 *,const ge_p3 *,const ge_precomp *); +extern void ge_msub(ge_p1p1 *,const ge_p3 *,const ge_precomp *); +extern void ge_add(ge_p1p1 *,const ge_p3 *,const ge_cached *); +extern void ge_sub(ge_p1p1 *,const ge_p3 *,const ge_cached *); +extern void ge_scalarmult_base(ge_p3 *,const unsigned char *); +extern void ge_double_scalarmult_vartime(ge_p2 *,const unsigned char *,const ge_p3 *,const unsigned char *); + +#endif diff --git a/jni/ed25519/ge_add.c b/jni/ed25519/ge_add.c new file mode 100644 index 000000000..da7ff5d2e --- /dev/null +++ b/jni/ed25519/ge_add.c @@ -0,0 +1,11 @@ +#include "ge.h" + +/* +r = p + q +*/ + +void ge_add(ge_p1p1 *r,const ge_p3 *p,const ge_cached *q) +{ + fe t0; +#include "ge_add.h" +} diff --git a/jni/ed25519/ge_add.h b/jni/ed25519/ge_add.h new file mode 100644 index 000000000..7481f8ffb --- /dev/null +++ b/jni/ed25519/ge_add.h @@ -0,0 +1,97 @@ + +/* qhasm: enter ge_add */ + +/* qhasm: fe X1 */ + +/* qhasm: fe Y1 */ + +/* qhasm: fe Z1 */ + +/* qhasm: fe Z2 */ + +/* qhasm: fe T1 */ + +/* qhasm: fe ZZ */ + +/* qhasm: fe YpX2 */ + +/* qhasm: fe YmX2 */ + +/* qhasm: fe T2d2 */ + +/* qhasm: fe X3 */ + +/* qhasm: fe Y3 */ + +/* qhasm: fe Z3 */ + +/* qhasm: fe T3 */ + +/* qhasm: fe YpX1 */ + +/* qhasm: fe YmX1 */ + +/* qhasm: fe A */ + +/* qhasm: fe B */ + +/* qhasm: fe C */ + +/* qhasm: fe D */ + +/* qhasm: YpX1 = Y1+X1 */ +/* asm 1: fe_add(>YpX1=fe#1,YpX1=r->X,Y,X); */ +fe_add(r->X,p->Y,p->X); + +/* qhasm: YmX1 = Y1-X1 */ +/* asm 1: fe_sub(>YmX1=fe#2,YmX1=r->Y,Y,X); */ +fe_sub(r->Y,p->Y,p->X); + +/* qhasm: A = YpX1*YpX2 */ +/* asm 1: fe_mul(>A=fe#3,A=r->Z,X,YplusX); */ +fe_mul(r->Z,r->X,q->YplusX); + +/* qhasm: B = YmX1*YmX2 */ +/* asm 1: fe_mul(>B=fe#2,B=r->Y,Y,YminusX); */ +fe_mul(r->Y,r->Y,q->YminusX); + +/* qhasm: C = T2d2*T1 */ +/* asm 1: fe_mul(>C=fe#4,C=r->T,T2d,T); */ +fe_mul(r->T,q->T2d,p->T); + +/* qhasm: ZZ = Z1*Z2 */ +/* asm 1: fe_mul(>ZZ=fe#1,ZZ=r->X,Z,Z); */ +fe_mul(r->X,p->Z,q->Z); + +/* qhasm: D = 2*ZZ */ +/* asm 1: fe_add(>D=fe#5,D=t0,X,X); */ +fe_add(t0,r->X,r->X); + +/* qhasm: X3 = A-B */ +/* asm 1: fe_sub(>X3=fe#1,X3=r->X,Z,Y); */ +fe_sub(r->X,r->Z,r->Y); + +/* qhasm: Y3 = A+B */ +/* asm 1: fe_add(>Y3=fe#2,Y3=r->Y,Z,Y); */ +fe_add(r->Y,r->Z,r->Y); + +/* qhasm: Z3 = D+C */ +/* asm 1: fe_add(>Z3=fe#3,Z3=r->Z,T); */ +fe_add(r->Z,t0,r->T); + +/* qhasm: T3 = D-C */ +/* asm 1: fe_sub(>T3=fe#4,T3=r->T,T); */ +fe_sub(r->T,t0,r->T); + +/* qhasm: return */ diff --git a/jni/ed25519/ge_double_scalarmult.c b/jni/ed25519/ge_double_scalarmult.c new file mode 100644 index 000000000..f8bf4bf77 --- /dev/null +++ b/jni/ed25519/ge_double_scalarmult.c @@ -0,0 +1,96 @@ +#include "ge.h" + +static void slide(signed char *r,const unsigned char *a) +{ + int i; + int b; + int k; + + for (i = 0;i < 256;++i) + r[i] = 1 & (a[i >> 3] >> (i & 7)); + + for (i = 0;i < 256;++i) + if (r[i]) { + for (b = 1;b <= 6 && i + b < 256;++b) { + if (r[i + b]) { + if (r[i] + (r[i + b] << b) <= 15) { + r[i] += r[i + b] << b; r[i + b] = 0; + } else if (r[i] - (r[i + b] << b) >= -15) { + r[i] -= r[i + b] << b; + for (k = i + b;k < 256;++k) { + if (!r[k]) { + r[k] = 1; + break; + } + r[k] = 0; + } + } else + break; + } + } + } + +} + +static ge_precomp Bi[8] = { +#include "base2.h" +} ; + +/* +r = a * A + b * B +where a = a[0]+256*a[1]+...+256^31 a[31]. +and b = b[0]+256*b[1]+...+256^31 b[31]. +B is the Ed25519 base point (x,4/5) with x positive. +*/ + +void ge_double_scalarmult_vartime(ge_p2 *r,const unsigned char *a,const ge_p3 *A,const unsigned char *b) +{ + signed char aslide[256]; + signed char bslide[256]; + ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */ + ge_p1p1 t; + ge_p3 u; + ge_p3 A2; + int i; + + slide(aslide,a); + slide(bslide,b); + + ge_p3_to_cached(&Ai[0],A); + ge_p3_dbl(&t,A); ge_p1p1_to_p3(&A2,&t); + ge_add(&t,&A2,&Ai[0]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[1],&u); + ge_add(&t,&A2,&Ai[1]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[2],&u); + ge_add(&t,&A2,&Ai[2]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[3],&u); + ge_add(&t,&A2,&Ai[3]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[4],&u); + ge_add(&t,&A2,&Ai[4]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[5],&u); + ge_add(&t,&A2,&Ai[5]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[6],&u); + ge_add(&t,&A2,&Ai[6]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[7],&u); + + ge_p2_0(r); + + for (i = 255;i >= 0;--i) { + if (aslide[i] || bslide[i]) break; + } + + for (;i >= 0;--i) { + ge_p2_dbl(&t,r); + + if (aslide[i] > 0) { + ge_p1p1_to_p3(&u,&t); + ge_add(&t,&u,&Ai[aslide[i]/2]); + } else if (aslide[i] < 0) { + ge_p1p1_to_p3(&u,&t); + ge_sub(&t,&u,&Ai[(-aslide[i])/2]); + } + + if (bslide[i] > 0) { + ge_p1p1_to_p3(&u,&t); + ge_madd(&t,&u,&Bi[bslide[i]/2]); + } else if (bslide[i] < 0) { + ge_p1p1_to_p3(&u,&t); + ge_msub(&t,&u,&Bi[(-bslide[i])/2]); + } + + ge_p1p1_to_p2(r,&t); + } +} diff --git a/jni/ed25519/ge_frombytes.c b/jni/ed25519/ge_frombytes.c new file mode 100644 index 000000000..1a059ee93 --- /dev/null +++ b/jni/ed25519/ge_frombytes.c @@ -0,0 +1,50 @@ +#include "ge.h" + +static const fe d = { +#include "d.h" +} ; + +static const fe sqrtm1 = { +#include "sqrtm1.h" +} ; + +int ge_frombytes_negate_vartime(ge_p3 *h,const unsigned char *s) +{ + fe u; + fe v; + fe v3; + fe vxx; + fe check; + + fe_frombytes(h->Y,s); + fe_1(h->Z); + fe_sq(u,h->Y); + fe_mul(v,u,d); + fe_sub(u,u,h->Z); /* u = y^2-1 */ + fe_add(v,v,h->Z); /* v = dy^2+1 */ + + fe_sq(v3,v); + fe_mul(v3,v3,v); /* v3 = v^3 */ + fe_sq(h->X,v3); + fe_mul(h->X,h->X,v); + fe_mul(h->X,h->X,u); /* x = uv^7 */ + + fe_pow22523(h->X,h->X); /* x = (uv^7)^((q-5)/8) */ + fe_mul(h->X,h->X,v3); + fe_mul(h->X,h->X,u); /* x = uv^3(uv^7)^((q-5)/8) */ + + fe_sq(vxx,h->X); + fe_mul(vxx,vxx,v); + fe_sub(check,vxx,u); /* vx^2-u */ + if (fe_isnonzero(check)) { + fe_add(check,vxx,u); /* vx^2+u */ + if (fe_isnonzero(check)) return -1; + fe_mul(h->X,h->X,sqrtm1); + } + + if (fe_isnegative(h->X) == (s[31] >> 7)) + fe_neg(h->X,h->X); + + fe_mul(h->T,h->X,h->Y); + return 0; +} diff --git a/jni/ed25519/ge_madd.c b/jni/ed25519/ge_madd.c new file mode 100644 index 000000000..622571774 --- /dev/null +++ b/jni/ed25519/ge_madd.c @@ -0,0 +1,11 @@ +#include "ge.h" + +/* +r = p + q +*/ + +void ge_madd(ge_p1p1 *r,const ge_p3 *p,const ge_precomp *q) +{ + fe t0; +#include "ge_madd.h" +} diff --git a/jni/ed25519/ge_madd.h b/jni/ed25519/ge_madd.h new file mode 100644 index 000000000..ecae84952 --- /dev/null +++ b/jni/ed25519/ge_madd.h @@ -0,0 +1,88 @@ + +/* qhasm: enter ge_madd */ + +/* qhasm: fe X1 */ + +/* qhasm: fe Y1 */ + +/* qhasm: fe Z1 */ + +/* qhasm: fe T1 */ + +/* qhasm: fe ypx2 */ + +/* qhasm: fe ymx2 */ + +/* qhasm: fe xy2d2 */ + +/* qhasm: fe X3 */ + +/* qhasm: fe Y3 */ + +/* qhasm: fe Z3 */ + +/* qhasm: fe T3 */ + +/* qhasm: fe YpX1 */ + +/* qhasm: fe YmX1 */ + +/* qhasm: fe A */ + +/* qhasm: fe B */ + +/* qhasm: fe C */ + +/* qhasm: fe D */ + +/* qhasm: YpX1 = Y1+X1 */ +/* asm 1: fe_add(>YpX1=fe#1,YpX1=r->X,Y,X); */ +fe_add(r->X,p->Y,p->X); + +/* qhasm: YmX1 = Y1-X1 */ +/* asm 1: fe_sub(>YmX1=fe#2,YmX1=r->Y,Y,X); */ +fe_sub(r->Y,p->Y,p->X); + +/* qhasm: A = YpX1*ypx2 */ +/* asm 1: fe_mul(>A=fe#3,A=r->Z,X,yplusx); */ +fe_mul(r->Z,r->X,q->yplusx); + +/* qhasm: B = YmX1*ymx2 */ +/* asm 1: fe_mul(>B=fe#2,B=r->Y,Y,yminusx); */ +fe_mul(r->Y,r->Y,q->yminusx); + +/* qhasm: C = xy2d2*T1 */ +/* asm 1: fe_mul(>C=fe#4,C=r->T,xy2d,T); */ +fe_mul(r->T,q->xy2d,p->T); + +/* qhasm: D = 2*Z1 */ +/* asm 1: fe_add(>D=fe#5,D=t0,Z,Z); */ +fe_add(t0,p->Z,p->Z); + +/* qhasm: X3 = A-B */ +/* asm 1: fe_sub(>X3=fe#1,X3=r->X,Z,Y); */ +fe_sub(r->X,r->Z,r->Y); + +/* qhasm: Y3 = A+B */ +/* asm 1: fe_add(>Y3=fe#2,Y3=r->Y,Z,Y); */ +fe_add(r->Y,r->Z,r->Y); + +/* qhasm: Z3 = D+C */ +/* asm 1: fe_add(>Z3=fe#3,Z3=r->Z,T); */ +fe_add(r->Z,t0,r->T); + +/* qhasm: T3 = D-C */ +/* asm 1: fe_sub(>T3=fe#4,T3=r->T,T); */ +fe_sub(r->T,t0,r->T); + +/* qhasm: return */ diff --git a/jni/ed25519/ge_msub.c b/jni/ed25519/ge_msub.c new file mode 100644 index 000000000..741ecbf11 --- /dev/null +++ b/jni/ed25519/ge_msub.c @@ -0,0 +1,11 @@ +#include "ge.h" + +/* +r = p - q +*/ + +void ge_msub(ge_p1p1 *r,const ge_p3 *p,const ge_precomp *q) +{ + fe t0; +#include "ge_msub.h" +} diff --git a/jni/ed25519/ge_msub.h b/jni/ed25519/ge_msub.h new file mode 100644 index 000000000..500f986ba --- /dev/null +++ b/jni/ed25519/ge_msub.h @@ -0,0 +1,88 @@ + +/* qhasm: enter ge_msub */ + +/* qhasm: fe X1 */ + +/* qhasm: fe Y1 */ + +/* qhasm: fe Z1 */ + +/* qhasm: fe T1 */ + +/* qhasm: fe ypx2 */ + +/* qhasm: fe ymx2 */ + +/* qhasm: fe xy2d2 */ + +/* qhasm: fe X3 */ + +/* qhasm: fe Y3 */ + +/* qhasm: fe Z3 */ + +/* qhasm: fe T3 */ + +/* qhasm: fe YpX1 */ + +/* qhasm: fe YmX1 */ + +/* qhasm: fe A */ + +/* qhasm: fe B */ + +/* qhasm: fe C */ + +/* qhasm: fe D */ + +/* qhasm: YpX1 = Y1+X1 */ +/* asm 1: fe_add(>YpX1=fe#1,YpX1=r->X,Y,X); */ +fe_add(r->X,p->Y,p->X); + +/* qhasm: YmX1 = Y1-X1 */ +/* asm 1: fe_sub(>YmX1=fe#2,YmX1=r->Y,Y,X); */ +fe_sub(r->Y,p->Y,p->X); + +/* qhasm: A = YpX1*ymx2 */ +/* asm 1: fe_mul(>A=fe#3,A=r->Z,X,yminusx); */ +fe_mul(r->Z,r->X,q->yminusx); + +/* qhasm: B = YmX1*ypx2 */ +/* asm 1: fe_mul(>B=fe#2,B=r->Y,Y,yplusx); */ +fe_mul(r->Y,r->Y,q->yplusx); + +/* qhasm: C = xy2d2*T1 */ +/* asm 1: fe_mul(>C=fe#4,C=r->T,xy2d,T); */ +fe_mul(r->T,q->xy2d,p->T); + +/* qhasm: D = 2*Z1 */ +/* asm 1: fe_add(>D=fe#5,D=t0,Z,Z); */ +fe_add(t0,p->Z,p->Z); + +/* qhasm: X3 = A-B */ +/* asm 1: fe_sub(>X3=fe#1,X3=r->X,Z,Y); */ +fe_sub(r->X,r->Z,r->Y); + +/* qhasm: Y3 = A+B */ +/* asm 1: fe_add(>Y3=fe#2,Y3=r->Y,Z,Y); */ +fe_add(r->Y,r->Z,r->Y); + +/* qhasm: Z3 = D-C */ +/* asm 1: fe_sub(>Z3=fe#3,Z3=r->Z,T); */ +fe_sub(r->Z,t0,r->T); + +/* qhasm: T3 = D+C */ +/* asm 1: fe_add(>T3=fe#4,T3=r->T,T); */ +fe_add(r->T,t0,r->T); + +/* qhasm: return */ diff --git a/jni/ed25519/ge_p1p1_to_p2.c b/jni/ed25519/ge_p1p1_to_p2.c new file mode 100644 index 000000000..9bb5013d6 --- /dev/null +++ b/jni/ed25519/ge_p1p1_to_p2.c @@ -0,0 +1,12 @@ +#include "ge.h" + +/* +r = p +*/ + +extern void ge_p1p1_to_p2(ge_p2 *r,const ge_p1p1 *p) +{ + fe_mul(r->X,p->X,p->T); + fe_mul(r->Y,p->Y,p->Z); + fe_mul(r->Z,p->Z,p->T); +} diff --git a/jni/ed25519/ge_p1p1_to_p3.c b/jni/ed25519/ge_p1p1_to_p3.c new file mode 100644 index 000000000..2f57b1096 --- /dev/null +++ b/jni/ed25519/ge_p1p1_to_p3.c @@ -0,0 +1,13 @@ +#include "ge.h" + +/* +r = p +*/ + +extern void ge_p1p1_to_p3(ge_p3 *r,const ge_p1p1 *p) +{ + fe_mul(r->X,p->X,p->T); + fe_mul(r->Y,p->Y,p->Z); + fe_mul(r->Z,p->Z,p->T); + fe_mul(r->T,p->X,p->Y); +} diff --git a/jni/ed25519/ge_p2_0.c b/jni/ed25519/ge_p2_0.c new file mode 100644 index 000000000..6191d1e6e --- /dev/null +++ b/jni/ed25519/ge_p2_0.c @@ -0,0 +1,8 @@ +#include "ge.h" + +void ge_p2_0(ge_p2 *h) +{ + fe_0(h->X); + fe_1(h->Y); + fe_1(h->Z); +} diff --git a/jni/ed25519/ge_p2_dbl.c b/jni/ed25519/ge_p2_dbl.c new file mode 100644 index 000000000..2e332b5ce --- /dev/null +++ b/jni/ed25519/ge_p2_dbl.c @@ -0,0 +1,11 @@ +#include "ge.h" + +/* +r = 2 * p +*/ + +void ge_p2_dbl(ge_p1p1 *r,const ge_p2 *p) +{ + fe t0; +#include "ge_p2_dbl.h" +} diff --git a/jni/ed25519/ge_p2_dbl.h b/jni/ed25519/ge_p2_dbl.h new file mode 100644 index 000000000..128efed90 --- /dev/null +++ b/jni/ed25519/ge_p2_dbl.h @@ -0,0 +1,73 @@ + +/* qhasm: enter ge_p2_dbl */ + +/* qhasm: fe X1 */ + +/* qhasm: fe Y1 */ + +/* qhasm: fe Z1 */ + +/* qhasm: fe A */ + +/* qhasm: fe AA */ + +/* qhasm: fe XX */ + +/* qhasm: fe YY */ + +/* qhasm: fe B */ + +/* qhasm: fe X3 */ + +/* qhasm: fe Y3 */ + +/* qhasm: fe Z3 */ + +/* qhasm: fe T3 */ + +/* qhasm: XX=X1^2 */ +/* asm 1: fe_sq(>XX=fe#1,XX=r->X,X); */ +fe_sq(r->X,p->X); + +/* qhasm: YY=Y1^2 */ +/* asm 1: fe_sq(>YY=fe#3,YY=r->Z,Y); */ +fe_sq(r->Z,p->Y); + +/* qhasm: B=2*Z1^2 */ +/* asm 1: fe_sq2(>B=fe#4,B=r->T,Z); */ +fe_sq2(r->T,p->Z); + +/* qhasm: A=X1+Y1 */ +/* asm 1: fe_add(>A=fe#2,A=r->Y,X,Y); */ +fe_add(r->Y,p->X,p->Y); + +/* qhasm: AA=A^2 */ +/* asm 1: fe_sq(>AA=fe#5,AA=t0,Y); */ +fe_sq(t0,r->Y); + +/* qhasm: Y3=YY+XX */ +/* asm 1: fe_add(>Y3=fe#2,Y3=r->Y,Z,X); */ +fe_add(r->Y,r->Z,r->X); + +/* qhasm: Z3=YY-XX */ +/* asm 1: fe_sub(>Z3=fe#3,Z3=r->Z,Z,X); */ +fe_sub(r->Z,r->Z,r->X); + +/* qhasm: X3=AA-Y3 */ +/* asm 1: fe_sub(>X3=fe#1,X3=r->X,Y); */ +fe_sub(r->X,t0,r->Y); + +/* qhasm: T3=B-Z3 */ +/* asm 1: fe_sub(>T3=fe#4,T3=r->T,T,Z); */ +fe_sub(r->T,r->T,r->Z); + +/* qhasm: return */ diff --git a/jni/ed25519/ge_p3_0.c b/jni/ed25519/ge_p3_0.c new file mode 100644 index 000000000..401b2935a --- /dev/null +++ b/jni/ed25519/ge_p3_0.c @@ -0,0 +1,9 @@ +#include "ge.h" + +void ge_p3_0(ge_p3 *h) +{ + fe_0(h->X); + fe_1(h->Y); + fe_1(h->Z); + fe_0(h->T); +} diff --git a/jni/ed25519/ge_p3_dbl.c b/jni/ed25519/ge_p3_dbl.c new file mode 100644 index 000000000..0d8a05915 --- /dev/null +++ b/jni/ed25519/ge_p3_dbl.c @@ -0,0 +1,12 @@ +#include "ge.h" + +/* +r = 2 * p +*/ + +void ge_p3_dbl(ge_p1p1 *r,const ge_p3 *p) +{ + ge_p2 q; + ge_p3_to_p2(&q,p); + ge_p2_dbl(r,&q); +} diff --git a/jni/ed25519/ge_p3_to_cached.c b/jni/ed25519/ge_p3_to_cached.c new file mode 100644 index 000000000..bde64228c --- /dev/null +++ b/jni/ed25519/ge_p3_to_cached.c @@ -0,0 +1,17 @@ +#include "ge.h" + +/* +r = p +*/ + +static const fe d2 = { +#include "d2.h" +} ; + +extern void ge_p3_to_cached(ge_cached *r,const ge_p3 *p) +{ + fe_add(r->YplusX,p->Y,p->X); + fe_sub(r->YminusX,p->Y,p->X); + fe_copy(r->Z,p->Z); + fe_mul(r->T2d,p->T,d2); +} diff --git a/jni/ed25519/ge_p3_to_p2.c b/jni/ed25519/ge_p3_to_p2.c new file mode 100644 index 000000000..e532a9e4c --- /dev/null +++ b/jni/ed25519/ge_p3_to_p2.c @@ -0,0 +1,12 @@ +#include "ge.h" + +/* +r = p +*/ + +extern void ge_p3_to_p2(ge_p2 *r,const ge_p3 *p) +{ + fe_copy(r->X,p->X); + fe_copy(r->Y,p->Y); + fe_copy(r->Z,p->Z); +} diff --git a/jni/ed25519/ge_p3_tobytes.c b/jni/ed25519/ge_p3_tobytes.c new file mode 100644 index 000000000..21cb2fc65 --- /dev/null +++ b/jni/ed25519/ge_p3_tobytes.c @@ -0,0 +1,14 @@ +#include "ge.h" + +void ge_p3_tobytes(unsigned char *s,const ge_p3 *h) +{ + fe recip; + fe x; + fe y; + + fe_invert(recip,h->Z); + fe_mul(x,h->X,recip); + fe_mul(y,h->Y,recip); + fe_tobytes(s,y); + s[31] ^= fe_isnegative(x) << 7; +} diff --git a/jni/ed25519/ge_precomp_0.c b/jni/ed25519/ge_precomp_0.c new file mode 100644 index 000000000..2e218861d --- /dev/null +++ b/jni/ed25519/ge_precomp_0.c @@ -0,0 +1,8 @@ +#include "ge.h" + +void ge_precomp_0(ge_precomp *h) +{ + fe_1(h->yplusx); + fe_1(h->yminusx); + fe_0(h->xy2d); +} diff --git a/jni/ed25519/ge_scalarmult_base.c b/jni/ed25519/ge_scalarmult_base.c new file mode 100644 index 000000000..421e4fa0f --- /dev/null +++ b/jni/ed25519/ge_scalarmult_base.c @@ -0,0 +1,105 @@ +#include "ge.h" +#include "crypto_uint32.h" + +static unsigned char equal(signed char b,signed char c) +{ + unsigned char ub = b; + unsigned char uc = c; + unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */ + crypto_uint32 y = x; /* 0: yes; 1..255: no */ + y -= 1; /* 4294967295: yes; 0..254: no */ + y >>= 31; /* 1: yes; 0: no */ + return y; +} + +static unsigned char negative(signed char b) +{ + unsigned long long x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */ + x >>= 63; /* 1: yes; 0: no */ + return x; +} + +static void cmov(ge_precomp *t,ge_precomp *u,unsigned char b) +{ + fe_cmov(t->yplusx,u->yplusx,b); + fe_cmov(t->yminusx,u->yminusx,b); + fe_cmov(t->xy2d,u->xy2d,b); +} + +/* base[i][j] = (j+1)*256^i*B */ +static ge_precomp base[32][8] = { +#include "base.h" +} ; + +static void select(ge_precomp *t,int pos,signed char b) +{ + ge_precomp minust; + unsigned char bnegative = negative(b); + unsigned char babs = b - (((-bnegative) & b) << 1); + + ge_precomp_0(t); + cmov(t,&base[pos][0],equal(babs,1)); + cmov(t,&base[pos][1],equal(babs,2)); + cmov(t,&base[pos][2],equal(babs,3)); + cmov(t,&base[pos][3],equal(babs,4)); + cmov(t,&base[pos][4],equal(babs,5)); + cmov(t,&base[pos][5],equal(babs,6)); + cmov(t,&base[pos][6],equal(babs,7)); + cmov(t,&base[pos][7],equal(babs,8)); + fe_copy(minust.yplusx,t->yminusx); + fe_copy(minust.yminusx,t->yplusx); + fe_neg(minust.xy2d,t->xy2d); + cmov(t,&minust,bnegative); +} + +/* +h = a * B +where a = a[0]+256*a[1]+...+256^31 a[31] +B is the Ed25519 base point (x,4/5) with x positive. + +Preconditions: + a[31] <= 127 +*/ + +void ge_scalarmult_base(ge_p3 *h,const unsigned char *a) +{ + signed char e[64]; + signed char carry; + ge_p1p1 r; + ge_p2 s; + ge_precomp t; + int i; + + for (i = 0;i < 32;++i) { + e[2 * i + 0] = (a[i] >> 0) & 15; + e[2 * i + 1] = (a[i] >> 4) & 15; + } + /* each e[i] is between 0 and 15 */ + /* e[63] is between 0 and 7 */ + + carry = 0; + for (i = 0;i < 63;++i) { + e[i] += carry; + carry = e[i] + 8; + carry >>= 4; + e[i] -= carry << 4; + } + e[63] += carry; + /* each e[i] is between -8 and 8 */ + + ge_p3_0(h); + for (i = 1;i < 64;i += 2) { + select(&t,i / 2,e[i]); + ge_madd(&r,h,&t); ge_p1p1_to_p3(h,&r); + } + + ge_p3_dbl(&r,h); ge_p1p1_to_p2(&s,&r); + ge_p2_dbl(&r,&s); ge_p1p1_to_p2(&s,&r); + ge_p2_dbl(&r,&s); ge_p1p1_to_p2(&s,&r); + ge_p2_dbl(&r,&s); ge_p1p1_to_p3(h,&r); + + for (i = 0;i < 64;i += 2) { + select(&t,i / 2,e[i]); + ge_madd(&r,h,&t); ge_p1p1_to_p3(h,&r); + } +} diff --git a/jni/ed25519/ge_sub.c b/jni/ed25519/ge_sub.c new file mode 100644 index 000000000..69f3d5406 --- /dev/null +++ b/jni/ed25519/ge_sub.c @@ -0,0 +1,11 @@ +#include "ge.h" + +/* +r = p - q +*/ + +void ge_sub(ge_p1p1 *r,const ge_p3 *p,const ge_cached *q) +{ + fe t0; +#include "ge_sub.h" +} diff --git a/jni/ed25519/ge_sub.h b/jni/ed25519/ge_sub.h new file mode 100644 index 000000000..b4ef1f5dd --- /dev/null +++ b/jni/ed25519/ge_sub.h @@ -0,0 +1,97 @@ + +/* qhasm: enter ge_sub */ + +/* qhasm: fe X1 */ + +/* qhasm: fe Y1 */ + +/* qhasm: fe Z1 */ + +/* qhasm: fe Z2 */ + +/* qhasm: fe T1 */ + +/* qhasm: fe ZZ */ + +/* qhasm: fe YpX2 */ + +/* qhasm: fe YmX2 */ + +/* qhasm: fe T2d2 */ + +/* qhasm: fe X3 */ + +/* qhasm: fe Y3 */ + +/* qhasm: fe Z3 */ + +/* qhasm: fe T3 */ + +/* qhasm: fe YpX1 */ + +/* qhasm: fe YmX1 */ + +/* qhasm: fe A */ + +/* qhasm: fe B */ + +/* qhasm: fe C */ + +/* qhasm: fe D */ + +/* qhasm: YpX1 = Y1+X1 */ +/* asm 1: fe_add(>YpX1=fe#1,YpX1=r->X,Y,X); */ +fe_add(r->X,p->Y,p->X); + +/* qhasm: YmX1 = Y1-X1 */ +/* asm 1: fe_sub(>YmX1=fe#2,YmX1=r->Y,Y,X); */ +fe_sub(r->Y,p->Y,p->X); + +/* qhasm: A = YpX1*YmX2 */ +/* asm 1: fe_mul(>A=fe#3,A=r->Z,X,YminusX); */ +fe_mul(r->Z,r->X,q->YminusX); + +/* qhasm: B = YmX1*YpX2 */ +/* asm 1: fe_mul(>B=fe#2,B=r->Y,Y,YplusX); */ +fe_mul(r->Y,r->Y,q->YplusX); + +/* qhasm: C = T2d2*T1 */ +/* asm 1: fe_mul(>C=fe#4,C=r->T,T2d,T); */ +fe_mul(r->T,q->T2d,p->T); + +/* qhasm: ZZ = Z1*Z2 */ +/* asm 1: fe_mul(>ZZ=fe#1,ZZ=r->X,Z,Z); */ +fe_mul(r->X,p->Z,q->Z); + +/* qhasm: D = 2*ZZ */ +/* asm 1: fe_add(>D=fe#5,D=t0,X,X); */ +fe_add(t0,r->X,r->X); + +/* qhasm: X3 = A-B */ +/* asm 1: fe_sub(>X3=fe#1,X3=r->X,Z,Y); */ +fe_sub(r->X,r->Z,r->Y); + +/* qhasm: Y3 = A+B */ +/* asm 1: fe_add(>Y3=fe#2,Y3=r->Y,Z,Y); */ +fe_add(r->Y,r->Z,r->Y); + +/* qhasm: Z3 = D-C */ +/* asm 1: fe_sub(>Z3=fe#3,Z3=r->Z,T); */ +fe_sub(r->Z,t0,r->T); + +/* qhasm: T3 = D+C */ +/* asm 1: fe_add(>T3=fe#4,T3=r->T,T); */ +fe_add(r->T,t0,r->T); + +/* qhasm: return */ diff --git a/jni/ed25519/ge_tobytes.c b/jni/ed25519/ge_tobytes.c new file mode 100644 index 000000000..31b3d33e0 --- /dev/null +++ b/jni/ed25519/ge_tobytes.c @@ -0,0 +1,14 @@ +#include "ge.h" + +void ge_tobytes(unsigned char *s,const ge_p2 *h) +{ + fe recip; + fe x; + fe y; + + fe_invert(recip,h->Z); + fe_mul(x,h->X,recip); + fe_mul(y,h->Y,recip); + fe_tobytes(s,y); + s[31] ^= fe_isnegative(x) << 7; +} diff --git a/jni/ed25519/main/main.c b/jni/ed25519/main/main.c new file mode 100644 index 000000000..5fbe39956 --- /dev/null +++ b/jni/ed25519/main/main.c @@ -0,0 +1,106 @@ +#include +#include +#include "crypto_hash_sha512.h" +#include "curve_sigs.h" + +#define MSG_LEN 200 + +int main(int argc, char* argv[]) +{ + unsigned char privkey[32]; + unsigned char pubkey[32]; + unsigned char signature[64]; + unsigned char msg[MSG_LEN]; + unsigned char random[64]; + + /* Initialize pubkey, privkey, msg */ + memset(msg, 0, MSG_LEN); + memset(privkey, 0, 32); + memset(pubkey, 0, 32); + privkey[0] &= 248; + privkey[31] &= 63; + privkey[31] |= 64; + + privkey[8] = 189; /* just so there's some bits set */ + + + /* SHA512 test */ + unsigned char sha512_input[112] = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"; + unsigned char sha512_correct_output[64] = +{ +0x8E, 0x95, 0x9B, 0x75, 0xDA, 0xE3, 0x13, 0xDA, +0x8C, 0xF4, 0xF7, 0x28, 0x14, 0xFC, 0x14, 0x3F, +0x8F, 0x77, 0x79, 0xC6, 0xEB, 0x9F, 0x7F, 0xA1, +0x72, 0x99, 0xAE, 0xAD, 0xB6, 0x88, 0x90, 0x18, +0x50, 0x1D, 0x28, 0x9E, 0x49, 0x00, 0xF7, 0xE4, +0x33, 0x1B, 0x99, 0xDE, 0xC4, 0xB5, 0x43, 0x3A, +0xC7, 0xD3, 0x29, 0xEE, 0xB6, 0xDD, 0x26, 0x54, +0x5E, 0x96, 0xE5, 0x5B, 0x87, 0x4B, 0xE9, 0x09 +}; + unsigned char sha512_actual_output[64]; + + crypto_hash_sha512(sha512_actual_output, sha512_input, sizeof(sha512_input)); + if (memcmp(sha512_actual_output, sha512_correct_output, 64) != 0) + printf("SHA512 bad #1\n"); + else + printf("SHA512 good #1\n"); + + sha512_input[111] ^= 1; + + crypto_hash_sha512(sha512_actual_output, sha512_input, sizeof(sha512_input)); + if (memcmp(sha512_actual_output, sha512_correct_output, 64) != 0) + printf("SHA512 good #2\n"); + else + printf("SHA512 bad #2\n"); + + /* Signature test */ + curve25519_keygen(pubkey, privkey); + + curve25519_sign(signature, privkey, msg, MSG_LEN, random); + + if (curve25519_verify(signature, pubkey, msg, MSG_LEN) == 0) + printf("Signature good #1\n"); + else + printf("Signature bad #1\n"); + + signature[0] ^= 1; + + if (curve25519_verify(signature, pubkey, msg, MSG_LEN) == 0) + printf("Signature bad #2\n"); + else + printf("Signature good #2\n"); + + + printf("Random testing...\n"); + for (int count = 0; count < 10000; count++) { + unsigned char b[64]; + crypto_hash_sha512(b, privkey, 32); + memmove(privkey, b, 32); + crypto_hash_sha512(b, privkey, 32); + memmove(random, b, 64); + + privkey[0] &= 248; + privkey[31] &= 63; + privkey[31] |= 64; + + curve25519_keygen(pubkey, privkey); + + curve25519_sign(signature, privkey, msg, MSG_LEN, random); + + if (curve25519_verify(signature, pubkey, msg, MSG_LEN) != 0) { + printf("failure #1 %d\n", count); + return -1; + } + + if (b[63] & 1) + signature[count % 64] ^= 1; + else + msg[count % MSG_LEN] ^= 1; + if (curve25519_verify(signature, pubkey, msg, MSG_LEN) == 0) { + printf("failure #2 %d\n", count); + return -1; + } + } + printf("OK\n"); + return 1; +} diff --git a/jni/ed25519/nacl_includes/crypto_int32.h b/jni/ed25519/nacl_includes/crypto_int32.h new file mode 100644 index 000000000..cae135e6e --- /dev/null +++ b/jni/ed25519/nacl_includes/crypto_int32.h @@ -0,0 +1,6 @@ +#ifndef crypto_int32_h +#define crypto_int32_h + +typedef int crypto_int32; + +#endif diff --git a/jni/ed25519/nacl_includes/crypto_int64.h b/jni/ed25519/nacl_includes/crypto_int64.h new file mode 100644 index 000000000..fc92417b6 --- /dev/null +++ b/jni/ed25519/nacl_includes/crypto_int64.h @@ -0,0 +1,6 @@ +#ifndef crypto_int64_h +#define crypto_int64_h + +typedef long long crypto_int64; + +#endif diff --git a/jni/ed25519/nacl_includes/crypto_sign.h b/jni/ed25519/nacl_includes/crypto_sign.h new file mode 100644 index 000000000..8472603e9 --- /dev/null +++ b/jni/ed25519/nacl_includes/crypto_sign.h @@ -0,0 +1,16 @@ +#ifndef crypto_sign_H +#define crypto_sign_H + +#include "crypto_sign_edwards25519sha512batch.h" + +#define crypto_sign crypto_sign_edwards25519sha512batch +#define crypto_sign_open crypto_sign_edwards25519sha512batch_open +#define crypto_sign_keypair crypto_sign_edwards25519sha512batch_keypair +#define crypto_sign_BYTES crypto_sign_edwards25519sha512batch_BYTES +#define crypto_sign_PUBLICKEYBYTES crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES +#define crypto_sign_SECRETKEYBYTES crypto_sign_edwards25519sha512batch_SECRETKEYBYTES +#define crypto_sign_PRIMITIVE "edwards25519sha512batch" +#define crypto_sign_IMPLEMENTATION crypto_sign_edwards25519sha512batch_IMPLEMENTATION +#define crypto_sign_VERSION crypto_sign_edwards25519sha512batch_VERSION + +#endif diff --git a/jni/ed25519/nacl_includes/crypto_sign_edwards25519sha512batch.h b/jni/ed25519/nacl_includes/crypto_sign_edwards25519sha512batch.h new file mode 100644 index 000000000..62fae6116 --- /dev/null +++ b/jni/ed25519/nacl_includes/crypto_sign_edwards25519sha512batch.h @@ -0,0 +1,33 @@ +#ifndef crypto_sign_edwards25519sha512batch_H +#define crypto_sign_edwards25519sha512batch_H + +#define crypto_sign_edwards25519sha512batch_ref10_SECRETKEYBYTES 64 +#define crypto_sign_edwards25519sha512batch_ref10_PUBLICKEYBYTES 32 +#define crypto_sign_edwards25519sha512batch_ref10_BYTES 64 +#ifdef __cplusplus +#include +extern std::string crypto_sign_edwards25519sha512batch_ref10(const std::string &,const std::string &); +extern std::string crypto_sign_edwards25519sha512batch_ref10_open(const std::string &,const std::string &); +extern std::string crypto_sign_edwards25519sha512batch_ref10_keypair(std::string *); +extern "C" { +#endif +extern int crypto_sign_edwards25519sha512batch_ref10(unsigned char *,unsigned long long *,const unsigned char *,unsigned long long,const unsigned char *); +extern int crypto_sign_edwards25519sha512batch_ref10_open(unsigned char *,unsigned long long *,const unsigned char *,unsigned long long,const unsigned char *); +extern int crypto_sign_edwards25519sha512batch_ref10_keypair(unsigned char *,unsigned char *); +#ifdef __cplusplus +} +#endif + +#define crypto_sign_edwards25519sha512batch crypto_sign_edwards25519sha512batch_ref10 +#define crypto_sign_edwards25519sha512batch_open crypto_sign_edwards25519sha512batch_ref10_open +#define crypto_sign_edwards25519sha512batch_keypair crypto_sign_edwards25519sha512batch_ref10_keypair +#define crypto_sign_edwards25519sha512batch_BYTES crypto_sign_edwards25519sha512batch_ref10_BYTES +#define crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES crypto_sign_edwards25519sha512batch_ref10_PUBLICKEYBYTES +#define crypto_sign_edwards25519sha512batch_SECRETKEYBYTES crypto_sign_edwards25519sha512batch_ref10_SECRETKEYBYTES +#define crypto_sign_edwards25519sha512batch_IMPLEMENTATION "crypto_sign/edwards25519sha512batch/ref10" +#ifndef crypto_sign_edwards25519sha512batch_ref10_VERSION +#define crypto_sign_edwards25519sha512batch_ref10_VERSION "-" +#endif +#define crypto_sign_edwards25519sha512batch_VERSION crypto_sign_edwards25519sha512batch_ref10_VERSION + +#endif diff --git a/jni/ed25519/nacl_includes/crypto_uint32.h b/jni/ed25519/nacl_includes/crypto_uint32.h new file mode 100644 index 000000000..21020d7b5 --- /dev/null +++ b/jni/ed25519/nacl_includes/crypto_uint32.h @@ -0,0 +1,6 @@ +#ifndef crypto_uint32_h +#define crypto_uint32_h + +typedef unsigned int crypto_uint32; + +#endif diff --git a/jni/ed25519/nacl_includes/crypto_uint64.h b/jni/ed25519/nacl_includes/crypto_uint64.h new file mode 100644 index 000000000..5aa007037 --- /dev/null +++ b/jni/ed25519/nacl_includes/crypto_uint64.h @@ -0,0 +1,6 @@ +#ifndef crypto_uint64_h +#define crypto_uint64_h + +typedef unsigned long long crypto_uint64; + +#endif diff --git a/jni/ed25519/nacl_includes/crypto_verify_32.h b/jni/ed25519/nacl_includes/crypto_verify_32.h new file mode 100644 index 000000000..978d84522 --- /dev/null +++ b/jni/ed25519/nacl_includes/crypto_verify_32.h @@ -0,0 +1,22 @@ +#ifndef crypto_verify_32_H +#define crypto_verify_32_H + +#define crypto_verify_32_ref_BYTES 32 +#ifdef __cplusplus +#include +extern "C" { +#endif +extern int crypto_verify_32_ref(const unsigned char *,const unsigned char *); +#ifdef __cplusplus +} +#endif + +#define crypto_verify_32 crypto_verify_32_ref +#define crypto_verify_32_BYTES crypto_verify_32_ref_BYTES +#define crypto_verify_32_IMPLEMENTATION "crypto_verify/32/ref" +#ifndef crypto_verify_32_ref_VERSION +#define crypto_verify_32_ref_VERSION "-" +#endif +#define crypto_verify_32_VERSION crypto_verify_32_ref_VERSION + +#endif diff --git a/jni/ed25519/nacl_sha512/blocks.c b/jni/ed25519/nacl_sha512/blocks.c new file mode 100644 index 000000000..1c5f23d5c --- /dev/null +++ b/jni/ed25519/nacl_sha512/blocks.c @@ -0,0 +1,238 @@ +#include +typedef uint64_t uint64; + +static uint64 load_bigendian(const unsigned char *x) +{ + return + (uint64) (x[7]) \ + | (((uint64) (x[6])) << 8) \ + | (((uint64) (x[5])) << 16) \ + | (((uint64) (x[4])) << 24) \ + | (((uint64) (x[3])) << 32) \ + | (((uint64) (x[2])) << 40) \ + | (((uint64) (x[1])) << 48) \ + | (((uint64) (x[0])) << 56) + ; +} + +static void store_bigendian(unsigned char *x,uint64 u) +{ + x[7] = u; u >>= 8; + x[6] = u; u >>= 8; + x[5] = u; u >>= 8; + x[4] = u; u >>= 8; + x[3] = u; u >>= 8; + x[2] = u; u >>= 8; + x[1] = u; u >>= 8; + x[0] = u; +} + +#define SHR(x,c) ((x) >> (c)) +#define ROTR(x,c) (((x) >> (c)) | ((x) << (64 - (c)))) + +#define Ch(x,y,z) ((x & y) ^ (~x & z)) +#define Maj(x,y,z) ((x & y) ^ (x & z) ^ (y & z)) +#define Sigma0(x) (ROTR(x,28) ^ ROTR(x,34) ^ ROTR(x,39)) +#define Sigma1(x) (ROTR(x,14) ^ ROTR(x,18) ^ ROTR(x,41)) +#define sigma0(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ SHR(x,7)) +#define sigma1(x) (ROTR(x,19) ^ ROTR(x,61) ^ SHR(x,6)) + +#define M(w0,w14,w9,w1) w0 = sigma1(w14) + w9 + sigma0(w1) + w0; + +#define EXPAND \ + M(w0 ,w14,w9 ,w1 ) \ + M(w1 ,w15,w10,w2 ) \ + M(w2 ,w0 ,w11,w3 ) \ + M(w3 ,w1 ,w12,w4 ) \ + M(w4 ,w2 ,w13,w5 ) \ + M(w5 ,w3 ,w14,w6 ) \ + M(w6 ,w4 ,w15,w7 ) \ + M(w7 ,w5 ,w0 ,w8 ) \ + M(w8 ,w6 ,w1 ,w9 ) \ + M(w9 ,w7 ,w2 ,w10) \ + M(w10,w8 ,w3 ,w11) \ + M(w11,w9 ,w4 ,w12) \ + M(w12,w10,w5 ,w13) \ + M(w13,w11,w6 ,w14) \ + M(w14,w12,w7 ,w15) \ + M(w15,w13,w8 ,w0 ) + +#define F(w,k) \ + T1 = h + Sigma1(e) + Ch(e,f,g) + k + w; \ + T2 = Sigma0(a) + Maj(a,b,c); \ + h = g; \ + g = f; \ + f = e; \ + e = d + T1; \ + d = c; \ + c = b; \ + b = a; \ + a = T1 + T2; + +int crypto_hashblocks_sha512(unsigned char *statebytes,const unsigned char *in,unsigned long long inlen) +{ + uint64 state[8]; + uint64 a; + uint64 b; + uint64 c; + uint64 d; + uint64 e; + uint64 f; + uint64 g; + uint64 h; + uint64 T1; + uint64 T2; + + a = load_bigendian(statebytes + 0); state[0] = a; + b = load_bigendian(statebytes + 8); state[1] = b; + c = load_bigendian(statebytes + 16); state[2] = c; + d = load_bigendian(statebytes + 24); state[3] = d; + e = load_bigendian(statebytes + 32); state[4] = e; + f = load_bigendian(statebytes + 40); state[5] = f; + g = load_bigendian(statebytes + 48); state[6] = g; + h = load_bigendian(statebytes + 56); state[7] = h; + + while (inlen >= 128) { + uint64 w0 = load_bigendian(in + 0); + uint64 w1 = load_bigendian(in + 8); + uint64 w2 = load_bigendian(in + 16); + uint64 w3 = load_bigendian(in + 24); + uint64 w4 = load_bigendian(in + 32); + uint64 w5 = load_bigendian(in + 40); + uint64 w6 = load_bigendian(in + 48); + uint64 w7 = load_bigendian(in + 56); + uint64 w8 = load_bigendian(in + 64); + uint64 w9 = load_bigendian(in + 72); + uint64 w10 = load_bigendian(in + 80); + uint64 w11 = load_bigendian(in + 88); + uint64 w12 = load_bigendian(in + 96); + uint64 w13 = load_bigendian(in + 104); + uint64 w14 = load_bigendian(in + 112); + uint64 w15 = load_bigendian(in + 120); + + F(w0 ,0x428a2f98d728ae22ULL) + F(w1 ,0x7137449123ef65cdULL) + F(w2 ,0xb5c0fbcfec4d3b2fULL) + F(w3 ,0xe9b5dba58189dbbcULL) + F(w4 ,0x3956c25bf348b538ULL) + F(w5 ,0x59f111f1b605d019ULL) + F(w6 ,0x923f82a4af194f9bULL) + F(w7 ,0xab1c5ed5da6d8118ULL) + F(w8 ,0xd807aa98a3030242ULL) + F(w9 ,0x12835b0145706fbeULL) + F(w10,0x243185be4ee4b28cULL) + F(w11,0x550c7dc3d5ffb4e2ULL) + F(w12,0x72be5d74f27b896fULL) + F(w13,0x80deb1fe3b1696b1ULL) + F(w14,0x9bdc06a725c71235ULL) + F(w15,0xc19bf174cf692694ULL) + + EXPAND + + F(w0 ,0xe49b69c19ef14ad2ULL) + F(w1 ,0xefbe4786384f25e3ULL) + F(w2 ,0x0fc19dc68b8cd5b5ULL) + F(w3 ,0x240ca1cc77ac9c65ULL) + F(w4 ,0x2de92c6f592b0275ULL) + F(w5 ,0x4a7484aa6ea6e483ULL) + F(w6 ,0x5cb0a9dcbd41fbd4ULL) + F(w7 ,0x76f988da831153b5ULL) + F(w8 ,0x983e5152ee66dfabULL) + F(w9 ,0xa831c66d2db43210ULL) + F(w10,0xb00327c898fb213fULL) + F(w11,0xbf597fc7beef0ee4ULL) + F(w12,0xc6e00bf33da88fc2ULL) + F(w13,0xd5a79147930aa725ULL) + F(w14,0x06ca6351e003826fULL) + F(w15,0x142929670a0e6e70ULL) + + EXPAND + + F(w0 ,0x27b70a8546d22ffcULL) + F(w1 ,0x2e1b21385c26c926ULL) + F(w2 ,0x4d2c6dfc5ac42aedULL) + F(w3 ,0x53380d139d95b3dfULL) + F(w4 ,0x650a73548baf63deULL) + F(w5 ,0x766a0abb3c77b2a8ULL) + F(w6 ,0x81c2c92e47edaee6ULL) + F(w7 ,0x92722c851482353bULL) + F(w8 ,0xa2bfe8a14cf10364ULL) + F(w9 ,0xa81a664bbc423001ULL) + F(w10,0xc24b8b70d0f89791ULL) + F(w11,0xc76c51a30654be30ULL) + F(w12,0xd192e819d6ef5218ULL) + F(w13,0xd69906245565a910ULL) + F(w14,0xf40e35855771202aULL) + F(w15,0x106aa07032bbd1b8ULL) + + EXPAND + + F(w0 ,0x19a4c116b8d2d0c8ULL) + F(w1 ,0x1e376c085141ab53ULL) + F(w2 ,0x2748774cdf8eeb99ULL) + F(w3 ,0x34b0bcb5e19b48a8ULL) + F(w4 ,0x391c0cb3c5c95a63ULL) + F(w5 ,0x4ed8aa4ae3418acbULL) + F(w6 ,0x5b9cca4f7763e373ULL) + F(w7 ,0x682e6ff3d6b2b8a3ULL) + F(w8 ,0x748f82ee5defb2fcULL) + F(w9 ,0x78a5636f43172f60ULL) + F(w10,0x84c87814a1f0ab72ULL) + F(w11,0x8cc702081a6439ecULL) + F(w12,0x90befffa23631e28ULL) + F(w13,0xa4506cebde82bde9ULL) + F(w14,0xbef9a3f7b2c67915ULL) + F(w15,0xc67178f2e372532bULL) + + EXPAND + + F(w0 ,0xca273eceea26619cULL) + F(w1 ,0xd186b8c721c0c207ULL) + F(w2 ,0xeada7dd6cde0eb1eULL) + F(w3 ,0xf57d4f7fee6ed178ULL) + F(w4 ,0x06f067aa72176fbaULL) + F(w5 ,0x0a637dc5a2c898a6ULL) + F(w6 ,0x113f9804bef90daeULL) + F(w7 ,0x1b710b35131c471bULL) + F(w8 ,0x28db77f523047d84ULL) + F(w9 ,0x32caab7b40c72493ULL) + F(w10,0x3c9ebe0a15c9bebcULL) + F(w11,0x431d67c49c100d4cULL) + F(w12,0x4cc5d4becb3e42b6ULL) + F(w13,0x597f299cfc657e2aULL) + F(w14,0x5fcb6fab3ad6faecULL) + F(w15,0x6c44198c4a475817ULL) + + a += state[0]; + b += state[1]; + c += state[2]; + d += state[3]; + e += state[4]; + f += state[5]; + g += state[6]; + h += state[7]; + + state[0] = a; + state[1] = b; + state[2] = c; + state[3] = d; + state[4] = e; + state[5] = f; + state[6] = g; + state[7] = h; + + in += 128; + inlen -= 128; + } + + store_bigendian(statebytes + 0,state[0]); + store_bigendian(statebytes + 8,state[1]); + store_bigendian(statebytes + 16,state[2]); + store_bigendian(statebytes + 24,state[3]); + store_bigendian(statebytes + 32,state[4]); + store_bigendian(statebytes + 40,state[5]); + store_bigendian(statebytes + 48,state[6]); + store_bigendian(statebytes + 56,state[7]); + + return 0; +} diff --git a/jni/ed25519/nacl_sha512/hash.c b/jni/ed25519/nacl_sha512/hash.c new file mode 100644 index 000000000..8934cc18a --- /dev/null +++ b/jni/ed25519/nacl_sha512/hash.c @@ -0,0 +1,71 @@ +/* +20080913 +D. J. Bernstein +Public domain. +*/ + +#include +typedef uint64_t uint64; + +extern int crypto_hashblocks_sha512(unsigned char *statebytes,const unsigned char *in,unsigned long long inlen); + +#define blocks crypto_hashblocks_sha512 + +static const unsigned char iv[64] = { + 0x6a,0x09,0xe6,0x67,0xf3,0xbc,0xc9,0x08, + 0xbb,0x67,0xae,0x85,0x84,0xca,0xa7,0x3b, + 0x3c,0x6e,0xf3,0x72,0xfe,0x94,0xf8,0x2b, + 0xa5,0x4f,0xf5,0x3a,0x5f,0x1d,0x36,0xf1, + 0x51,0x0e,0x52,0x7f,0xad,0xe6,0x82,0xd1, + 0x9b,0x05,0x68,0x8c,0x2b,0x3e,0x6c,0x1f, + 0x1f,0x83,0xd9,0xab,0xfb,0x41,0xbd,0x6b, + 0x5b,0xe0,0xcd,0x19,0x13,0x7e,0x21,0x79 +} ; + +int crypto_hash_sha512(unsigned char *out,const unsigned char *in,unsigned long long inlen) +{ + unsigned char h[64]; + unsigned char padded[256]; + int i; + unsigned long long bytes = inlen; + + for (i = 0;i < 64;++i) h[i] = iv[i]; + + blocks(h,in,inlen); + in += inlen; + inlen &= 127; + in -= inlen; + + for (i = 0;i < inlen;++i) padded[i] = in[i]; + padded[inlen] = 0x80; + + if (inlen < 112) { + for (i = inlen + 1;i < 119;++i) padded[i] = 0; + padded[119] = bytes >> 61; + padded[120] = bytes >> 53; + padded[121] = bytes >> 45; + padded[122] = bytes >> 37; + padded[123] = bytes >> 29; + padded[124] = bytes >> 21; + padded[125] = bytes >> 13; + padded[126] = bytes >> 5; + padded[127] = bytes << 3; + blocks(h,padded,128); + } else { + for (i = inlen + 1;i < 247;++i) padded[i] = 0; + padded[247] = bytes >> 61; + padded[248] = bytes >> 53; + padded[249] = bytes >> 45; + padded[250] = bytes >> 37; + padded[251] = bytes >> 29; + padded[252] = bytes >> 21; + padded[253] = bytes >> 13; + padded[254] = bytes >> 5; + padded[255] = bytes << 3; + blocks(h,padded,256); + } + + for (i = 0;i < 64;++i) out[i] = h[i]; + + return 0; +} diff --git a/jni/ed25519/open.c b/jni/ed25519/open.c new file mode 100644 index 000000000..1ec4cd2bf --- /dev/null +++ b/jni/ed25519/open.c @@ -0,0 +1,48 @@ +#include +#include "crypto_sign.h" +#include "crypto_hash_sha512.h" +#include "crypto_verify_32.h" +#include "ge.h" +#include "sc.h" + +int crypto_sign_open( + unsigned char *m,unsigned long long *mlen, + const unsigned char *sm,unsigned long long smlen, + const unsigned char *pk +) +{ + unsigned char pkcopy[32]; + unsigned char rcopy[32]; + unsigned char scopy[32]; + unsigned char h[64]; + unsigned char rcheck[32]; + ge_p3 A; + ge_p2 R; + + if (smlen < 64) goto badsig; + if (sm[63] & 224) goto badsig; + if (ge_frombytes_negate_vartime(&A,pk) != 0) goto badsig; + + memmove(pkcopy,pk,32); + memmove(rcopy,sm,32); + memmove(scopy,sm + 32,32); + + memmove(m,sm,smlen); + memmove(m + 32,pkcopy,32); + crypto_hash_sha512(h,m,smlen); + sc_reduce(h); + + ge_double_scalarmult_vartime(&R,h,&A,scopy); + ge_tobytes(rcheck,&R); + if (crypto_verify_32(rcheck,rcopy) == 0) { + memmove(m,m + 64,smlen - 64); + memset(m + smlen - 64,0,64); + *mlen = smlen - 64; + return 0; + } + +badsig: + *mlen = -1; + memset(m,0,smlen); + return -1; +} diff --git a/jni/ed25519/pow22523.h b/jni/ed25519/pow22523.h new file mode 100644 index 000000000..60ffe0d34 --- /dev/null +++ b/jni/ed25519/pow22523.h @@ -0,0 +1,160 @@ + +/* qhasm: fe z1 */ + +/* qhasm: fe z2 */ + +/* qhasm: fe z8 */ + +/* qhasm: fe z9 */ + +/* qhasm: fe z11 */ + +/* qhasm: fe z22 */ + +/* qhasm: fe z_5_0 */ + +/* qhasm: fe z_10_5 */ + +/* qhasm: fe z_10_0 */ + +/* qhasm: fe z_20_10 */ + +/* qhasm: fe z_20_0 */ + +/* qhasm: fe z_40_20 */ + +/* qhasm: fe z_40_0 */ + +/* qhasm: fe z_50_10 */ + +/* qhasm: fe z_50_0 */ + +/* qhasm: fe z_100_50 */ + +/* qhasm: fe z_100_0 */ + +/* qhasm: fe z_200_100 */ + +/* qhasm: fe z_200_0 */ + +/* qhasm: fe z_250_50 */ + +/* qhasm: fe z_250_0 */ + +/* qhasm: fe z_252_2 */ + +/* qhasm: fe z_252_3 */ + +/* qhasm: enter pow22523 */ + +/* qhasm: z2 = z1^2^1 */ +/* asm 1: fe_sq(>z2=fe#1,z2=fe#1,>z2=fe#1); */ +/* asm 2: fe_sq(>z2=t0,z2=t0,>z2=t0); */ +fe_sq(t0,z); for (i = 1;i < 1;++i) fe_sq(t0,t0); + +/* qhasm: z8 = z2^2^2 */ +/* asm 1: fe_sq(>z8=fe#2,z8=fe#2,>z8=fe#2); */ +/* asm 2: fe_sq(>z8=t1,z8=t1,>z8=t1); */ +fe_sq(t1,t0); for (i = 1;i < 2;++i) fe_sq(t1,t1); + +/* qhasm: z9 = z1*z8 */ +/* asm 1: fe_mul(>z9=fe#2,z9=t1,z11=fe#1,z11=t0,z22=fe#1,z22=fe#1,>z22=fe#1); */ +/* asm 2: fe_sq(>z22=t0,z22=t0,>z22=t0); */ +fe_sq(t0,t0); for (i = 1;i < 1;++i) fe_sq(t0,t0); + +/* qhasm: z_5_0 = z9*z22 */ +/* asm 1: fe_mul(>z_5_0=fe#1,z_5_0=t0,z_10_5=fe#2,z_10_5=fe#2,>z_10_5=fe#2); */ +/* asm 2: fe_sq(>z_10_5=t1,z_10_5=t1,>z_10_5=t1); */ +fe_sq(t1,t0); for (i = 1;i < 5;++i) fe_sq(t1,t1); + +/* qhasm: z_10_0 = z_10_5*z_5_0 */ +/* asm 1: fe_mul(>z_10_0=fe#1,z_10_0=t0,z_20_10=fe#2,z_20_10=fe#2,>z_20_10=fe#2); */ +/* asm 2: fe_sq(>z_20_10=t1,z_20_10=t1,>z_20_10=t1); */ +fe_sq(t1,t0); for (i = 1;i < 10;++i) fe_sq(t1,t1); + +/* qhasm: z_20_0 = z_20_10*z_10_0 */ +/* asm 1: fe_mul(>z_20_0=fe#2,z_20_0=t1,z_40_20=fe#3,z_40_20=fe#3,>z_40_20=fe#3); */ +/* asm 2: fe_sq(>z_40_20=t2,z_40_20=t2,>z_40_20=t2); */ +fe_sq(t2,t1); for (i = 1;i < 20;++i) fe_sq(t2,t2); + +/* qhasm: z_40_0 = z_40_20*z_20_0 */ +/* asm 1: fe_mul(>z_40_0=fe#2,z_40_0=t1,z_50_10=fe#2,z_50_10=fe#2,>z_50_10=fe#2); */ +/* asm 2: fe_sq(>z_50_10=t1,z_50_10=t1,>z_50_10=t1); */ +fe_sq(t1,t1); for (i = 1;i < 10;++i) fe_sq(t1,t1); + +/* qhasm: z_50_0 = z_50_10*z_10_0 */ +/* asm 1: fe_mul(>z_50_0=fe#1,z_50_0=t0,z_100_50=fe#2,z_100_50=fe#2,>z_100_50=fe#2); */ +/* asm 2: fe_sq(>z_100_50=t1,z_100_50=t1,>z_100_50=t1); */ +fe_sq(t1,t0); for (i = 1;i < 50;++i) fe_sq(t1,t1); + +/* qhasm: z_100_0 = z_100_50*z_50_0 */ +/* asm 1: fe_mul(>z_100_0=fe#2,z_100_0=t1,z_200_100=fe#3,z_200_100=fe#3,>z_200_100=fe#3); */ +/* asm 2: fe_sq(>z_200_100=t2,z_200_100=t2,>z_200_100=t2); */ +fe_sq(t2,t1); for (i = 1;i < 100;++i) fe_sq(t2,t2); + +/* qhasm: z_200_0 = z_200_100*z_100_0 */ +/* asm 1: fe_mul(>z_200_0=fe#2,z_200_0=t1,z_250_50=fe#2,z_250_50=fe#2,>z_250_50=fe#2); */ +/* asm 2: fe_sq(>z_250_50=t1,z_250_50=t1,>z_250_50=t1); */ +fe_sq(t1,t1); for (i = 1;i < 50;++i) fe_sq(t1,t1); + +/* qhasm: z_250_0 = z_250_50*z_50_0 */ +/* asm 1: fe_mul(>z_250_0=fe#1,z_250_0=t0,z_252_2=fe#1,z_252_2=fe#1,>z_252_2=fe#1); */ +/* asm 2: fe_sq(>z_252_2=t0,z_252_2=t0,>z_252_2=t0); */ +fe_sq(t0,t0); for (i = 1;i < 2;++i) fe_sq(t0,t0); + +/* qhasm: z_252_3 = z_252_2*z1 */ +/* asm 1: fe_mul(>z_252_3=fe#12,z_252_3=out,z2=fe#1,z2=fe#1,>z2=fe#1); */ +/* asm 2: fe_sq(>z2=t0,z2=t0,>z2=t0); */ +fe_sq(t0,z); for (i = 1;i < 1;++i) fe_sq(t0,t0); + +/* qhasm: z8 = z2^2^2 */ +/* asm 1: fe_sq(>z8=fe#2,z8=fe#2,>z8=fe#2); */ +/* asm 2: fe_sq(>z8=t1,z8=t1,>z8=t1); */ +fe_sq(t1,t0); for (i = 1;i < 2;++i) fe_sq(t1,t1); + +/* qhasm: z9 = z1*z8 */ +/* asm 1: fe_mul(>z9=fe#2,z9=t1,z11=fe#1,z11=t0,z22=fe#3,z22=fe#3,>z22=fe#3); */ +/* asm 2: fe_sq(>z22=t2,z22=t2,>z22=t2); */ +fe_sq(t2,t0); for (i = 1;i < 1;++i) fe_sq(t2,t2); + +/* qhasm: z_5_0 = z9*z22 */ +/* asm 1: fe_mul(>z_5_0=fe#2,z_5_0=t1,z_10_5=fe#3,z_10_5=fe#3,>z_10_5=fe#3); */ +/* asm 2: fe_sq(>z_10_5=t2,z_10_5=t2,>z_10_5=t2); */ +fe_sq(t2,t1); for (i = 1;i < 5;++i) fe_sq(t2,t2); + +/* qhasm: z_10_0 = z_10_5*z_5_0 */ +/* asm 1: fe_mul(>z_10_0=fe#2,z_10_0=t1,z_20_10=fe#3,z_20_10=fe#3,>z_20_10=fe#3); */ +/* asm 2: fe_sq(>z_20_10=t2,z_20_10=t2,>z_20_10=t2); */ +fe_sq(t2,t1); for (i = 1;i < 10;++i) fe_sq(t2,t2); + +/* qhasm: z_20_0 = z_20_10*z_10_0 */ +/* asm 1: fe_mul(>z_20_0=fe#3,z_20_0=t2,z_40_20=fe#4,z_40_20=fe#4,>z_40_20=fe#4); */ +/* asm 2: fe_sq(>z_40_20=t3,z_40_20=t3,>z_40_20=t3); */ +fe_sq(t3,t2); for (i = 1;i < 20;++i) fe_sq(t3,t3); + +/* qhasm: z_40_0 = z_40_20*z_20_0 */ +/* asm 1: fe_mul(>z_40_0=fe#3,z_40_0=t2,z_50_10=fe#3,z_50_10=fe#3,>z_50_10=fe#3); */ +/* asm 2: fe_sq(>z_50_10=t2,z_50_10=t2,>z_50_10=t2); */ +fe_sq(t2,t2); for (i = 1;i < 10;++i) fe_sq(t2,t2); + +/* qhasm: z_50_0 = z_50_10*z_10_0 */ +/* asm 1: fe_mul(>z_50_0=fe#2,z_50_0=t1,z_100_50=fe#3,z_100_50=fe#3,>z_100_50=fe#3); */ +/* asm 2: fe_sq(>z_100_50=t2,z_100_50=t2,>z_100_50=t2); */ +fe_sq(t2,t1); for (i = 1;i < 50;++i) fe_sq(t2,t2); + +/* qhasm: z_100_0 = z_100_50*z_50_0 */ +/* asm 1: fe_mul(>z_100_0=fe#3,z_100_0=t2,z_200_100=fe#4,z_200_100=fe#4,>z_200_100=fe#4); */ +/* asm 2: fe_sq(>z_200_100=t3,z_200_100=t3,>z_200_100=t3); */ +fe_sq(t3,t2); for (i = 1;i < 100;++i) fe_sq(t3,t3); + +/* qhasm: z_200_0 = z_200_100*z_100_0 */ +/* asm 1: fe_mul(>z_200_0=fe#3,z_200_0=t2,z_250_50=fe#3,z_250_50=fe#3,>z_250_50=fe#3); */ +/* asm 2: fe_sq(>z_250_50=t2,z_250_50=t2,>z_250_50=t2); */ +fe_sq(t2,t2); for (i = 1;i < 50;++i) fe_sq(t2,t2); + +/* qhasm: z_250_0 = z_250_50*z_50_0 */ +/* asm 1: fe_mul(>z_250_0=fe#2,z_250_0=t1,z_255_5=fe#2,z_255_5=fe#2,>z_255_5=fe#2); */ +/* asm 2: fe_sq(>z_255_5=t1,z_255_5=t1,>z_255_5=t1); */ +fe_sq(t1,t1); for (i = 1;i < 5;++i) fe_sq(t1,t1); + +/* qhasm: z_255_21 = z_255_5*z11 */ +/* asm 1: fe_mul(>z_255_21=fe#12,z_255_21=out,> 5); + crypto_int64 a2 = 2097151 & (load_3(a + 5) >> 2); + crypto_int64 a3 = 2097151 & (load_4(a + 7) >> 7); + crypto_int64 a4 = 2097151 & (load_4(a + 10) >> 4); + crypto_int64 a5 = 2097151 & (load_3(a + 13) >> 1); + crypto_int64 a6 = 2097151 & (load_4(a + 15) >> 6); + crypto_int64 a7 = 2097151 & (load_3(a + 18) >> 3); + crypto_int64 a8 = 2097151 & load_3(a + 21); + crypto_int64 a9 = 2097151 & (load_4(a + 23) >> 5); + crypto_int64 a10 = 2097151 & (load_3(a + 26) >> 2); + crypto_int64 a11 = (load_4(a + 28) >> 7); + crypto_int64 b0 = 2097151 & load_3(b); + crypto_int64 b1 = 2097151 & (load_4(b + 2) >> 5); + crypto_int64 b2 = 2097151 & (load_3(b + 5) >> 2); + crypto_int64 b3 = 2097151 & (load_4(b + 7) >> 7); + crypto_int64 b4 = 2097151 & (load_4(b + 10) >> 4); + crypto_int64 b5 = 2097151 & (load_3(b + 13) >> 1); + crypto_int64 b6 = 2097151 & (load_4(b + 15) >> 6); + crypto_int64 b7 = 2097151 & (load_3(b + 18) >> 3); + crypto_int64 b8 = 2097151 & load_3(b + 21); + crypto_int64 b9 = 2097151 & (load_4(b + 23) >> 5); + crypto_int64 b10 = 2097151 & (load_3(b + 26) >> 2); + crypto_int64 b11 = (load_4(b + 28) >> 7); + crypto_int64 c0 = 2097151 & load_3(c); + crypto_int64 c1 = 2097151 & (load_4(c + 2) >> 5); + crypto_int64 c2 = 2097151 & (load_3(c + 5) >> 2); + crypto_int64 c3 = 2097151 & (load_4(c + 7) >> 7); + crypto_int64 c4 = 2097151 & (load_4(c + 10) >> 4); + crypto_int64 c5 = 2097151 & (load_3(c + 13) >> 1); + crypto_int64 c6 = 2097151 & (load_4(c + 15) >> 6); + crypto_int64 c7 = 2097151 & (load_3(c + 18) >> 3); + crypto_int64 c8 = 2097151 & load_3(c + 21); + crypto_int64 c9 = 2097151 & (load_4(c + 23) >> 5); + crypto_int64 c10 = 2097151 & (load_3(c + 26) >> 2); + crypto_int64 c11 = (load_4(c + 28) >> 7); + crypto_int64 s0; + crypto_int64 s1; + crypto_int64 s2; + crypto_int64 s3; + crypto_int64 s4; + crypto_int64 s5; + crypto_int64 s6; + crypto_int64 s7; + crypto_int64 s8; + crypto_int64 s9; + crypto_int64 s10; + crypto_int64 s11; + crypto_int64 s12; + crypto_int64 s13; + crypto_int64 s14; + crypto_int64 s15; + crypto_int64 s16; + crypto_int64 s17; + crypto_int64 s18; + crypto_int64 s19; + crypto_int64 s20; + crypto_int64 s21; + crypto_int64 s22; + crypto_int64 s23; + crypto_int64 carry0; + crypto_int64 carry1; + crypto_int64 carry2; + crypto_int64 carry3; + crypto_int64 carry4; + crypto_int64 carry5; + crypto_int64 carry6; + crypto_int64 carry7; + crypto_int64 carry8; + crypto_int64 carry9; + crypto_int64 carry10; + crypto_int64 carry11; + crypto_int64 carry12; + crypto_int64 carry13; + crypto_int64 carry14; + crypto_int64 carry15; + crypto_int64 carry16; + crypto_int64 carry17; + crypto_int64 carry18; + crypto_int64 carry19; + crypto_int64 carry20; + crypto_int64 carry21; + crypto_int64 carry22; + + s0 = c0 + a0*b0; + s1 = c1 + a0*b1 + a1*b0; + s2 = c2 + a0*b2 + a1*b1 + a2*b0; + s3 = c3 + a0*b3 + a1*b2 + a2*b1 + a3*b0; + s4 = c4 + a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0; + s5 = c5 + a0*b5 + a1*b4 + a2*b3 + a3*b2 + a4*b1 + a5*b0; + s6 = c6 + a0*b6 + a1*b5 + a2*b4 + a3*b3 + a4*b2 + a5*b1 + a6*b0; + s7 = c7 + a0*b7 + a1*b6 + a2*b5 + a3*b4 + a4*b3 + a5*b2 + a6*b1 + a7*b0; + s8 = c8 + a0*b8 + a1*b7 + a2*b6 + a3*b5 + a4*b4 + a5*b3 + a6*b2 + a7*b1 + a8*b0; + s9 = c9 + a0*b9 + a1*b8 + a2*b7 + a3*b6 + a4*b5 + a5*b4 + a6*b3 + a7*b2 + a8*b1 + a9*b0; + s10 = c10 + a0*b10 + a1*b9 + a2*b8 + a3*b7 + a4*b6 + a5*b5 + a6*b4 + a7*b3 + a8*b2 + a9*b1 + a10*b0; + s11 = c11 + a0*b11 + a1*b10 + a2*b9 + a3*b8 + a4*b7 + a5*b6 + a6*b5 + a7*b4 + a8*b3 + a9*b2 + a10*b1 + a11*b0; + s12 = a1*b11 + a2*b10 + a3*b9 + a4*b8 + a5*b7 + a6*b6 + a7*b5 + a8*b4 + a9*b3 + a10*b2 + a11*b1; + s13 = a2*b11 + a3*b10 + a4*b9 + a5*b8 + a6*b7 + a7*b6 + a8*b5 + a9*b4 + a10*b3 + a11*b2; + s14 = a3*b11 + a4*b10 + a5*b9 + a6*b8 + a7*b7 + a8*b6 + a9*b5 + a10*b4 + a11*b3; + s15 = a4*b11 + a5*b10 + a6*b9 + a7*b8 + a8*b7 + a9*b6 + a10*b5 + a11*b4; + s16 = a5*b11 + a6*b10 + a7*b9 + a8*b8 + a9*b7 + a10*b6 + a11*b5; + s17 = a6*b11 + a7*b10 + a8*b9 + a9*b8 + a10*b7 + a11*b6; + s18 = a7*b11 + a8*b10 + a9*b9 + a10*b8 + a11*b7; + s19 = a8*b11 + a9*b10 + a10*b9 + a11*b8; + s20 = a9*b11 + a10*b10 + a11*b9; + s21 = a10*b11 + a11*b10; + s22 = a11*b11; + s23 = 0; + + carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21; + carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21; + carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21; + carry18 = (s18 + (1<<20)) >> 21; s19 += carry18; s18 -= carry18 << 21; + carry20 = (s20 + (1<<20)) >> 21; s21 += carry20; s20 -= carry20 << 21; + carry22 = (s22 + (1<<20)) >> 21; s23 += carry22; s22 -= carry22 << 21; + + carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21; + carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21; + carry17 = (s17 + (1<<20)) >> 21; s18 += carry17; s17 -= carry17 << 21; + carry19 = (s19 + (1<<20)) >> 21; s20 += carry19; s19 -= carry19 << 21; + carry21 = (s21 + (1<<20)) >> 21; s22 += carry21; s21 -= carry21 << 21; + + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + s23 = 0; + + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + s22 = 0; + + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + s21 = 0; + + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + s20 = 0; + + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + s19 = 0; + + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + s18 = 0; + + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21; + carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21; + carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21; + + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21; + carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21; + + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + s17 = 0; + + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + s16 = 0; + + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + s15 = 0; + + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + s14 = 0; + + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + s13 = 0; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + + carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + + s[0] = s0 >> 0; + s[1] = s0 >> 8; + s[2] = (s0 >> 16) | (s1 << 5); + s[3] = s1 >> 3; + s[4] = s1 >> 11; + s[5] = (s1 >> 19) | (s2 << 2); + s[6] = s2 >> 6; + s[7] = (s2 >> 14) | (s3 << 7); + s[8] = s3 >> 1; + s[9] = s3 >> 9; + s[10] = (s3 >> 17) | (s4 << 4); + s[11] = s4 >> 4; + s[12] = s4 >> 12; + s[13] = (s4 >> 20) | (s5 << 1); + s[14] = s5 >> 7; + s[15] = (s5 >> 15) | (s6 << 6); + s[16] = s6 >> 2; + s[17] = s6 >> 10; + s[18] = (s6 >> 18) | (s7 << 3); + s[19] = s7 >> 5; + s[20] = s7 >> 13; + s[21] = s8 >> 0; + s[22] = s8 >> 8; + s[23] = (s8 >> 16) | (s9 << 5); + s[24] = s9 >> 3; + s[25] = s9 >> 11; + s[26] = (s9 >> 19) | (s10 << 2); + s[27] = s10 >> 6; + s[28] = (s10 >> 14) | (s11 << 7); + s[29] = s11 >> 1; + s[30] = s11 >> 9; + s[31] = s11 >> 17; +} diff --git a/jni/ed25519/sc_reduce.c b/jni/ed25519/sc_reduce.c new file mode 100644 index 000000000..d01f5a573 --- /dev/null +++ b/jni/ed25519/sc_reduce.c @@ -0,0 +1,275 @@ +#include "sc.h" +#include "crypto_int64.h" +#include "crypto_uint32.h" +#include "crypto_uint64.h" + +static crypto_uint64 load_3(const unsigned char *in) +{ + crypto_uint64 result; + result = (crypto_uint64) in[0]; + result |= ((crypto_uint64) in[1]) << 8; + result |= ((crypto_uint64) in[2]) << 16; + return result; +} + +static crypto_uint64 load_4(const unsigned char *in) +{ + crypto_uint64 result; + result = (crypto_uint64) in[0]; + result |= ((crypto_uint64) in[1]) << 8; + result |= ((crypto_uint64) in[2]) << 16; + result |= ((crypto_uint64) in[3]) << 24; + return result; +} + +/* +Input: + s[0]+256*s[1]+...+256^63*s[63] = s + +Output: + s[0]+256*s[1]+...+256^31*s[31] = s mod l + where l = 2^252 + 27742317777372353535851937790883648493. + Overwrites s in place. +*/ + +void sc_reduce(unsigned char *s) +{ + crypto_int64 s0 = 2097151 & load_3(s); + crypto_int64 s1 = 2097151 & (load_4(s + 2) >> 5); + crypto_int64 s2 = 2097151 & (load_3(s + 5) >> 2); + crypto_int64 s3 = 2097151 & (load_4(s + 7) >> 7); + crypto_int64 s4 = 2097151 & (load_4(s + 10) >> 4); + crypto_int64 s5 = 2097151 & (load_3(s + 13) >> 1); + crypto_int64 s6 = 2097151 & (load_4(s + 15) >> 6); + crypto_int64 s7 = 2097151 & (load_3(s + 18) >> 3); + crypto_int64 s8 = 2097151 & load_3(s + 21); + crypto_int64 s9 = 2097151 & (load_4(s + 23) >> 5); + crypto_int64 s10 = 2097151 & (load_3(s + 26) >> 2); + crypto_int64 s11 = 2097151 & (load_4(s + 28) >> 7); + crypto_int64 s12 = 2097151 & (load_4(s + 31) >> 4); + crypto_int64 s13 = 2097151 & (load_3(s + 34) >> 1); + crypto_int64 s14 = 2097151 & (load_4(s + 36) >> 6); + crypto_int64 s15 = 2097151 & (load_3(s + 39) >> 3); + crypto_int64 s16 = 2097151 & load_3(s + 42); + crypto_int64 s17 = 2097151 & (load_4(s + 44) >> 5); + crypto_int64 s18 = 2097151 & (load_3(s + 47) >> 2); + crypto_int64 s19 = 2097151 & (load_4(s + 49) >> 7); + crypto_int64 s20 = 2097151 & (load_4(s + 52) >> 4); + crypto_int64 s21 = 2097151 & (load_3(s + 55) >> 1); + crypto_int64 s22 = 2097151 & (load_4(s + 57) >> 6); + crypto_int64 s23 = (load_4(s + 60) >> 3); + crypto_int64 carry0; + crypto_int64 carry1; + crypto_int64 carry2; + crypto_int64 carry3; + crypto_int64 carry4; + crypto_int64 carry5; + crypto_int64 carry6; + crypto_int64 carry7; + crypto_int64 carry8; + crypto_int64 carry9; + crypto_int64 carry10; + crypto_int64 carry11; + crypto_int64 carry12; + crypto_int64 carry13; + crypto_int64 carry14; + crypto_int64 carry15; + crypto_int64 carry16; + + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + s23 = 0; + + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + s22 = 0; + + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + s21 = 0; + + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + s20 = 0; + + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + s19 = 0; + + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + s18 = 0; + + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21; + carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21; + carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21; + + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21; + carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21; + + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + s17 = 0; + + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + s16 = 0; + + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + s15 = 0; + + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + s14 = 0; + + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + s13 = 0; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + + carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + + s[0] = s0 >> 0; + s[1] = s0 >> 8; + s[2] = (s0 >> 16) | (s1 << 5); + s[3] = s1 >> 3; + s[4] = s1 >> 11; + s[5] = (s1 >> 19) | (s2 << 2); + s[6] = s2 >> 6; + s[7] = (s2 >> 14) | (s3 << 7); + s[8] = s3 >> 1; + s[9] = s3 >> 9; + s[10] = (s3 >> 17) | (s4 << 4); + s[11] = s4 >> 4; + s[12] = s4 >> 12; + s[13] = (s4 >> 20) | (s5 << 1); + s[14] = s5 >> 7; + s[15] = (s5 >> 15) | (s6 << 6); + s[16] = s6 >> 2; + s[17] = s6 >> 10; + s[18] = (s6 >> 18) | (s7 << 3); + s[19] = s7 >> 5; + s[20] = s7 >> 13; + s[21] = s8 >> 0; + s[22] = s8 >> 8; + s[23] = (s8 >> 16) | (s9 << 5); + s[24] = s9 >> 3; + s[25] = s9 >> 11; + s[26] = (s9 >> 19) | (s10 << 2); + s[27] = s10 >> 6; + s[28] = (s10 >> 14) | (s11 << 7); + s[29] = s11 >> 1; + s[30] = s11 >> 9; + s[31] = s11 >> 17; +} diff --git a/jni/ed25519/sign.c b/jni/ed25519/sign.c new file mode 100644 index 000000000..de53742a6 --- /dev/null +++ b/jni/ed25519/sign.c @@ -0,0 +1,41 @@ +#include +#include "crypto_sign.h" +#include "crypto_hash_sha512.h" +#include "ge.h" +#include "sc.h" + +int crypto_sign( + unsigned char *sm,unsigned long long *smlen, + const unsigned char *m,unsigned long long mlen, + const unsigned char *sk +) +{ + unsigned char pk[32]; + unsigned char az[64]; + unsigned char nonce[64]; + unsigned char hram[64]; + ge_p3 R; + + memmove(pk,sk + 32,32); + + crypto_hash_sha512(az,sk,32); + az[0] &= 248; + az[31] &= 63; + az[31] |= 64; + + *smlen = mlen + 64; + memmove(sm + 64,m,mlen); + memmove(sm + 32,az + 32,32); + crypto_hash_sha512(nonce,sm + 32,mlen + 32); + memmove(sm + 32,pk,32); + + sc_reduce(nonce); + ge_scalarmult_base(&R,nonce); + ge_p3_tobytes(sm,&R); + + crypto_hash_sha512(hram,sm,mlen + 64); + sc_reduce(hram); + sc_muladd(sm + 32,hram,az,nonce); + + return 0; +} diff --git a/jni/ed25519/sqrtm1.h b/jni/ed25519/sqrtm1.h new file mode 100644 index 000000000..d8caa23b6 --- /dev/null +++ b/jni/ed25519/sqrtm1.h @@ -0,0 +1 @@ +-32595792,-7943725,9377950,3500415,12389472,-272473,-25146209,-2005654,326686,11406482 diff --git a/libs/armeabi-v7a/libcurve25519.so b/libs/armeabi-v7a/libcurve25519.so new file mode 100755 index 0000000000000000000000000000000000000000..92bc523570c2c5156b18fbb65855418208ed2135 GIT binary patch literal 135228 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 @bm)vI#sM4RCU@8?tm;3m@Jx^ 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 literal 0 HcmV?d00001 diff --git a/libs/armeabi/libcurve25519.so b/libs/armeabi/libcurve25519.so new file mode 100755 index 0000000000000000000000000000000000000000..8fc5d8f21649b530c8cb72f127876cc6fcf001d1 GIT binary patch literal 131128 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
_lH5+mdekM>Lj8=LmNy52!!~ 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 kDJn`doJu8~ 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 R+`WlxHsckEDy$Uo{tsO#YwrL6 literal 0 HcmV?d00001 diff --git a/libs/mips/libcurve25519.so b/libs/mips/libcurve25519.so new file mode 100755 index 0000000000000000000000000000000000000000..b738852c3a996c4b30c8b321513e406e1f475a30 GIT binary patch literal 235636 zcmeFad3aOzo&Wni=NN2^0YO+qCk36#vXzP^BB7}=mtUf=iiCiYFw~i+Pb_vjVG&D- znVVlxO%^8+$Z68lbb1>PW--KAh7d4)+Mw78G1f2%5bG7@jl^)hAQ@`_$1}y zm!KM@M#`nhNnGTJ_#5jM=Qiugef^H#uj11fUIAWFUh-Gi z+2wrxmi4Vt|HFB|jQ5E1fA?(^|GApi6}-mr`agJ$<#n#VZ#rKNbKbwg`-F2`Sk8CC zn@V1#&hMA-evR}05bs~*<>6JqtAy8={j;%QUp=VcCZV_d3GWO41{Zt)s@9)i@FM5` zQ=Ip6y$x}`6n+){jdnf^GrpkDq$m?%-1$p^-eS_EE z@(T01me-g0ll3Nl2EDuoaN<8xst!Vo;ByHbNAJ{C2fxebBp^M(U}y6#pYMQp$vbol z!}57HOiJy7hqNP~eW zfft}3{YZa*kp8wQ)xZ$tv#c2pi{MV@elPv`Xn(fzImz`Mdz5Mhz;gX^`a5yIZqZ3T zU&HmMpr2q`K5yi+`UhRrIG^{^f0D^Q?9lUXcz+rITYp(opi4M7e0G_s#Mzf-QfPj`aD0|p+opr$jRmQ{ULt$K>xUN z|DE(7uFcc$-x+@){l7u~YL%wSobf!*?=GG^jOSmu-?LU%RZjoEqCd}SO}+oA-QTTT z-<;A_ghegm|31y7p?9y-{}tSyWPUFHPI7&&fdBW?Uh~zON&tAd{|@>5C^FR<&riAk z6~^!K??JxLJq^FPPOdLTP!!H5!8j-Xqpe@$_un#(yI9y!XZ!-)M%oix$nSs8XH}!C zu=D%>K5Kk@tzLP6&iES+K0oF6!v3D&`*}Y^hC+8~@0YyCZ-XAtRX&Fp?^VrysCQHl9fx=aCdx7?Rws>U$Wu9)5v!hF0WlAZ7j<7uz1zVl?(n0 zjV@ZSe9?E8Gp1{oty!{U#gdi(mFe|2a&zsnl?&E=cg>Q*p{?c1|H8=DFIiK&bi+AY zw0}}7Ao`MZgk|FIc?z!WXRl?!pUSP`i@RGRO<{!eB0Z!MaroH>_K-_5v3%vE&ui=elmi zs>QWSYnOb%JeICmwc?BVfF2Wt=!=p-?~M7}v0%;OwHBS%)-3qu#LE{hShuLA+9vs` zBpl(c6|2_w6T4=?+L~(n(z9ABWY*e6G_m-*i~4Un2Sd3A>J_j_nqIhk)uP+iesKp( zXzl6?41nKj*REW$Y{9zP{MgSi2qFHOC5u+ANLGh0+}t9l@C8?_`W$r&X~K{g&;^>X zfpDQ=aK(xOf}LkR6O$8x6QKISz7{XM5PD@iZd;dsL0gwse_>nwI2AG=wyM`JShKEn z#f2ck2o|qGW}dP1ztnAKEhk&-R9|tqAo~lZB5SdF(Sk)a7hHG@?Ta~a`2|5m6Bm|2 zT(IK8SAePHsyi;f{F|3wh}rgHSheyWmaJKIfyr~(g}}$pYgc~}4lLp&FIOnkwd)oy z3WY9RyGlN+aNZX=?~9kqf2xuQPXh7_GK?t9a*L7(`G8CPUyF!&0tQM z*!Y8beTLbP!Z(gJDmAllqZ&A|SDg$;)iaZbcr=e!)0^>xx0pVku8a-br*xBFt@{X6 z(0<1BD4jks^uZWU7(w-mN*UWEXKb%9jtPvzqkf(Zssq__^*CeN&zSaQz1FzyWn3wh z(@&>;YCyTwc2kZS6m2nsQO3J9En_u(O1H-w<+lNhN$Dr_!}f2%g#BA{6uV-C#Kr!$8PbI33U z=DaV%9ISbVoq130wRzUVyeB#H4y%LN3F?XS%)h~z|Jcnkf3x+3P2Vr1L9s)F)6Q5O zes;(l2RgsK%5Tlg!}*Q*+w{rl4s9%cP8ZrhJD0A?5W41cGwAD||Cw}?HoqL*pn*j< zXpn&hne)*v;?VCepW5{6fPS-~pBLQ3)RWn1>QFWc9VbJ_2soMqj>6z*0yqjRC#42EG}*9OFi}VoqF~N%xA5DEJiSf{O$Ir?UBzz&w6y39Mxm{R zQD|kl7`13wV%^uKkL5mxj!jYGtAfv*QEp)~MPCWsj6~I~=I|m9^CLoL-KPxUYm~LM zEekIUcg?xHRfh2P1o!6(cx0~&n}4*;c=Pw2jr&CZZ}gEFs-h!hY|tEjMRjPNf#w-# zo`L2WXr6)Q8E6h4_rXUS_i^~9)X*)yRED+i>Sm!qNBWI`^czvX+HB7k8IjtYF?*uA zwEtz=79PEj-Wd?NCw+)aIq^pu8#%Bs*;!i=Xf_E@yr1>9FFVqrT}xW2uC!MzR2~AdBRmpU-_(X4 zX)96N`0d&BMJlRB$h1^Ty2QG+n25;2q^FAOK2dw=gG}S5PaVKN-I#Chap%`gZ4< zZ|k{`==c4k+mFduLXns10OUfIVTPFri1Dw#`}IUs5*InxnM5snv7AY zVQbGh&Isd-yyuK_CUN(Mwh0!#T4+;v)XM8wi)KOSSE7#5e(`D#-z#64U0811x*=OC|aVM4ezNi zG?R5;HVoImgbu!F=xhD>3NzkG@L9%T(GJ?$W4}%Fs=aAh2hzUq-3R^hw2gvQiIBFf z4qAKx1J1QBzDk&9*rILa>M*n|QLh%zb}0AmO-GQ<%qaL|ZX;MvX5<}fT=p6=xOOIZx}JFlxlY$2>b|zg@I%JtbubfgFk{UNy1MHo zc*eZkHQ2T~0!>N;+s?J_yp&qDS}^WoZWivDN7!k@tL8v|){^IK)+1PgCT7&H2@Qo8 zUe(5&1ZS76j;dGEQSMc=BXpJdNqb(BBwfpjRWw?nq7j0)s(rN?nW+V$4O%GnF4xsC z-`98#T5HFQ8dm$E2sH4rej;x>xLgQLX0qBhwM~Q1LJJ?XkTo54XyFqcpFxY~3wZ3( z;xYKQhx^3uYEg~8$28t*`lveh9EbBlbS%-C(b&$Q@ftwrRC(AtcC4cW5H zmLsu2$XUNO8%)H&gsjU5bBRC)Tb34U!Ozj55bG`-+E$CqjfsqRXqLysZL4F*7*`IO z&4Pgj-E8$kgO}1~>)#>YQ>?#c*58LoAG8WXmoWXxx^nOHs$qu6w5Z6j{>Jiq?K(CqW;|6cgs!F?~Mh3;~_OB0dTQAETM11PgSG%QGK5k3kQc zCnDqWJol;x;ECwNnaCqYZzHp;^=8Q(^pB+vZMjne{g+#Er-e4Vz}0SEB7YVtp>5FO zeGpo@IGKq|%&#HANHnfnI#AX^zwWDwX`=f=aj~gt6>BnUdZ%h-swIog$&{(x*pFS< zcka44Z`GM;seOZXyV2}3&<7ZJgG0WP8!z=hD=z1F3j(8XQH{jv;sBn?cZg!zs+h( z#w7Pe`{ySz?qEjjTkPeGyH?ZKxDD92LIaUs-IvivxELF^7%cBpT_mk-8~1f3_hM&f zMg+ya?V`41f5yd@>22ZqeHm=;Oe14?n6XGaMdAvL>fUq;dmY*{wsg8d-IK0Uo6--f zrgU0m`2V475*vP<`ce8W^~3aXZ23FYmbCh(V&-*s+9c^1Q4gf`RXV)h%WqF|?ZIpf z{*pQdxVH{e`XOF_Mpr&HOsQHbS2m1Nq0WajJ!??=@^D(M9*RAGFl$!buIq;esKBXL zjX<(pFM$?i>To7d_p%;pTC15IzcCi$N8od3zk?qUR`-L``&b7J=|Ih1u0KS-KZI5X z@iiXDpMN+#Nj;QS57K5XVyOGD%{oRJX8bzc%>4nqbBT_u&xRUr(#o0dWZDijPShrF z5Be}`Hg;-VFBh{OIx}%Tmq1g-lQrvptv#%Mn(lfOesmVAu9pW=uk(1;Y^u`BxHiH# zMlyzrxi$^mqNm1DD*`{bCU9z;!PtoJbY`NR(Bk{p+?`_#vuTY!neQa_V`3k6?HZu& zQjes&GKLDJJervsVN8JbvsIRP@3VL?4H~Rdn^`Mac-x7Ox<3=`7){MXXC}}wUuO=P zXmY4B8>@6|JjT918%Pc{m<#?l{nrfPw-Lr9{_mCFYKEZSIy36QQSg%a=f)W_?lSy! zv*`}qOuod}4-u1kG863?sG`jSRJ6fR(Uh*tx{PKv&d2Y31eyB2X7+5*%;tHT*)S2i zm+_zatr3dfuZN(y*?hpVfkRax!>suLUNmXZ#3*HU4xx>kv^4y@lXL??%1YB}wKzf4qRu*Du~i>R&Gr$1;5tB0|_?`{jK2k{;5 zZS$!{e4TsR67;{AF;=lo;;fr_tQik$MSq!!s_zd{-(gLu)dQg6=CuASg#h=Js6Z+V zEw?i6myyvAq}5hK>AT>4!*E00Nb;Y82eW2{t{=f?g-&Wgu?jVR$oQ`UQx8Ii>-9k5 zeb&L_+Qnchs042hYN3WTT9Ntz@u@N3zZ1W_1MKh1nhiH;5#A-9QVwp=iP=!@xY2cF z6#N1)F}%W(v9v zONqk>9u9-&J=tgiJ=ys!#yP<#qhIi!iN*&qpRXH&6`y==0xyla3Z1%Ir#)~5pBmSI zl|O=$uwf=I)k8fi@Q>cr(XB?PE~AHX*Xw4-K?^r#(^~jl3er1q(8Dw1sIhH}@WF*dq%we51w3UH{B5)uwG=&W9MShwd>>k7&6-ntL_zy0) zW?vQ?Iy(<}YkE?QMQY-_<=uA_tNQy`m$4vYWqnjF11mGqsbhmx{XK&$tdyy_#7PJ0 zn{FMfLP6#cU!|GxJ*s8alvhP=5Y zU4NfPr7rcT=$v7y>-&aE(N=V&q5L&3Dl@rK`GW(rKvhT�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 literal 0 HcmV?d00001 diff --git a/libs/x86/libcurve25519.so b/libs/x86/libcurve25519.so new file mode 100755 index 0000000000000000000000000000000000000000..c06bd2b7321edbe96e36b2d8b2f159058c52e74a GIT binary patch literal 163904 zcmd444|G)5eeXL11B_s74Q8+hESqsXNkpuq!ch{XRbnX>c;YH6A`!Ow!V6%8BN7=4 zEF6)mCSr-SI(N`}q~&$_^=9?*R>-Zp!n<~FDJ3b*W$YLn8r$H;f7<5e;WV}fJF&47 zN0`R&KHuNoXZ}gTZu?%}V=Zyc-uw6e_uGHY-oJC^%PYH9MWfM3_-}S(b|fsdPnDGDl(TEwS|ADxz}IJ|AqJz?cYz&MkD&0qf#V^T^!HRevWqgQ<*=ti%0i!LwzS-jz+@21`zZY@?T9mU{n4XeQG~Rdli2KPVpDg zuSKlX=L#ATvGh;nYf7a^MCJGLceFkd`5mrTV70rsWwA> z`}V)!I`~=i^Ys3aa^|Cv$e((CXDL5$Gcpqp;(yg_@Zo=phyMWO@Sp7YVan?VB9Ys< z3VxubeTeeO--<-O30<}SKIJ_JBawL4mA|R>yCRXl=yc`BDIfiZNaV)nUAag(xi%8{ z{duk&kD>pA(a5LaEqcwABZX+>m%jW-%BN37BZzG6JJb&RSAF?Esr*nh@(o`;LOF9X z8d>ejKcd|Jx6w$Ww}(1L^JCG-pZNAh%A>%q_vJ3i`yP!(#(a5*a^~@9KSufJccYO$_(=XabD{Tl(UL#jO?lrlkx1V2zln1APxf9#`853Za4-Bp%E{Bw z$W0#ptCUY7?<(K^7nGX`rr+}IKcGDL{b;1gmw!n)`D8Sb_wqGh=Y2njM(*_ZzeTy5 za_Ilt)&6uevN7)Xk5L}P-hCea&nQQBLyUjP$g3A0s3{+zoT+G!tXsEnc*D9oHgDN5 zu=$Idd~w6jrs2&)6z(3{u<`TjHuish-5nb?4@7R+aL|cO*g*({VP7S?z5Y=Y}y97?rodz;qu#? zcKln2Y`=YA^G4CwSmw}o*Oo0C{uM+wZW!3OeE^v{KD%wxraL!n8Tz*n`PAmmZrL!j zecPr=(e9@5UqSSqP1`o#v13|_uBpl~pWVJ;TVEyD&u`j+)gv3X?HC-oYn^0Rx2ey0 zW80=XRy5<2b$6rvhHZCl9~fG9`-Zy_dPd+oHm%#x2mMO{+`awwO9O1)g5X1!hC!H1 z0}S1D`;MVacV7Ys#RlKEZaTO-@9Nup$L39Mp~oHD?z;1>cwmnekvlitxp9yvtTM_M z@Wb6^=ezqiynn@ww{IBQ*uT!1{H{Sf0@j^(-BU7l|AxE!*E!G{N|nyKdm|+Jwr?z< zP2*5tuwI3mMEdrDyEcCQ?zd)uLU(VygaEW}zI)52&u$pnT$X(rA%NS6dY7Oi_iWp= z@vb`u*EL@POoZ;5V%bw0?(OXID z#XPd=ZrIp=$^HlATRY^&OGXYPE@4xXxZ%Bb z-=)Hx{<_g$`v$^_(v+!+cd$NS*=zrn^Z&d2eG^xfuJ$kGua|RO#$SRztt^`PdpCdj zcO8E>^7kA3eUQJa_*=o>`}k|*?>+pr^7jG$-p`-b+*(Jm#E-P`_v`%W-}U_cCVveo zRQzL^bjg2O-mrvJl8W+Tf6wDXZ_|uZ1^U zWkZ(7k$3W!e(p6--r0q^Pbu=Nq0S4KPxg< z2B)lNkeIG8i|dL}61pN_QdbD4bj6q#|3a3y_;u0Jzl&cNk$&BtIen3TUzgVXYCdz) z%JOo*D$g9Xvb65ke`g-BvOLnCQF))0b=%*l@*c`uOaDGq{1W#&ANmeaGSQbUPK+s< zg3fF_-^eA2be&lKmE|eA)!zFcm4mV;z#&@=R;p|_~(kBaA5IIKeT6J z=$*97x;r2GOf`MzeV{~dkN=l%$Ub!2t?>A1ekhyCk7OJ3bAyp=ENIChh*{_2p3@k% z{!sU_(agEAAAfo5MEiL6viy}2xC|=uMK1n<91XQpaPBkf9-25Pm#h5UU~RT37|J#m z#}HxXLl0eKDL<5L2frm4$hN}N7{)T9vgaragX1$u0^f#wPc}z$&oqJt4^NC8ICb%& zw`T+7W3uJ%;r;wv5F0?7BKRH&Zp}Fm;!YaVNlY$Ub8s$?7Kq)9om%pUjb?4wyi&if zUyy$P#MZ;VLBI5+^mFQW&8U7!aRP~eN=x~G{%hK$FRgr+DPJlqhBKCN$@VX@ z&zjqA{ZujkrtI>!Z$>Q~uuXS%3G3mMmeT-0Fpu57QD%r(0J^UYn><-nE-RbbN2!mu2Md^7x{KnE83XuuRpZ4y72arocRcLSo0uET18MxyAZ-|s zDy@^h9R1Q4zv{bo7hx;QJG+H|6;A3h>ZXhH6Og_cI_mphc_L5muityLY5=$`D6BYo zYrf^d{K$i`XiH(?NFkOVdN58g5gjVTDJLe{XN{ekzx^K=75Cg0oLce3t@x+qRDR@C z?1q+;SC5<|@=gX^CO#P*J3oK>KMBzLgZ>jdg_BNV{dSaWf_qawI{EJ4wpcJPzd4?t zJNdKxhltAjs<=Eo`AU>~02flow`CcsV!?1c=uZgXK>!Qqmr*ZRwbk!`Ga9`ZP(dNT zBX02L^RaCG{jUI7e}5_)eRA@J=!>#2NQrz$a`L@M5r>qBMBxzyE3ps)lUtJ0u}=p5 zDexdaxh0-oZqg>p(ymHN*vX%jS)?c58-<_OBO!KT0WWbWsg(};8;oum#ux(vNoR-D zWI6DYAy@Z>hdxk)@V14GKxVsW#O2CRhAfb(7uZV!|g$TCsxhxAlytRdBCZm zxReh0Rn6AhJZYpzYsX!p@>&Z>>~$t}6E6>_Fp zZ2*`B<-5>XxuCzx6hnx{{N|j6R%M5C{!wUo5s1OonL3=L&4u=GEa(ERV`Qp3M!Rf= z;t=*`z_iUX^$f9S8T_iR5&e2-YIV2<^$*o=__&AXi zq6{Ifb0n%dhmAzhi$t+hYN!n$Y47Cp!)^$uZV(O@!SMQ^zc1g>n%_Yro1_YPC!8CK zod+u-19LS_q3wIE#_4J`Y!vi8uhz7s@azxz2TTa`Zk4&F>vy@SgK@iyx#sGaYj)BSsk=+Vh(=pfVoHlHqumE4f@e zyz(MjJ<8!5o3Vq#qgv$Ow~PPjviVg=-Okt}+c;9wtyd0@8`5qet;*#;mSf@axK<t>I%m1vGnzam`xF-@6>dyR5wdB8Q zl6_16i;NLyU9`HbOkmU#5cnbk4?0?d)ouBmYRP}q?C~xAUn0=FXmv|SK)eOG$N&Qd zjgH}9bxVHrNPg?ENxoymxHc|Yjpe=ATL;1G7+$n`B=q9eXzPAzq&8Kb-fd& z&xA=XTAd2P^ILm`v3}9&zMvx+tY-Psn_s;)zhiBQdVJC9L>atW;Ju4huMIll!Rkc5 zqdUL4C%>a71dlFS&0^JQD$wplt9ycIusVjf6WVaFU6M7y!z;dLEJMC4TDZ70I2)XY>(avMr3!Y%sun`Lz^9#ju^wIR>-|O*f#rJHVD&O`gJ{H9U+aHHYTtr{GEZn}b>zd$6IPA|B*?PfFL-|?!-#l3ic4bH9bqc}W ztom8-pdUaOJ$kZj`89DMHL|6HleAE1q@eA9Hn5uN??>eN`vHj-D7Q}bWz*3UK;a}8 z3Q z4LJ=>pb0cBGzL=bLujn*@}$}!r4e4`9HhERWWFZkUDFDF|5lW6Y0)@L)8R}`E+W@VvdD=@r*>c8>v)o)qoN_82zOSfPTORR1N)sN_(_ok7Fo~DAC6e zR*#EGI#NknEF^8ar8Q8FK0HdEIcSo#BI5&2?{2`_A*F>zBYc}E%MQ}BQH&6=iE_7C z(JrzIi1Lc4Y~)74(ypy8W&n{995FBmu6Mm>tSzkLoReBSw-#@B+K^9hvaYNtSC*h=l;#IcnvQQJ+lDU*o z5bjqF^PIzU-A{bFg=gciMi%DiSsIJa*WdqG{ryh{XQQVkpYua-l~m{%;b;p2jS!D< z%hLE5-6*Q@$wX$hKOdZRgeDGVN8xPLb}5Rpr zzu*MQRTcODk`>2DcIa?7f36$FFG`@{!r}7@z_;N(P@x0gWLRHf5;I=z@0#j&l@`)b>t3zg7VM-zyArfvEnQU zjvbpdc6`=Ldu!(Z+k)eo{(H0_+LhH*y|Dr6CfcK8=Q7)WR)45t z_6jY6T32wtI(mFpR_g)%qiGgJO}_e9_;I{&I$3wJ{?Nj@lf-&!;m7H^2Y3D$jxH3t410*0Ne$@m@hTRO9Cbx%W~@I0lb=;C$9pkhNDQ0cY+B8MV&D2o7zY*$j+k!zdv} zv!w`3v^s$2d}FkrhLO{VlYtaa5j4YI^0xl{%axpC3esUx@YReDH7x=34=pc4fyi|#!O!KblowYHRkSo z-c&wlodLF52IwEwSV&QGul^y$6%;iG>YodV`a?SwDJ)hek*KxqWN@CySaH$E1`g}E zlK40ZJPNqjNKv`C%4PqC*nq?8CFU~haFve@&Clkt^ol1e__S)1)x=eC{PfKJX}d&h zG?{f0J^WgYZ9pr`L0ga;cE|;Yem=Bj&SQ?Mc3CJ;8y=84%$dHaUh%5ph*e?182Y2 zirUXp`{ya@nkWeW=ii{v2AbrhPMnmNKy4Sb(#_j|+E#i%54BQ0wA2tw=#gwUwGy4$ zaYO>PliEIN<8@a1jH}I3+fMC1SG$Ka9AMKh9*hHbFnf}u8(LMn3xGJ1;TqRr=Xl)?P~Z9%bkrDG=0Jf01jvc-RTmR9bIOQCN!QHTdcBo|om4{86FvdC8G zz`F70-~8_#|42%HzVr3S=C0RcDsZZ+4UHs2pmqR~EFV+B-e0sUP zU!iK^h7FWe=HeDtrF*I@PCl)wT@G=t zRRtE~8iR@-VKrJNp)o2*WvQ^n>s+I1Lt>XZ`abAnZHz8p1>LJ!uJ1J;gz!Id{_^l9 z;@`TeN)ZoFh5E)+s6VKJQjAR;bPjLH7PmXzR_hhNy$arVe}by-$hU}DDZmG(f@Y~2 zdpWxONe@b3GjPB(Xv-Go!7X?(RPqgVFlSn-jM4K{eTR{tmeFHVLI0YnN=BY4UCbmc z{wF;-R8t>!R4O&tK9%t8Q=vXl!OQ!^ri=qlGkW!V95<`=c2xha5nvuw-$_qZ?-f1n zRLlB9s+@Xm%%oXz2Nr`iK7IQ&L-J#rneR^>ok|7wQuQ4rt@`JW8aCUfV%%Qh>;_!# zo4^9xx<T*>A=j!tF3E*0l#l$|vd^p!KAvQ_a#ACW=TM;?83iiN%nlDCTYeeq9S zRizYrr$X(V3bjR52iYGhMP6{0xV4T=+7Ds&<**Xy@SE2!W+x3 zJH=D?lZ7{yhM-HULAO82bMjuX)_(YP{I_KCAA(b#f8uAeVOMxorxSzGM~E4oTm-j$ zXK>P_D4bh%-O0karBSxogF@lirOQtU)(N+4Cqw?lXwZdF1!zXm!^`%Le9!K|`=Y_A zp?@@>*&)!sei1goCI~ZVRFMrTTi&t>MJrz6cUP>&-{$4Gxx1U{JQgi#+{77~iMfZL~Spe zo(*Cfh*{!1!%(Fgpvcr0XVGt#MWsA74{vchAlpUo#)- zg_jYd?y>PT3xd~z#|jq!NZ8$(^!S>~3Kx>&AFoHdT?nq~@imtV=Hp2ZGrud_I{xuR zc`k)Fz+E{0@suloxY+Ja&xK0iOd8O(@sD3gT^pdmYvUik%H1c&*IY6F@w62TEI0n~ zC6sca`0=Ig4susx>|Ei+G~#G4rS8G;H4W+ou)RjtWTA-6*N(4T=5}A`lxrd%I<-B) zF3s0Ku++-p7oWIz@s2%v!f|kacCU_}zLs69Rqd|q5SQW~1GDp?f8d=)p03tY6CBC* zanat?#1Y%U3l6Xlp+PB`jQ^O2kwH5J0STt>d*Q-1$>2WwwV+^wR12xYY5U~cuixyFT1kAh_*ol{S;;{`SnNW9t%R|a1xBT6| zC1Z5wLkmzx`&A_K=Ks)kr`T{~c-na+dqks@)24|{tOqDU>!2>Xvr6dZSdOUYrvCSD z5eKdQaXHdp0A+#4iZ?a^rh+W8%Wdkq=Hk5Nt_e@-p*jUv6FULyZ9q}*^@omR7l`gI zx1FiHRC0Cg**qSg@^+rru!8nF$6go>=5ntM&tO%IJ8gCbtK!_H?JmJxgWV;$%h+9t zyR6-%xofn$2JZBZYOpH9U9;U~xofezM($eeu8F%gyKCmI-R@er>$E$K2RXZIBmTxt zf4T15_^O+Ty}$4~fHGVDO5*Si#UTZYLmz*W$3Tq)lizOVvi&#wZst44GCj(U>@Odj z_498I#qT)I!in9}u^&f=W*?guIm}Rf@ufd;)i2t<>SM>|0ulXhH@$SEc< z*qiO5YJ8Wq#a^ly&4hn9Sz*6*X27h z+zr@Wmb*c_(@Cj@lzc}Mcf)qq%-x9HwQx6TciKeVWp`~zFW7NP^!PY--K|?JFuI>q z-ALrimIfWmjvZ?+Ha}|5UfM3#K1d34#=uGli%?@DfMLb3tsmX_ZbaE_?#y51hsGb` zTQ@YWgBUqCmG7%q{N$-8|DRXTiC_(vgob1yawNS(qh^o0ZRB>ZyKO3sGg5{@>R#1z z{yRZpqec|7DrE%$-AD$I0u!s+38P})_f5A(l!#}X5*BS;)E}FB;V{sVW3jmx{Ra9k z2)B$z*s-JSLTRTts^yzD=R_|hdcY`{=8URR11gXVzhh5GjYLtqt+Y4Q@_goRnIssc z*KW1ZuT6ZQ33fxV^=ziL>fjM4SHzPF>j~ zSvkqEXTQAsv9Elue%G&z6l2DKi>`W|fAq}J;m3y_+5T$q^0Cf2kktJ@y7HEHQ7{x^P1e@ zDk?SkY`H4b#D=SCO*WUIq{-(fR%r5hibbW-5rAFU2K<5l=8dPKJ{DdrMX5rj*+uD6 zqI86!Md`85?7*npf974pWAXJzG)1vKLCe^2X-%G&b{4qA-NpQOyxoSctWfYa+inMu*F9OGNp9Yf_<`-=<8E zSbnPlIiBB^Qi4@}YnoCbzb#EEk>A=d`*V7^g1Y4FF9Ot{B&TwhX0IeruLe zI$xLHmYqF6&ubTyf^Ow!#gHZ*3?_r_6eS&FCxb!S8Yn4_c@4te@fgem@t`|PtrF=i zJsf=+l?Pdx&sRKg+lq_I$r{%(7{$X-XO`26+$>I2I9cKJ>2#d4x95{9p5R=GqeUGv z)gM~UG1JSiOmN7DU9xq@?8IaaqPtO;?qn1WZPodq^7}-|FrH6xh^52sLs;=_U9rS@ z|M9tjP7o9D(cl%ncx9Bpwo^?z(FdT;0QVo~cx-OndH6O4#}IP?R)_FB5w`R6IjWXi zttCE(Iu;07LpFX5sl!nF>TrR2!U=sm=>#eho)53H!P8Yz&n`)QIYhK_t)yEeb$wat z*(P;!rPP;|q@G1K08d;I^N0( z9toc2Xzt5o3hac#PL;8{!B*N^Nn)KxCBe)rdD^Tv3pBkAlEs?n-iAe$bSqoT za+)kHoL?&2daJhYe9+oXq2&o>q*vVM$4K@ZhB6}nGeNDmE7`Ls;3SNjsUA|f`j&d1J z98LvWEpwUUI{?|W5wSiqzS7b(HuPhiM6i`1aVa1<+T+&VPrEwNAc@k!R;6nV&@Nlr z$&7feR`~QFDo{YXWGjvR%p$9^4Cup3uCevo)ema@_GvyT#T44fvmOhxAD|wK{G@Fc zFr}bl@e_QtKy?^9##N8w#ou&wdd@bO8^W@1Py!gYF@7VD2ZlstNJIpdy82|mD}~S~ zzR2eU3{LwQp6D-`u&vI+90P{1h=Ei5Huu`Ge#Ik|xYnUT@kkBkZc%3)5Mqx^i`|x; zIA9L`j>dxG7a(E?tdW0P1b9zCkCGJILVViY_DfsVP7;bA4Uv%XN+DUkdB`^w;?+v6 zJ@ew+8mXURaZ-){U23Lk4eyP@*XR=p{Kr7vu_u&wcZgRhukrkY5C?hhn8L3o#H*E8 zi>TtQ{AUf`U+)yY0>qcE^w=*^spwGvSYw_881W?zboNO2>4C^raBh7?d{Wt{m~@GI zV4MXcKR>@Mmj9yO(2eJBNw96UUv${pVttkWVk+3}l6!B_x~iY;v;#ug){y_A-o75p z$H92OVGQR7C z`IrIZT1)P1lKj7MbW;8JUP(1WM{nwA{?IsVM5vRTZ&|6Kyw7#GaUleN+!h zLe^7jSh3GlBuf=Wo`IxPA+n$}mMX-VinOahlzXYbcCr#I?&A_Y8hwI>qpm^rvDEVK zazphas@`ih0_~t$pRo4tyLP!Pk0&P&WJguw(mgwYJxY;UF=X6n2S~}bos$D>m#OS> zihPx_Pz9oaP9S>gNFZYGtCAjM(3h2FD-a1{Rj+t}VJp4tngKbF6+<(jJL09y&64UW}(Rog7PL-mDCW3WL8-^2*jIMRZ>GBl38WxAP@^@S0$-c zrGALi8Kl;ptdyYkC>L-u;PwKTh1Pm2mx4HJx`?pQMuGQIXUd9Xq2!G%O43D?B)G~_ zL?GVSs*)}Ok>HLt$^fFE_E-{JSbnr{SsCs`hgxrOZAeQMV3nIM*^^$E6j0vgs_Hio zOMdrySVhU=qU8M|N|Ie@(nXZK(N!gN1R~j0mM#MEMpu<&SJgg5!|2TzT^rm|h1#QB zz-^{RUIDhn%dRS~P78oO#+#ui?+8jUE`}l}lbd&hHd>}6*;VD$5hZVWQIbZYB=J?2 zIs);gcf{SmcNm(8--!6yv)@M%KyUg+YNQb8O7D0x9pGQFzJQ>qvZE1U+Z@=A%4cSG7Mp{gH% zfE(lRDCO|2nDz8@piry2d^{NR_dc>uZaVYsh zRl`;#e+g%_%+h;8FztewL(?o4>v|B=F>Yu1!3N}3K$N?zx9OH2V?EOj9*;*MmNsxQ zV6gh>^v^S3CBd@irV=Jtdbr8}-dcVv)7G-KqhZUlRnR{L9o{#Mn$|1%k9$;v1=Gm#I^yJqETuT*PKX7`+>ee%s zd(MXP0B}9!=WRF+0w-1VES4?74FT6w1vhN_W+T}V;Iu)ODaofNPCae1Y#T)j+R{9D zdeM&95jE5rDx`DAs1VenZybUN&-jQt!q)?EX-@zdd)XD>ZITfVCQjh8cCZu=oh1=E zdC)AvyMl+kY+CbXW%~{G=-C=9gl8L6w18Oe9zb(?c68F(qvIe?oj`QE9Re4Y0Xvmw zZt(?XKTn)GYk@Jk=wVz=u}?DS$#O@^2o3AO6Z?jQ)F**p+W-#C*z}BCK4Z*Xu1iM^;GPk!nS3>j(#utGmaO!cvyBbj(>R1Lr60|3;*a* z0kPuO0ANFjTk69+2ei1LO(y!i&%5Q#J7KNy1s1Kqv`?hIH_^2E^A7Tzuu6h83V<3? ztC=6tlSP_E{+*gJ%sm={wK#2LaFD=wUc+|Us>0EpV`&evF9ZoE06z!`r`_YYas6dj zw6i^>C3Uzs*4&{5wy10T&~T-)sB1Ot$GJ`$h6Nx}phK$LOWQN4wW&%X)?>1(l(v$y ztpSv%^>_w?(kK=#{2X<)D)V^Q(;qQmRg!QcMt`_0+K88403xQcq*qzOs;tGbTf!0l zlr=c>u`61$MqGR z)->{@uacB0W0Qoe0>&zw6NDqODQGATbI?@U^Ll7VL}=v10+6mUSyhQsmsc)VCfsG5 zR59u}2}bS~jyQ>obr?v%tc7a5t|tX*7FJ$|X3t;CAz89&9i_+>fGBHyB&Mk0mJ&`4 zKLw}72q?|fp0cVGM5^-2s!GZ>OctT6S>Mmb4W^fx#Yz~ZUdq(z)%AL4p7rk2jP1F_ zy#NWXhoF4t5!5~;hoztz;ZT~5{YI(w2Whr}S>v6bPc@O)AZO<$4Ca2DRr9z~`GpNk zrglbVh!1Dv<8W*TGy`1ck|#BvYkRkoA;o57&ezK`ayOz?ZIo(8ZZFT{+FsSV0a`ka z^P9psu=KvSZLc18v#~yn!UVA7$uNmnJAn}+!|QfV5N+W}5673}7=YBp{S zXX7@TjkU;7Jh(-Ma_E{E)eo1j%AbCynT;E7u-UlXX5&tL+zT%At+sI6m&@r)u-Y3Q zW?l?-Xh+xeS%DA@2Ey!}2zG?iu%E#FY@C~BHXei)$juH&NHd}f8jjSzAtf_UZ&x%Ie$w$|G5McHk*h^!cMoz{O3~nThjU8U>%}<5hSwt z&o$(4(L#Y{eIztJk$7Ta_bnJNswCStls@|^ce5eHJ(q+lC0!|{QX(s`Gp?aQ_vGI$ znP~dnL}GLF@xX{YrZm~dgIDPZF?kCBn%*SMcKKs0HWKP^X9~|PUH(e&C@{*lOww_p zuN0nJviuC6#Q+AlR1KUyFjfPmw>Q!u7-(sc1cvH{=$QbW)h?>}CUBPuBsdd&5do=K z{;1EnRWD^Ly#7#EHm0o5cyLDL!kfwQ9Jv$|O@E1zBICJPkc$=GNY_0YoC#hpyg7F~ zH{0D^KAwviQoQio?D5>3!i6h2F;?>8t8|fg;liBp+}!Z~vhmzJyT=lX$8)^ZH>d7p zUvUBKy5QCE+-(&;hV0Cw=9GS>w47;KKaz++|_?72~W%mu^IVGe5kChhJp@n&(@J4Jrr;JqMB{816Iyi;=4TWdt7T!$N zof&82nHWNiYjmAoIFIDl^1)qBcQWeM?4i2@9r8`}^vY;?kW2C1_vygrbNBI~&Lj2^ z?{~vl^dA-20UPE>pJ)u4vJ*FVGq1;rr=BAvN$F0<8z)!>(hcH)&q%tpNR!GmPmtCe zaE9sD; zj88w{`@jtWQ%Z5;pGu9d^E)qgzMSRbHi%q*NXtqc)Hw-@7HP;$FI&LY0yKOsTd4bZhVu3Un<-jbPNPT4NEV&g& z$BNNHQ3m6cuGu^yxz?T!4v(GXt>~)epOEF^n)iP2EOLyU<#pyNhzBbml#nxamR?&0 zv9AK+v_X*dT?O%_3W(lWqA|AyVr>P)NrT{x{wf;3Q2~*|EMsTm6FjiiK&611SlNh6 zEcPD04Lj?Rma#I)I2QnVl%1!3kz}y2{^bqj`r9k&pDNe$;$coL zo>do=^jtr)yx(Rlm~fBJCQ6r+_xcKX=b60i+rOR9Ti!5V#X$9Z^g|C?Dq;ffxKzYz zzpmVRcQWd}cW(auZK`)Zl>fT<_b2l2-fW9J%NvLCENePy+T3l%R`52 zOUvul(=aqmYfSvOGmJtbt|*-If0!e>sG2yK{k~e( zWbi|`-6^A2rU=$3p;x8|*2qgTMX;usyY+V0!krQmgEg((X&?*Mw6UDa=GE_cdUHdD zoy8YF^CWW+qxzMr5Duy6C*~$rNq{QDB}SKM5yT5%Ldqtrw)0kvT=~;GuhzQo}Z4 zJUFHFKfcKK1gTkdZ`8f+NiAS9eH=KxDe_vH#4_8eOb0J3ca2BPIjo=@Px`A^JK_*+ zUGY7c6x4YjrGwY%4}D>-GTA0c<~v0$SiJD#WZhG|kaV{01P{%#Je{E6NUV5*B(g3Z z*bl9;>^}(A1y4%Ix;H&9WF;r;`D9S2KlJ7Kb+6X%Kc8NpwemTvoZ$^8@*ZFDf@IC$5{GJ2@Kz1JE?J|#WR1(GwZ;ki@=CO1jVf_Z zLJ4s#e-DE!yp*_Cl*FCZAalKxK5~LrOE!7N#Km2Uynu(uWtA(ZbKC5ape6jG3Hha~ z5cEo>L;2_h;_=qSb;s+T)< zcdYJd(hGNIA5iZ+aE^u9R=r@@ADm}+Bf~L@4PKllGmuVZR2G8s)QTtc{f}i@AT)zQ zVlTbt5bvJG7B(h|g29MW0Oox??OC7ca2h{dgf#ZH%N?!YwGZi0`cRiMc z+>AZe#ptZCtI|yeJvrtDQ}NIjl%s3XezeYgqD9}%dDHrcAnKk}?@iJ_3v^ix6Z5@u zoxqQU{kEELMVLmQf`ki7d=hWPCn4P~o0g61ywlm`kqW!0?>f6&y6-yEdRvsNwA0+WEL}r(23ixteQj09wwjItFfe{h7YGO zCc|{BH>A_Ub*B(!sbF7AYhF34sr~V{7oG`3^LpK5PoNf_(|2Sao;8u<=M_ZCxyZSu zB>%M~`70vS+)&5o(iER^!>VP$W9ruk?D{jQsdQy6wPgF<*@H44-vx74)P+9HWS^ip z!a7asA2=PVLxw$N6pX@DeRDK;9eXeY5FvaVtni#ZEETVNSpD7ic6FFn>k4_yrn9y; z>Q14f_&{(_T%8Pcoal5F#*X>m#=srRt_iz1v>G3*PWzGkWcI zYh^F^L2K7+&ctCt;3HlIkbjQHp-k5QbX2=#gDR@F?sim7#ooGuw%RqjT!ye((wY@? zgPrOD+vVO2Emk3FNf}}0ZcX2WY%n=ipa8$|r)V;^( z{JP7%@#@ocmJrxzbKwu>xG-IGV%^0! zdO$VKC4@X9%gt93S>#vSz;sNWx!J}PJWYhl*-L7S0yu>138l*0yUV2Uv)awiOOZp5 zDduP0AW4Jyxt#)iNN2_BKj@z2qJPM&+Xe0f3rvm}4W*G#@URU$z-tt;)ZRUSy2X z9wTo?EW-d%SIyF`aiMFN-0!||$q75`P-Gv_dVrMP927#gTXc!j9!6ub6G)I>c*(gW?>v%=nl^i zR0|9ur5SdRSPzLllk$kYobEM&(t4=$!L}Q~vXmpCl%9e-!W&zmuyUD~6l`yJ!mY(| zD+?@pNLrWdY65H2M^X;H)^q69XO+6B98u*Ibf z=2DJ1pNdwBC}1-tz;aBDCFMyk+ew*R#w^R$i)_Lw+ux@_mt9bcVHyZ)fA+L9eydZY z8{_wQ+zn%_#$1Sqc>Z;MpWUO z&UDDod107&HOr3Eb6YJw#_92n>-LTlp}3PE%zIFelqFC!Cy8l=P}c(g-uqtn*@a^Z z_PQD1z*Dm;l{6H`y9+{6rA9k%#9HlimfGbttnz!wgB7kXIu4btH~bpc%Lv|5ikTXF zIF1A`Acuiu_}8q2xMAjsOd;!loB=so$6_GH6O|j_sFZ!2C|N+Q2e9AGkq72&r{*+` z;ZN0YP|L2Qj^MqPC@d_g6?m+Yt`-6@WioqJW32N+e2=Jyv@)XUD*yXm3Mzvm%dnwn zbd=Dp&x*Ge(hN84PVI02Xl2^#qzVAprDQs{Gg^*#O|(AZjY7=?fW^!9NqB1z@gg0{ z?aX90Hf?>MuFy7)=t7o7LadURES6?4lS9-9QZlZO2MZ*_Itx)8JB6J_kdh;Z;VxTa z*}OB|6!`t_OCTi_1&(Yh6ec$A#AU%QTYxZ~WMu+%p(bvsCpE+HK^23H@10>fkD|&u zB@Io>@*^%9c&`~l#o#6r0wmKA%3((FQCEAK3;tAAj}NR`3ZPmg&sCNL zRd%8u+2(FYf$&p@_H6G3Vj2=QE|m?eUvW^syiZK^vBO4sm9}MN*<7V+aeW`~=5Iud z;aYr;=xrY&7NG-EZ~N((-WxJ4+df40eZYnnWbx*&lG<~Xk?Zf@?H1sG65>*?2PFL{ zQjhtreIW@X2qTmFJ|!!+qh~v>FFOl?XUXconU%>s3+;fF8-bE7VA}+Qw{Yn3qqI=# zE!>QSTClJd{4E|==J2?uxrK6wE8BzXE!+$rX!c&CHG}#ua#eEiN#vyfY=M$Dt&957sNhZ8>I1M%od6-*B&>u4@iZc!Lx?DgQ|s-j zhp<+Rs-7afy^?JnS9bONKg9Pj=*=s}8Um&;mJMVbrt+*(>0f1$}0VXMhT=Xd^w*q75K#x^@fY((va^*N*fS z;Fzt`<65LS+|y#`MnH$}qRX4F-K$?kKwPE1j0bmJECh6e0^b+KQk2P#n8wjQXu!-S zfIaU|Gp<#Z3XTU)&OT!B3YbOijhd+z zlI^Iiw9ssQcF;+^i0=Z@dRpWwE#rD9Ew#oGu1Au(5;;`cJSzzWWBzfYzQ6_Ng})C7 zQ%z-T_(Sn_K8ByE7TR#PYvfzR4o1Et$TZeT>cb)s_I2k=ujZil%TWbIxJyCvappW}(Kl1CXpjo2l;FOnW~-?X{r{2D__C$+4jsbnnMC%z#)( zC~yQA*QFZS2QnBlKwlSss_mq!6+ za#e6T00@wtlZebNF%&v&s$AsQW2J$>z2Z`Tzk9>H{(iJ`zxdVfjRb#oJB6%IR=D@e z^}&s#BDNokJwTiL^2TBkA@S`#D)6m#@;FLbT==oJalL4Y8imu+sj%-y`aOmL$2m8sL z>C|91tA?<)+Iq6~J(8q+)q{zu$>ku|MTmA!FD@HNQ%LqeGE)*%SuNcV$W%!ghb%`N z(@UA1LNc3AWlI!eP^_kwhFU|O!@wB`#-BaUI$}qjvhel#&NLS9WSfic6HiZ3%=E1#Y|T!$3mv~fXI*=G zg8Gh7e0qZ7rB6;lFcA`@6Ct8Lzs#4lrl;5lT6%hlVy1Vbpx98R=pM|hZ%j+ZNKLyQ2FLx_5?D9jS7M z(0T#P_UVbX=iO4m(d{hL)sDdkfva`K^rYQ-r+U*pm3#e__P09 zHzP`tfjK1<0}4?>x0RM7N05c}kIf2`Kid58r;eSyIFV>Rtn1_pW$8{Wo;4#Y_rM7& z19~e~He+mlcx=z>7o*1x>jIxAqpveUYdkm0Ej(ahx0wM?Mq*2$-yT%p{d~-q=;} z24wKIN-eW+0sN!Q?%mWXb2$RI7d&AdHd~cZotdlHn60K`V`1!>R0f#m@mh(83{qhM z7r}kmVd7@w(#FaVVp)O7HpGjbxkAaMeg##tt3U)^HVtbXJ;9Vc;4P_08mTf(HT4?1 zm`O!h_0@t@$Y9nJ;YxFe?u@o@L&1I<6$u}WrkA!z4vTaVHyTDg!lmMdRkTb*B9k29&`QE4yFVn&m>5betj;*Oz98><`L zb*5{emo}5xRSV+~d`9b-L9tF}cBslUu$jp&p-H_YGnm9#k0ou7Xjhs9yg)rrUwH1W zah93U&7?Y!CoQFF^2{-LhD$SBfh$F=8!-1}7F&hu?V292U`-B*TZXZ212f^LUHhI( zCLha|m+m^{)2g5f#~puh(pJJl;$*;SE9WF-T)hMya}4$BxIHqk6AiXmQ2^kboc5AYiLogi6#B5#_hBwk?aI zg=V^z%G+GehE%kQ^~#{64sBY)SkntMO$e&BY-pC_3wiCpt%%DMS}Q2Dg4Aa7CcU>) zy_f#hUeRyVOOZRAEqm#~N?B1@8`K_DMO4Pur7_w7igW}ZHrz2^aRg-j6PPpQnj-;EWr&(pvAKW5ofGmZ+Y%A_* z^F#Kuin_;Izqv)vZw-;W>xDFp(q^3y)DPSWs%kx4ENS3ws~NakEdXr`KU@$w_k`B+ zqI}X5n674D*3Q$PiC~(ktiebtOd~n%})>31bN+!NPgbJ87MOyXM@{~mJ=EE>8 z)rM&{!`R7WXqaZSZDD=vqOC962<;|s7{mw-(*kh^TD9kj0EXb>LF(W*(=8j~=H+ZHSs|-bl42lvR_vYm*lXCATw9>hS|`W%;bo36~*mE1g^|QPdKG3>xmjNpM$o zloFdc+SDtrN*oPl(Dfl5?VJy>SI#P`ngTm~tV7na9t*bsZRys{%4R(n-DLtjEOYuK z0xP(XKbv@9|39_pvz3$*`@(rYOz*0g_I=({Nmswm8nEs=XnUWbz;1UnT>Tw!$0dFN z=q&CxSu07&nmA$~7s!orDU*|lF`h!!Il^pST2fZ-qM8YNAv6kbNC7K@E~F}Tx-p>E z(!E{|%)8d`?E^zJTN-(57lc?3H8!$rQ7~2 zA%di|=(PLFd{WD8y;tJDFvJ%-7V~P*`QUZl6SB=$t+wd`;i#dHbu`>Se_%D5q9Gkn zo_v1u<9_6zhlV3Zx!Y8a902=KL%@F2s07}J@fc?00#Wv(h5CMFU#uQ2{>SFyZmei@ zdtc96Cq7|=Xbsw^BQF<;ydNcM$@@`a^Kly-9(G$b&l4`{zQNP%2C1(`^_e-PP^*@_ zgwi&4)ga)9nvxH~Xm-Swh?tc1-OUdJY^Z67s0d#l`cMO*poN@f{ z!5zk-8z*dh8ifSWEE1(|0VNr#JQ7$3fQHd`D{}-AjD_?1Qg>j`>?>;|0GUdokXRFf z>j}HQ@EE7pk3_v#Lm;INXB~$i4acog$I;l;3mL{PFlxstb)is2MzT^@(}OBq4UF2Z z1%^H|rl5yMmp!277+f<3UHcddnT&@95omCfuBuMyFlss=2zr&;`V^pskYEs0e>4uU zvd-?Imv>W3@Ck#D1uZ-;!!HImmq==d2p7U`8+KayiQ*qE)7Eis`2Ssp4PJE}mLHvJ zidS~o7cpXz*dHZeDtm9rPk(gTS4v&hfYrSTt@UV|BE0&1mo3cHWgq|7bXme$UEKFv z99Gq3tNW_&r=j=Utd0X+zlDT)7zFomF-kM`Ro|h@l+^K!l2D*D%EfnCqclxl^Z{%o>adu>cUdr8H>~NlAcWlz1a-q{x-38mWU_86>YO><7sWq%qf~=QY&q}K`Q@Z~ z@OYH)If(V~prQ&x$9>IsN7Pq_QFP#>e!IuVTisb765}1SfZOMgXZzOi4*jC8Dv?Oz z$%0aYGSxlwB7UuhdWC_J8|7xaV-5g{OR(np*6|Mg{->|JoZlp(UNKX5cD!RAEu#)R zI^H4a)#59C@YMO*aej-c1ig?ieJRLb&Gz^3ho>k;e@R6`P~#nXcT4Rl z5SSRoJ1(Q5mx_6=c)5y@>ZYomFP%}_W3+SQ9rd)SUZ1Ay8ShYnwu!MDn! z(iK!Isl?V)fXz{Pykn77IeT6<-l2sG?{^b{;~k4>$^fa2wCs4tmA+{HVt)=XTByE? z>Nrrdfl7>b*atl++D~kacP#O(<`rIA@JLIEGl1rjGJpAa$JGFlOD~lhB&xn8o~9Yo z>xcBhqJHwLVZ7s7-@0(TV;P?)xtCD1ZynZrFNmggK5jy=p2T&>`G^S@{X5PHmB04s zT7PHDvhQh7ND4=u14>4dq?UDc?yD7rxWL zJLh`yQzbr_z^BT1A9cnLoakM4LF!#<`y`3JY{H($&gbkK7Kc{N4ftfaz4OcmG6Hqy z44*b(GgesoMuR?b5`Hq_xc~IUp_Ni&efY|!R9IOTyh+!!&pJG5A4>4z@(qckeGPyw zb(KDo!0_TeE#W@IGSBM+!Pog}gvo{xdk`UleBIo)=^m>r;67{j_D(fSd8-=RYcz1x zmy#ia4B|4Dx^qw`V_4tbK@j5-snp$BCBKxV1a(!)E*{d*~ds^G3#e(?DlK=*u#tIps3&P)9;YWK6SkBb|Dr_ zH5VTO_!vX-s_QWq-)%5+C8ssliMnFg85lgPg8LmB*eZhiozs~MY15&!X_J;f+48O# zrR9CpDbDT1D2)}NyLq*P;(%5iK_vJ zsC1YFxk)0g(Y6}PJ*$GpXLDg5pZt4)ope=xkiEYCHs(Wt*K+PCDP8nzy z!-`8R(_2vpsaF(R5HvRVoPIo-<{Zv5{j`ww&t(AHo57TOjm}yC2kqIMra|ZoL1&2O zVQU_-E&d$!UE!W%4pVip_ukGqW2cT-#7Y1V$eEH*=whIiqG~6Er6pj!fUO0r*A&zS z9bi(@gIP2=YoWG~yuHk=p|$jGEMzDD-egnu&xqv3ek0IsHl1Y*FWWGUS^?lBv-jfN_P8cGUSrUmRZG~VjjsDb z1=EPv5GrU}$S63Bf+IjC{l*B?EUe6=+98%nWlDp=oTwp+I7-VpjI#h>=6=8w`Ld1kZTTfs#tI== zCv7gB*y^oQ1&}*x#M%vEe0usvY|&ih7Y#-7ixp)!4&^JyPP` z3%2p?1#{5z1#5`+43wRuIiSqD-LN}LyayfcA#`^~N{BF+Cqt^F{SdS)%gJrHhop}A zO*~ANTKF{6?BZ}UPal9>Y*|Oia@LWuokCQMi~YkKDepkET7wYR7%wh6o*06+W;E;y__LM zF&l9d!|lT;HbrA4u;aJsWo!p+czTLr*6ApYgfbq7R%ba)$#sIV4cl$pt0qTi+7!=% zz`al57eLa%RtEYOu}#!H*$hh&)wA~TKFHW$fthp|I!Y6rQ5R3k4%ghA7M`!o&@tnT zz#4EWko}s-2@(oX+o6--r8uDHYs#?IKwHXqa>N)7t0q;OA@j9l8X?o@OYZ%J$0^ zqVIys8Koah^`RR}&%d-`)?=7HOfV3FTT~hK8mY~MW|Zku`KL9Cv!s`#Y?K<(W;zTq zZ7Tn?krGB@FOjm=48jKs7!B{!*zsueJaKz~TlfqvJQL0ddSoz!OPi!0lMG-%JtG+S zj3zPTnMu}Gjr8CVR~XS8Beg4n+}Y8Sp3x*`I83s(I!1a$)l$>-cvu;;X2UstC0Y5J zr?Q>3k{pLp@e&8OshM zS>v&*;_?T0q_9<&jv4d;l0WXSFU9FS&*!$wKSBrp!_gLPYTC>+dN`Lxt2vrJDs#sn?}Ab z3(Wc{qzT_%4`zM2w4v2${jwUu_2KDqq(svCXElQBzu|V(bR=Eht;vqkFsoITMVrCG1s@`NpkDFh7Z42=bo5aspPi;q21lhr&Yl$!SDlFc zbooQSy{Ek!*FRc}S4&y49FldZvv^)Mu7>Z^XycBaNF;Z$9~^e1avqa*3wx10=bobe zKkh;^WG(eZm59_!#CBJ9rnL%0`em4}C2>MOrRV#>P4`?o>9BRz57Dsp5c9IV}TqO1)XH3?f2?dL^a|D0fJ!D2P&}OQ5xTKwKB@4Nv9A(>(qchhREl0@mZ-uBS@(@%=s=OC{DdH zg2=_u*r=j1f+#8@NX5#GpcHjlENEFE>!n;qkZs6PiXD zCwB)*%`PQK1Z8;=c9asNqO49)DM2cFtG9$$OQ~pE`70i)yFeWoK*O_E zJg#r1-@;Gq4bJxUG3sN!o}WAzoa5`~Qa{(%^NTBk^L#yN5yNp`Uq^kNub)r-d|$tS z`USo|L4Cs4Uq=09zW#FRFZcEJ)YtoZ-o`* z!NtD*O6srl^?WXW@G4)=srqo**Ds-diLYNu{Ze0lHT75fdh$4i8+`pW)L-N3bJyN@ z%eDMe&7t;XH-2K+UpL5%Q+agV%9yxs5BKXaCRu_H_;Y?>TonBlUEzKCcC-A@r5Ar+U( z11xp;9lsBfUdCq8Y4APV*ioGl{Dg|)QrCF4vN1{HFVy%ZKdS}ixyr^gjlWXkY1fz* zC&zy5XokiHDvA%e#$qLABaJ^5=6`XG_ASd0Q$Osc@9z}@*BGxPt6zB2hscTpG>%== z_dYTdsm*fvLfzPfFW3LxZO>9uEPhP*$0o=vd%E~x-LoW^Sg8}7wZ-?i^7@IDI-Ke& zUgOIB6Du>62a1=u^00pTkv;3;oBVhmyhagvVx=V1-ed6vSF6q0iIoyf8^Xm?uJ&H5 z)&7`{u8M!{YIW>0u`);P5!HUf)#?OPKO^bRyNmy;tKDa{z0~d(><(9}-G+&k>#5ad zLvf?4eZXq_snzLK@#C)csMQWq`(4$3(AA!_+F@#cpxWzP?K9ltKAAImmbM?N?Cgr= zk_)xHA*W%F6!mgwaB?RtzxH1npWaF3y}$k+Kgvg^uBbmyxB{R!ogv`?L)4SBUrzhw z?^8P`5APF<`?MgSM!t)u*FydNlULR6KXDcPE4`w?;eI1xQRrTA3UC#uYm%s%4ffhC zOcY2g;ZoPTuN0O^>Py@fg1arYCZ7O8+^%=wGSHAz(C!Kc3rSVW`#aEryfL;P$8%iP2Dtj98o}$ z(_B`ZK$aYTiVOQpK3znDIFg_X%523iWSqDbFG)?SWbOzG9*{|f=Y|sv(hR#NfK$-x zbNYhyP)3KJ7&)Qe`^Dz8+GqUvsY>xQ)6d>z1x(-R2Rw^BjQW2Q_df7-U&Vp1Whq1r!d4o!C_x*P<(4W% z1(}GhC<5e0BxU&YU^( z`w64riV6$T!l1hW60qRmW{jtSjv@G1F9VR6JE`T6)_EzoV#aD2QG?AeSG%06&Qn@= z_!*O;gRirg?j*_aK#L?1;YNW>Di~n|wqOL-F#;FkbYTfjovv|$EZWCL0xcc1R)Dlt zq_lQvu293-R(e)1t~qVlFM|w$vU_WAb)9afd-*7U;!ACR>B+PqNDS^v9vl z!e+4qgrI~(FHZ2azX(mB!Mo@NDJNA2NL98g3Y6N@qV<4i*HwnDm`P1lHoc}m28Br} zC}U+!@@g7sv08XURRhB;6|^a>6GI^MJK6)(s+Tlp>-0U4RtV7}n5|Mt!a`>Pl+9Qc ze4s%g@VV}E(bDmGkE)R}-!)!p(nTKBx~Z+C$wy2)&5bbNP4rNR^hi5Jj@!|CFs%?H zhyQAB8fzH+-M{+O-&a?N+8-r|hSddv3eIAT)O;{4+ereZ(%v(aY+*Kr*PU!ZfZ^@n z0gEFH(VvXTz||-U(yIJ&MquRnEZIUo+U!k+z)FRVy3x_RsL1!Se?VA)kchf+ zPV4LRk7Qt03SizVA$Pb_=wE=ceU=3)=wGNbgG~Z_08PR`3N=Ay2(2c&X56J&Fo`6G zr;iPMYUCfGFhxV(0qmJR^_F8l{Ytp%RaUea&GH(e)iA5txobhKrnVNu?8?i*5a@`7K2z#tFs$X4J_Xb)v8(3V7|j1%Q#1%qg=KU+axUJDjP#d}Bg z$9wY;P{|6q>DO6X3!FYKJ6#kzwQ(&lg!U`u(+9e13&9q;91iq{#OuPM>D4a;Em7z^ z&<>1~hQH8KNs|N9LhL>Ew7Sj=wj?tUInp>H4dj>1K!CE_EekF%10l>HrAL8lq}k1; zW&>jvx$A`iq|gsNw_+wRyl(o~#V60Z1xIPdf-r-==~Itf_{f{-(cfSOwJj{Rgbx2m zU<(av6=4NbFuAY*Ln~u6aBJ%;Spb*9N&P2caD7Sr0u1gh9$EeVWb9*E{n}E(l(52n zQvWde8qDe+K>dB_=N?hhGbSo1cq7FPMALeT6b-c2ClM=edi({ek#C z5!1z{uzW)4xx^KlfCj2QVi7PTUGFT$u0n%88;Yr90LAoxW$A$b0m&i3WCH?}J!lyR z3Le;i5H?UaFWP{9RIv`UV*~DWE&?gWP0!h|fsW~8zxjrX{|h#-qhX=AuABXF>3jrKvV}hSd4A4rf&O1VM0zQ99`_}4=w5Oucp}iB z7B9VQEn6jXNK1j5iKW2E;}T23fIY=fvV=5E;8x^FW5voO0wkmt2S8biWjL?2zzl>i z13ipEaA^j58ig5jVg~MannAh}qUY`~gRbdgozwsBFEN8XH8U8RKK1#Z@A^1Byr?Sg z2r0d_WCqoxpjNTOQV=_y&T`owh^3%41~-({FTmjZo9J2n>CdC%4Q2IfOD5*&Z6%UP z{b>)+>en$hPS7E$nM%9p145ueCG`)WXBFg9pxg%0eLrQ+?}4QIo^zK1|3*8AJ4LLx z75g$vf%=H0V6dwE;@ntW3LHw!S&BnrF+E^eI^ge?9OF(lAV68gvibB!U7g z+GW5340x0WDjI`Le?n77W*wT*kQ##qSO7Zg%DV4FMJZMws+r1|{pTy|d;}!oJem>u zsi0OUC^t8n&2)j6*~)eqvF*?D4sju`VC+v9Ef7e z21!Zxv7{vRO9E+(O9|nnB|w0(jg|#3s9y;6d&F_qA4B~*8e{ZYM_C`LcZ+jEyPq0% z+Z*cdpFXzZj~;wI>fcvWf9Le6TR*Z!|JaMVv6A}xz(~-bdN8P}zgAIIfA*up0%E6Q z?xvB31r#G?IGbXKIZzNooZyj-z@JdolhFtwP!Qc2W}=6kaG;;7 zP%DE=To6T&d^l4@w5Te+_FPc}i#jUKoMf5M0GF;$$gYSk#_ZV|ND&-_W(0zeAq1h| z3C24ylU%`#EeM?fX*7Xbnn0%cMJ2%O!WX(1S4AyAR(v=k!<+`C1pG-$wFw)la4dkwLni}j9zMdh_%3N z&b1(dWb^6Obl4_B@ZPy!_7%K^+FC%6V9U?ldObbdJj!dq5E#orf~qmsDwbXgVz*-& zFTED*cR19h4MBimAK;Pgz@Jp>%4i2#(=aX64Z}>d(^^m;C8(|i3nC;mAeQ-x5W*EI zS;JEd6fo~=QG!;c|KY%WNTf<%E#&E6J1nM&qIP1UED{*)ga4UZB9+6Cv z6O&zlvfC{SMlb~-OrZy2y)dLH=ra|jFoG$h^^Ktyfr2Ayp5p0a|Mfq9|En;C+iRvk zpy14Z{hO^|>TfWG5%?HgYFnsPR81k`;CclkU>b#3eV`!LwG%NoPQVOUfWh6xBO8G~ zp*fO`K#NM45>|Lg7O-TXKvaYV>`$P;M?fX@7l8s71Lf9>nKcFqmRbz%58U^OREaD3 zB^(3Q5xTF%3GyOYbqw6S&kYl33K3NfH1);zW0tFzyj{6SpZ>z-+#{izscwf6O(sayXi*)s$csUuolIfW?K(RK#`tZ(g#CsH z2@Z%QAgn>CWDOB2i1UF?R6leY;{>h*aX4@pcp|W#7OOH<{!1?dPZ}g}8CW_3ob0Hyv1OSw^SQd<61VR{ro3n>qjldv5VFX2xKqDxE1owpz5G0s?@w+~U5!_!h z0)hlv9{Ej!oCXQXMo^0dG#EjxqG|-SWgs>^R`k+#KtLrO4q9VyIYt ziYE&aBnyqLyXG(zL4uebuq++$cT27sB#5$#Wx)_OAcPHg*m3vQ26Q+I8|Y>$&|A<3 ziXcHgAXd^#`PIWAt0ktud-;*~zyi09q?xp693_dy#exm1nG4b8xkpHTy=e7;&s{`F zJo>G9WbO;O=bAIPLeDw+<>#z9y5P8_Zp zr^XWiL-y^Wjj%Mq-xQ@n;)s4z7W+g41Di4f6Lkz|WQ=hnm>88ZW(G-1AY}G=(`{j zur!s%$k@^tC-WGm8pb%88JKDuqt?4VnAji_VZD|>$`Y8^#267}vfkPl!+MKp(rCTK z1Ofecpx-FYNTOm;7DB;7DNu4GhB?77C#lhYW0?L0xhbr71~8435i-(vyD<$mUT&-# zZ%|1?j-HL9)_OMuTN5%8)@uorErF*=MvZV8^>|6MVZF6c*IRFC)SCnS7IDVgw=iuX z6fBeiC23KDP<~&NN>`%%#%A&2c%+sw{9@Qi0<$#C64;@1G`V6i35r<));3CD6B3vV z7N=y2$pSZ)z=9=wfMF~B_{KztK24yS_G1#&{UZ65L zJOW3@`kzJ?5WAERSiB0L!yXv+(=fz-!81q_n2-qY1B`*kOm}nfr&kXlMuL2=DY+r^#DQ6TF6;=4!MQY zB?iadaX13s^%@sx9-|oR#t8A<;DzQMsUWf-CWPrJ9&Aq0&_oE+6LJ+q8ta;}&|_xt z@z+$Dr==>`YU8k#kjpvSs4W_&WK}YI;Uh_A7p-7npX?Fz5Gxy7Fl!-_kv$ZVn7u=} z0hc6Wp9byQB9peZQ8N3%3(W&kVM_^OLP$oTBALS?4G75yA({0^W&@J3O`hXOCRJv` zRWjO*R>Wf&$=D*ZSDmU=$sB}_B$>Umf`tXyBU%Cka%(|uA(D|1B$Jp`GIl9R#y$;_ zu|+0rZKGrkfft&Gr2_gOCWK@ZDw0X!4hYF4g^|gHWHurh+vNG0zns0?v`usA9Gg+q znY}#)dsWFCfsZ7a{Zzr_?XowYd;plW7RgwMWMl-%BxaS2T}qNUAcp$cWQ$DN+D6Ho z053G}lFC9w2x3A=W-slLOcHlMNG2&vH-?oK$=D_@1V|=ThUugQEm>43av7VkMP^TR zC-$n6ISC(0G6$)G%X?&xppi7$a)Mb4k<4BSkxXJ%$=IbN8T&Ly#uk~hwT+Uw54_O4 zUn)46K}-nAC{!eq#2pZl5lYSvt1gnU4U$P3%i4^EAW6nzB&{Nsk&G=ed$y{V%meU| zBy)r+xO`CdB0m~ttp&M-NJd7GOk!5a*rg~bLJ$)| zG71&RByk6XWP}jKRv)p}A|G2)xR(c(lbVD9CHYtkR@J$Td~A`Kv{k))9)_DFpOaL< z>o;UFPE4?DEyyiIJ~D!Q63fcRE+zTgBZhkU*dmj*woyK(!3)hNq_R*Ef|wBUIYN8n zlY|}+@)1H5vp!eX{&nsX@?u^$FtKyr9%$m&}2Di zEkQD}fMledT&c0DSc;=l;m0-}ol1w|PH;h_OUw%uAqWT|4F!obl86IB8c9icw~nsjr+=8$PtJA2W4zBz*otg;CmV69&#j`I z{<5{^jP2*{fO{^j9)Et0S#+mT;udg$4149tJL-j00(+KlLTP>UB&Q&0nQ++oqAgr7 zaj!>cl6d8s6KlRsEUHf&^Ob&a5S8#jR3i6>k6|&;Yml}l7$+iXK`$#q@*H1!@mHQ_{c^5ylaD zO_%GKuK+IosAco%k?3>DB7Ib+!k=RLfv9FGV}WX&kATFOM>BMpf2q>AZ0ltY!^E(c zi%=Len0(n|mpu-G_2PMW^}f@Wa?>2zOqUAtl?~QBKG#y=z4A->^ETWntUhqMYwmHb zTvs3XdRMlSxre!+_B+}d4R|i5Vf)lJhdbPbIl^jU>#p5Azfhl*rz_fV>`Gvs#JQAUU2+BA6?Br(|f?QDe{i{X!R7+ z3zB-P)~)sTgJ`+*f*4#*FNncS@JQO_Jp`-c24oZt5;BzC^XN%tC@TOHs)C5ptb;+S zdh;E^8b2GgS%l;jh$SF|CR9>&JF4dH|FXxB!Dg840Ov68G9jxXe?X;c0AQR9j8kHy zh3zx_WWkYjnl2L6MqDP<-aM$gaW~Q*2+qAoOk{ZRKr+ZK<(CLRYsNB?2Y8@*Ayn@* zwzuP|-kXU+^#eqYy;)H8#^P=G5OwEY7@P(r2AquCC0np{a#PF03UJP=gt@6;nQf`+=pV_OReI=@U7!2_%<9M5IUnn)@un z%i)3Mh0uJFO`zt7V6f19kxif`Bb#6yABRn;1UIaF$(zCL%LZ=48G0JX7U0F7Yl>q{Nr9+~q%dTBP*ga3_74bbLZwlq90GcpH?tY6=7uD9fd$1} zqYkj8Y{G=`5S=j!^=&)&bcE12EU9>*pxB>VMCtu`LWkkkGD^_Yh^svfdP)sy2?Qfa z`%;6%5gs8P69}U0cFTe++7|+6v17M#6{O3Qk5X*k+yk+Jw^)SE8chPL-uKwtWCum6{`kG&sE-s`uj z@vP}iFm^4qH`gkPy%}6X?$6+h?T1m7lyGiS{v@SQJ(8X|CuWN68P{W!$49x*pOFLJAWy!r+I^ge?oHpUh z9|Dy5$3+>J455D^^k3w4sQ+$wDD+?4>8pReoI!dX3jLG#|M4%r}H6gh5n&m$u`hk(!VP_w9p7iU$!yPGrt;K-l1urP>Ox3cw@XY(|a6HN?brs z0t~X3N45ihQmtp99cWQcG4WqiGwFMQjJjVm0hR2a$nS7=58|7-b|ER~^k4RH`fPv; zw`$fFu!F%s>R)!Zi;WrKNHXt|ORhe`B{vBcAB~R&WS5-Dfx>hMqjydJ#hX7OM+QUE zNOD2>0+bC~7F?iwA#fE59V)+v9x2No2HgHzp`-E}5<1BEZ-4p152O6~P<~$O|K0I5 z`{~h9C_gJ+tl!#^+r2TiHl}Xf>EeH^(}y4JJIk$cUZ zkDheHML+FmdrXncSJnkaA;cwAQo5Ui3Uc}n!{-nb2Eow>6_4LrsMpos%!ZMjFwWrM zUWJ@{yPSKTQ>R>={v&Enu>ZAWqdqQ8*+8AnHskPR1tj?VeeLj#H2TLlU5Dhh-)ZpJufPb(Sb?`{6eKkYw#qNx-H2k^X%jz>Vz%gWV^-F;FDEPQG2X>9LaW9V`c1F zDOP4#1k2KfZm_I^lm&9Itg{p>OH?>F%VrTV5kMIGEaTkFgV`wrJC`xN|u@W(${SHX=n}RJs*#RC&)tsKK21&9weKR&SG+)F| ze6?y6PTW3<_z7=Wqw0gA;=FACfDoHdN#mU%a+5&A5!5qd!hLf{ol4v|ROPWM^%Q!jn zK>tG88~8jTs{bMI7WyyZCh9-pCiCg;1cC7u#t;1d1;38|Z?EZpWct+Azxw-r?o^rD zYq{vjle~qYUk*QMrvg)B;DlM5X8H*S;($}?z2iWTJhI;X$w8BaZg(wX=5 zK3O&AzNdxH>7aT6r1PL<+BZFNt&3~lcBkK@bU`hvz8yZw2k@%uYZb+@EU7-WH&}^X zZTaK3!>Jy>9Sz5C9@zl=DUv+#6Ef5^l#k!K)r0g%2c0;950|6m4~vjvw^%rY`v|CH z2I&n9Sm;G<2G1(cOCBY&K808E%#U!~X>hP?C$}>bhQJfjF>*#sCGXqS$XUY0s#h}w z6jnsemU{Oh%?;R<6eLzWr;rdvAV67*WkD7rNKklZ>1>|7v%ch=olL%2pMe_Gp53$l z>EoN{*5c%P`^^PUwe8AazS6-1 zWI_<>1B)dn%|CADj-3wA;7QIMeTyS@+L5THs=@*<2-Z$I10_q+E8^rUW@kd#^>%~Q zWs0D9`QRNvaXtR0bnpf_GU%2@_23<470ZG=%1uys%Z9r;lX5*(7=Bq#OmrRjY$y$( zb>WlpH6#?j9!+n+;>cQWNUNkzy85P9W2!=r5s>V zQ=a})&R8D^^n>DDs0e{th*7U+)EmscXVe=QIZ|3?W2;Z12ZhqvzyY?QtYU< zG@+KV;l>FSX-6p^Yb)5`V?6lSC?9cG0Xr7Lj)jc0$kwmria)(Hd8lm)Y7^q;;2UIw z81*Eho?_IKjCzVuAAvzw()VN3lZ<*C%}z1ujcL^5Xm;Z=njO!YjaNWQqn?dXZw@}T z$Usg4=NujESjec0Z2ic$txn$jrzh*v(8QXj%npD0LQg5NXfmixiJ$XtkP-6yOMB10 zJXqd@JSp=)0;kr_0q6KmWH*B(wGgJJEVeegP0J~P>@JNAtH^WtbkzI zbqOY2$&iM1&QzpfYo}O{Qw#bsPwox+sgXRILkUJa1gpiCv zMKTH$$p|4CAtbZjbrB1(6thVh%i0WE!A?SvFs~w)w3g`DIH^l3$zFOQnS;UQg6u^s z5#$zv+(INHBS{*hGeHtWVi!2#y8zpmJ(7azNc!5Do z2+1f^B$LD)5Ryp>bKhlU#b#`S%_NOwZHDP2o3R*4tH@<+#uk}9TQ%Cu5z3Iv1Ht8k zvKL!F%vuO?3z3YBAeqFhlCft=GPjGUem2=6leV@|GK)d;A*ql=AH;-^j6y{+N!$S; z86iZm)ko~b$j6ox?&ZOpN=@?Bf~$?i3_*1+BOhC2CT-OypOcg!pNE6jZ^&dg1|YW( zwFQN4U@kx5(ID4)|o^9iYtL?6V2kk1j?BcCMnfRK+6qL}p& zD=zY}C51_QS+ikA$$pY`g;JN1k1aBj)rntPbMB)W`K&mf$3mrr2c++pCQ0-{B+>Ig zK1V1-K8a=JW6zR&?9(70TV&GKHp-_pXtqg(@Ieq0LOzNX`6QtSgnWb$g{vq`x`HAf zTT*zC2lkVigc&9ISj-R>cq}6yTVy6}Rc}AI3kat@810Zl&%%T!f>}$Dj4U7-X@46@ z^g*Oc%nOyyARvS^6eQ9}A`S>?2r=+OK4P0j8n&eH2oI!@nuO6LX;_RjRN*qxutk=J zt(KBTcQ9HBMuk8rgd`-7B&2)Z6EC~_D#biC*kPdF;dC%_~Cw6xxzkgai4C(E9Qf;SeY=hG8)g=!zo%_4*rq!imhW5gAOk)tIjUWuunOr$?gCb&2#*oeEEw5>|LgmxEoz z{>te*=^K8cnyHKhs&zgB5@R0CK)M_>{l@h=?u7W)cR$VFAiES{9^;9|}Rwyo(NGG1MIS z8bkClO{nLhxWH3yM%isMj^s()_=g|5{by;lkyh%2YoRCpUGGm)KT6y3wV>B8<*W^^ zeZVkE%UVrwEm%(30cixs3Fy~d3p(bc>rFAd_#}$}!<*nC0}RohB7tv{^Aa+2-kom; zQuH29j9m9pZdzSL@i&W#+kN}bRD{4LB;tgPdz5r7Sf52dn6v03YRn||o6yCz;A9}3 z5*xx~_)ja5-t;IJzCcjB9%3BnS}>6zt{|BrYexXl%vd&Gkpna@gyx69=S=ik(Cda) zdYQ6Ue5lb|p2EI2AuZma_~Ix3um4P|30jTNgLfz{dfvdBz+qyX+8;qi>i1i}`qz^B z=y~qdf6VS#hfuxxcLZGh^N=V*^QRaReXfnfgl6mCo^aMP4$4iaBnh5ry-QTM)AkPt zY(k~cn6Kk?mh-|{?fMak^n9Cj$wc}M@dAAWR2r2l#t=QtP4X_# z+^OU;xcO6OGDsf3hknHGCoEJ=AC$RF%%ybet0lJ8Pv>!GRQI_ki1Rnv_d0-~e_u+y0mtrH4eh^jk7m4)Zz?}~qCj*C&IP}2i z)s4&5iQD9@Z3S#*$y8VrvgW!eN{QUdCDI3p`^8=OCSH7Wt6k9ZQrtO-^xah_G~VPy zm$gKC@sUU`K3rfQIK+WHCDQv7x#P&&rWxDA6I(iw{_j5V4_C8B?9jcS9qJ;r_NAXa zHVvnJ#he9Mf3d5f%j5Zhu`DPt zJB83xIb&l8xfHu{Iax=)#*B^KDwoA*`q-;p@!<}%y~l#^%J@p+M|!U>{+L49D{~zy zzv@)=ylWMO=M64jnrMNK*nZGMNy*+%r$qV~T)ZmL8iQ-&ku~g3XzEL}p+Pk~oHQy{ zcuAgj2YN1h-d?nKh-#+NZvTJ~s8C76yea{g>Cb7TMEc^Zh@|^4d%IbNPaqW4t?EJA z6-YZpsu&gd5__}zqPpA+KlHou zZ0|fLk-if|kx1`2m)fCepiqi^YN0>u9B3H9{QX;(~nXjx1jAMaOsHAi^ z2Ne{5LIc@gR{c!-6hd*%Q=i_Qs!OB?2UA$H^KG3CVs=KCJ@cq!?xfj2SepHMWI8{k zMEb_pC}K59tN(hCBbIS#R8Qkkw$ZX6j{zho&R-pH^OO_mfAv3|J_(k68iR`k6K4AE z&D($C800*zq-fbQpGfZz7Ag}ltdvOaPZH^Q)cI(mXrvn-P4B8RMajB~M0#;>xV9q+ z0MTD4N6CsrdUNEHKUPbmFFrLt1Ifj^z z%`wCSJd%z%He1c!m$*1La1t5x)kNBUtfB6+69+}eaak-Jo_z#VQg|(oKF?0}eQc*l zrDyqOacD1p#hyEfzvqdHJM+K{gfN5R?5r8+3>9WjL{2mVx4H^FkH~C#m7@I*-u_dVLAv*fxk=2l@|@rJ z0>f%drLRRv8a`iLt7w?(X|Dp4r=+1e@0_@xLUSNgMJm1HV1wcTtHw?I$wdeg^HQyS=!Q?4piZzgRgczT_&5pM?4Zs)1y9oc3Pwy(7&gUkOy=C%33T7vKT>v!bLOc z6)sv2Zbj={WYhN;t$|Y_KfdhPJAM4nsXuwSksR62fzauDH}8FSJAObPIhPzMo>n$} z{UO|?48g&0YYf6u=0XA}YqN}X2InOGWIIQHkxlQRX%MC8r4`xq-duRJZC3`Ix_N+1 z2qHaTu>_?JF(Ewrq_Z!0RAe`Oi*t7J!>FdJoJ~*XE2>u81A#6vI}>W_x(gGu5LraV z%ct)moBk=DzCn%*x}{M)eMecvvLKIg6BO~?ez=KjdXEl7b*jRT8L49H?!%3 ztzr2g4Gq?Yf;CGR^|zZ%ALvKLxlkDi)IyB<*~_L6Ce}&aB<_XESTJGz=+ zagt3RsD&8gvzkpGd~B3~S~h*KV<97bCbQ{-+JyMYrVlbgjQUy4rVl>0$Ur1~!OrGj z$3jN^OlH#uwJGs)2o5qr9)j8AJOI1b1>|uVNaxT;q*jq(v{mSt&ZZB}W@IT6yI^o@ zFlZr}MUo}6C^2aEnbGr7_F0OZ*JtP1BBwH0+n9Ye8#H%Fg{fuN0U-?`%wUW~=~>LC4=(q~UL?`N?A~D3LL?)zNG36>WDK;HBx9Jh zK{B?;lCic?GW&z(0jZEg8^nZ=j6y{+3KTmLLNY?S9Z2R`%%%@67i2F2iZFXHn6(he z$R3hO%qp3^z$MA-18k6tEwW^+ZIsNRpm|s-pbug~NJgO|nI!Ijkc<$L8AmeDVm5tn zdAsZpFp&jvS%X;%k&MhDnZ&G;*$-TjjC~p;V~Z>qYa1nVB52+vm4%8BUZN0^*-LvQ zlf)emk`Z!IMKaG~Hhpk;kL(flkp*&DgINoajLagL#H^Az2wakkeHtWVi!2#y8zpmJ z(7azNoVS9Q5Ry@-NG6FpAS9C%_N-^q2bT}ZUe38Dn?9Je5Xs0al1a=enIpg@$=nXu zAQ@X^$ynPcnZ=;_kW|Q~4`M<{Mxi2^B<_HaOj6i)DVsic{f112V*tw!2g?>BA6Z5| ziDl(;61XIvdjK2cV~Z>wYa8WrI%qy26|(7rm=N+gLVM(sgdPy`5n{id_3>?Bqe<}vgw1-N-)|Tj0zzM8ATGZcHW7TcOOz*#BIo?|Nf)<{QRRq+H|X* z?+DGNA4Fu&P&R$A)+K$T*?`=L{7L%0 zoK0_i3Mdwzqz47YFf1kl9X`mW=YiP_2_bz?PSbd_IZ(~GBeL#XEAFlmxY zbt|8dYeg?3r&T#`D+iQmIaIA#DsKm*PJ&^`mA}GL z+=DQ@O)3N`e zAv9l3-RS{uk+)U6M^Q`NS(iRb&wCX6M}G5f!ClIx|J31wMxPtA>1)^g4YxwIin?2& zu%suw|91%0`+rBk{XY*BW4f*hi8x_nZqVE7EOY3b)vh0rP0t6Cm&~RQq;z-)SVo)LPPAylLWIFBPW zU|q_lCokY_Kl|Cw&}u5rd6EJ?+1B++SW>Fx`@qJefZCj|)hsjTV-sVVmac@0Q8K*! z4u#strgtPHqwsKQGDLqe7SjSu^G?XnQ}I4U%8G_p#~L{Sj62;59~2d5LHm;s;3FUr z=Q;0vpDSY*FFssgA2`H;J!RAT6Z6I!vrRL$cafWkyYqZH^byO?*;?=W(%U}3 z9sZ6`oV)zc^r<)8x#ul#+Jgek`*2_E#llr$?Yh2r3nnsf%QeNk^;2nW$Tm5z#fG%} zO}<=LZ}I^!%!ha+2IkXGc+$u4GihmeV}=#k^j`RyE7VGz*Z5}Bw?;O-sJOKkJ$Lv% z0upf^)lV(+8_$n!?m+k#JN0+(H80)B{k~Vi`B}v|wrq zF~4*my{Uh)WF5D-QYt-fI(_WWADOeYM^)=uu?X_r)AzpA$XdU`P~BM|wVa%Wjk#7) zY|P-=Vsak%V((#5Ev?KcW0--o#^7=)eGIORM^>*tQJs#mu9oVJmm|c=Lb`k^y{Kj? z?e?FqbodCUq~33qN{?HCxfJ*NU4gMvRLW3!=cV~uzsc83s#t2ySSgcMI^ge?T=gbD z$|{y|lg|U?3!!}P?)9ZqzIXWM?sU@A**T0i?q7X!ck8F;A$3jB^sy)Y=xaZR@(;@n zI~sk+qyOrMe?>o~>W_ov>E_X=>7WapH@uPs6k}zOJq{=(B%ve$%6fUgvU{^Xsn(}x z!w58|bMxp2nd#@`(Ti%P(r5qqO23bQL@YKiMIL=MQE#bK`pCy+4R^)TB3L&D1NDG7 z6{-qg*(oJ;q+~s{vTQc}5NcD<%VyI{V@G938c90Le*wydEekF%0U>aya@GB0+4M!a z)~+xCvgyZP_WUa`f%z~2mwHU#y>I*7NiaPN+4M2E_V_Hlz)WQ%1~k{fglN0KP zps_*Z^eE|Gr#SQx3H9YyU6)9x_wCA|)V*-d6G``g^&m&zM2PN={B;}8EfSoUcTPB&PezPn@c#0=y-v7~_6GoMiJ5J;$xVWot6f09toqs~n; zRco1qdU0^Rwj((J(O)PB%8G>gWxh{O!Hutxj*Y|c-$R< zho_iOFDg#bfok4IKw`|JiG+GryZVHB=WI2hUOWwMSx%_8<{3z+7Y(OoIjo+VqimmL zoSJzsF@=~}#i`jynWyIPnE1`!?7nTJEKbeyrG)yPOW*b)l)T{V^bE;4VfxhLXK&p_ z4-@K>va?ufC*%et*DC6iT=l@62~U<#?{L!55LN7VG*}&vq+?FbR)=q4YGp!(%8Qfp zH%+J)Atz?BaDMg?P)XrWE1^E{9u{HYe4OL1Jp&2#lHvF)nIt2R&rx=}WgMS*Ut z8SJE}IAhzNoGBjxi8x_X0C$8a9m2KF( zGZpwk;BY(aDK%*P!;Ys#$}zpIkdVi80Loe{cAu|w@W91s z7mGd(3HAI$X~oOLMMC``WltfYp3YZPx2}{>FJ@;#ZGML%`-~*igB(S6OQU)WkFtto z9K(5_+yq64*X{f}nNTnJg-WmFxnTA|LOl;`;=7qpA8hr@Pfn;0)-1s#|8^7V1O1>l z7b*jRT8L49HxufEtzr4e3H8C6C5-ypO{fp_qvBksj09>SM*Zw1)CUvmq@EM%gAq#@ zFuM5cIGLX)pkI0+*P9@X_XEU-?OQ;V9 zEhM>UD@>4Flo&Jt%{;JD0$Pfoe`^W#L34*x$WRYrLP%p4K&#-e&$FsIE z9d9>yp}9vY2-gQ$={%5zqDC5fL>ds%5MtnCEK1K}LVa+#PxfjF^}(!#NJeInOk!5a z7;7y_#z<>}WNeWoV{M~k_6N-aQX!8vhzTJXg^FYpD3TFEGD1jZJ(77A6Y7J@1=)*$ zBFr8PW-UZAvWH|6vr1+!a7i-z02?G@i!2#y8zplnXdadd=!2LLl2NEgCW$*BBqM}m z#*xgkm{1>F-Y$CtOk{y{Rw2?^c_0~?MKXz5C9@y6BpLfONX8afGS)Uq<^*^#h`V@z zP6%Q`NMZYn%$^KpEkrUhi)0eBO6DMNNiz0nkc=&|WUOtJ z%zZ)geyMOl9>j!@j6y{+2SgeWHY4N$tMkKpYa15fq_M2cSO_>-h9X@gX+(f{sXz5( z$HvKC`n>oNsyT5y07J0wAP*#SJB1*(5abpj85uz`2kf9^j_{)-8T&Ly#uk~hwT+Tl z1TQonk_rj+J`N`c$tYALlf)emk`Y1_TYZGfgnVpC;a(o!$D__Wqsu;CO4W z)+SHH&0)_H*s}y@^Qojrw~K?wf*px0M1LXs!+9-5+Q4RgezakpT7I-2Tk51EVIGHE zmmJ+CUcxMPK$j;F7`VRG{OE45(7PhCg+z<7iW-}-4ys=Jr24q_d6N9-^N$_545{V( z=sOPGdUd!gwBhe=SE`6dyo$#>q zMQvk#bib&$l@C-x;1?2O9!)JjIxc(+A-6)I_%csUksc6Lap!|VkxRO&x7g(9q3Wl` zxsT0%hj^&*Q{%%#T2=KDpBhj2xsR8};x1MINeo*yUm4^R#Pwu;qfJB_fQ%j4^u|32>P`Aj8=8~T1U|;?Si|WT_ z)(74VB1Dpmtb>uoMS9%KY9^tFNRsVf`S){=#)>0J78xU%_0|5hT%05o_)S!}ftN}W z4&hw^gtO5yZs2*Kbs@CA9(+#0akOsOc${A9vq+5I$fyg$-UPoSMo*r4&sS+>V)Q0@ zaF=`28$S5I!C{)q1noB=r3v8Lb!}ssU9G0jdZgJ!GEi}oJ6@;t&iJ&vgBY!hC#`RC z3?#$zkSv4qCp>wRJ3+IA3GEi2$|+K#9c3@&e$;F{CD?5i71#6jCmG&HK&A1lN5!TN zIxAcOswQ=?92Kyd~c!%R3WUNkeu(cJK7;$Nuqn}Ptw9&%J zm1rfI6rV+68o;fT5W`-XPNG5Q0+j8uEC{1>A#iR2pHp(O>Fe~~s&jzi@E0c2UmEr= zB~FniJaOLd?LP54wCx1L46QI?8&BW;nGe400*2JZkSwRSnHmF4^QrzqOwCf5r+*bMZEI+iKlfSd2PN&EOq8kF< z1nlhJKyq}<@+~~Oku6q@&gJ zsg~WZy%5Gt4&6b+B0z@#ISpCDh+OmPKR*7=(xr9jYJ1$|5|jFbgBYz$rb#UT19$N# z7~rDb79c73sNK_c=06nDm@b|6$+ViE6d~s+778`6J{k4d`Mk(v5q0 zk#UCR5h7tS3sCl;WkHU~EQCc)f@-SJv+Gknvt{*CLY9ty+c;^oSe)lmq>nkgVfxq$ zuI{=B4pPi7ECETANd1cEUiSh9-=D>ljiM&4@CnO)Tww{dn(Fcnmp%A$E_AW=?MOqe zGlrhDsV_Rps7>}D4DE+_)a*eB@vP6>uo&w@ZJ?ckOhg!4HO0CsfBV$!*an!xH$=;Y zy@&!razf`=#bo+yxn9at`fT|WbAJL7lj*bNo1FXTxJO$7?-POdw0PBw8e$_#1FbIk zskGSUr^-uyvXodyQEqn?MR})ZCxlliFy0lbAhaWmDt!`DnBo?TgBxr`D6ihInAXa? zGU!c&#h{^a5T$7z)`H;}XjEXxIrmCoS}LZG&3v%M1g{*h?W8}h!2kZhU+HIcv2AuU z?g~=V@2^lcUiTsM)>L9j!AaY&FH~(sI{}-o56wawVbylN!W#$&ur&s6t7I#}^lIZF z0|>yM;MawzU?tk#M)=aV9+z%1kqv0HoPgbmkUB&)Q)#zBkQ9cq9R*=`c$WH%d8{)U{rz20>mS@kKh z`AUD_6+$CI8E-MRn13(IQ{D4Xo_itg7ljq!Y9zN7F0>rzP4U#h)T5lK41gH38Yt#4 z=lG!LG(N|Uk{$~?vc)-e#Qu&EY0POI$f_?JQMjDaMS4RLOQK|#5}<527?2MvC_$lB zW{D1odT#nURN8T7lG2$y$?+*gdL*gL zr!cfloglQ?3WL~-vzu+qHdjO&EyG=ip6i3ihD^ks4ORd?WVL)u!PLT~V+t{Q#zoD5 z(|Vi8?Wk;|3mWK`pTEDlUQeD8aLm{+eSGwJ?R(Jf0Sf5h zemOOL@9ppWS63FPB1>p_GGSb9{!c=63Ow~ugBZc47y(fOv?73RCoH22T1h`~M$u#^ z(wCdoNAH#zI-a1ka3(e(cXx^{q99Bl3PuQ0e}c&hCUAHrF?%~~k@cO_R#mprfP)Qa z*r|tVEdlQ&ShAuaHkGN^^c>ATS?Iw1H;CC)Hd6QEt&ME`K@Ndzl}}0tMpaF@5i?_k3owNSQfD z*MTW-tIN;pm>{qrN|aWP9#m&{-b>l{Vtm9A(3b$b?1=H%AMGV594nMQnWp(BOrR@s zLg7@N>7A%@lhPFrdj=1%3L)ola9QH1kv4n5jt0`!VJ))j&PKA4X>Bz`I*K`6vxD7% zd{4$Ij&lq^Y%#TQsLlx&npC)S93eb|M1eSh6smCqsS-!9j5q=hR4>G2jyOVWWX$k< z;!yQ&G&z=#Z*4gIP9OiF554^fRDV0Qikrvuy?ZXd>CLFVlTR!g-mG8D15*_<_4P}- zepf7>Feg6Hrp2=yIEo!hmZ;tDK(aZgdSQwWSR5!&SAxPfVjtd0&wR+7L^Uoet(m2~ zOS=%Hqvk=e5n$i}ZXr~>35=GYF+&MPkv6)?(lAZ>?5$`YRBZrRXV!YQhrG5Q3gm|~ zR?(ef0IDPmVB#DD2;+jP5|XbfhGG%eOGWY#6DeK|kW^Jzyd5A1Ztxa^S$($>E=VtsN)+K+zuIWTcDc#|l+xv!H}AB*Uh>D;F4-g9>m0dIisfElASJ=IjASW${xiXR4&`|+8xpHM~B{g>A ze6m5FR^V9-au3z$$`m3*@;W{SH2Ii1c zY3}Isy*n-*b`_}3A5Bs$si`w7W=>2O4J%cK!VA*`D-F-*NZy(z%MqHzWH~L***cso zc~yUcVl-%NvdIh|q93X>SkN$8#3*M;z#@TOnk=y3+{*JM@y8NGNv2w`7^LNpp3g~^ zx<2O2iq+M|Y0elQYM~7NtV;Z4cq@3}Z-xh0$}^|lsj8njv5MOl(M4RVHgoVFQ$VXV zxUdym&<^sJ!AzS~tocw*KRrlzXw~I3EV7|E8ZdR7uADk!lS;cyyS=e_@c9#YbnpO| zkEVw~?2$ybd;G%35wSt+NgODG(5v?y_4{N7VRUdK2ty$1PE7k~7b0(kJB3k-#W(=S zg=^h-By@W2sC3Fv5sb=*6iB%3O)~kMwzZi#m1iaWx@3QbKgO9xQ4z~bAe&Eh94@S5 zZdk`j`T;O;l75f}+lo-SzF@8JCzFEGhy587n9b%JLcs$4PYZEW1IH8o+>%cC-h6E2 z7NfI?A{50XXwy#5R)6A9QcveLp~y3(f48kkm*BDO zAGU6rskHNJ>o&B;^XxX%#&gB$kN)wcXN9c2Y4u0{00@x|D3ciQ>RV#`V0N@~XV#pZ zU3)$weZcO__uOuF?Q?2{?bX8bYK5J%Yg^_Xo?Y9VV%Dv*YgeWp?Xzo}(vQy7x6CIo z&Nj_nyPAGUq%2Sng1V1P&!4kvSLG&iXDF6p6hAK)uQ^LGG~1;4+=Au>1HpqTgE(y|6}9tW)R7XAN9wXPOf~~=e|C>_LZ~O4(!}l>0!41 z{F)=b@N>7lW7p1G(9?>Yf9n5#>Sum>=f=u|tS!B)K7A}K{g3|T_RXzVT-SQp7q<7z z^;foUZoA^Twks~b>aT-TL;cWmF>eZ_U%+kd=5&yfnXotK@O>)Afmx~8MD9YSMm zbDLY&tb>GP#@gpLw*j>RwQe8lnA_YA)CSbHeXMhCa|cj6Q2X|=t}7;6fN0W5LC4%= z%iQL!?PJ}PS6qnz*y{#e^ z#@`ZJ-}>4WTVAm3Skv+ISBU5FG=wk1?N@#y>5E^-&u{w5j*>jywD^^4w2PPg1e1DS z<-vc#Ft#W2I8Yh%JJm${<8##mJ{Br{KAP}u+W4`P2Y+RH@ndJso}K%$P%DKKmEOf) zr9^loLYl&+F|AMdcCNpBD?5oxGRDPMSu|K-&a^Ck(XK3zMGqC-TUp$H3B~b+BAYRU z#ZTEXJ_eIaccyZ{PU$t_&c&<2xm{0IQaG6+d7DV$dm;%*M`dxdNCGdoMw>x^trbXC zE`j7ykz}%R!jN7PJl};WQ1g&TP)32|(|i+bdjltbadDMwF`U0cg7g+i9?p)I+HfY9 zKr%0q`r-VxNUFo>J+FQ^S1f_#4I-%@&IXYLUTZ{B8qQbMeEo3#)Jd>--nnD?-gCw@ z@ticK#r#r>CqQzIbvfUmY)NoeZ@crAq*0k;H+0`?@gLQ1BltwGIJh1cEM<)|6 zU4Q@IDA?6t55Eg)yvWqLs5+)Lj}Qjwtu&p?W_9xP>RXTOY}tDBf%BS9Y8s0_(HUeU zzWP7>HUDpr$TbCOT7YWq7$_4ywRQ5e;sNI`9+_-*{S;*nM!+bNavA202r-T5BpqBX zG{*ue_ASc=gN;{J(+t|Df;cTbCHu(J#NSJg!rxAHA<`So6qO{ApOfa=Dn( zuDyEZ-}<|LHodc;QVbF7O(##C{>3A+#h?nl*e4bz8QPt)|iR%40XQ zv4(U3yv2oUarCM)nR3-%c+=u9Q#gIwpIn8k_f=Xz#G>NiU?*SA-ue0-^kzuf!^6(^ zjH$)#l*}>hU-O|eXO-S_KA5(sE*B1}7jOPGsBCMyn+NpT#5GpwPk-Kjg-MMX48OlH&)2Ix==)$Av(56mS-*W8> znFFhDx%-tTxBOsI4IQ=}Pxnf;?WE6|BdFGMOEQCZ>?-7N7kIw%XYbfm>_6;q^Y|wl z7{;c>w+(9-9~;J!Cdd}&=(?T1mrU33B)Y-M&e!#*9sYIh{6Np*8-5uxa5++$mP7ox zyD1|Eu=8qTOBQz}-D3+|XHxS?dD(VE-;~^QVkWrQpG~&x~z5Hn#I>3##k* z>u8gxAZ<*hNBx}or9`OE!HhQ*(> zKjPT#@!jzw7DP00+miCS;!5T9zAK-qyk1zNSe4gb{YOTRtdJ8-8Vc!BHaJ|e#H?BT z@okC!B(_w@m|j8IHOt4yVS@Ka_pp_1&waIBboFZ68b2$)e#`^ z57>GsRyo^om_uG22&8lI&xhpiqTsKa`Lg(P>=Zk%>e+TYEv`3xAQp!Y@C086L+=DZ z7iYwC>=lDv+?Md&Hk8J@8Gg6OtMeT0Hi`e!JHSLP^3D%2340W+lMtI+{FpVoLxPI7 z9hJJPH{ClIFJ{-$ezK(Oo_aAwUkYP8$wWzd!lXs?vmi=S)*XR17dQw_FYgd{{(53T z-Nv+rty?V9>|AqY8KM4;WlNb|qrPkM-1b`KdE0B#_>(-Jhnt5y#rN{}C*``10#$$% zhz@0)XdBOW`zjk*yyy0$P~`TRbYHZ1Nh0*K5F*)jx0{7Ffp$cx|3t&R7HiT_`)OSD z?HNUMVD;WZgWFK&J=lYbT=(K_uaW=e;NQPdQNhsr5R^yXR&`Y>#x%C_UF3#yJdHJG&yROme2Hl<7p68@GrrlBB>Eu~lXD7DN*bUSC zVHHYS9|QamYLm*fALh{U_odeEwADHD;av@7ko_&&LBi>pSkX8-us_?zz=A~#<9geE z^ZB)}G931QKzoiXjKxa^Rn*T8BG`O6ANd;inMZ7PAieyaH}cs1S{9G8k0_&Ucl0FB z&iS7t(zSRMUQ=2RsK!a1RD>_LOpzjw_Xq!7t8e+x3+c0X^Ht@8b*#s5gb^le}GQ5LvZqgZnsIs}K(gjCQ205 zIoGC5e0*r_*Av5~T0UW+|25s`blscNF~^*uc7@yu(S7sBER<&bA-f+51|$Y-0J?B_`wD^ltiW(?4uqNyQ`;d##96 z0qRCzwVSYR(-9lx>tERi^*k-1HAHKvF!8&aKKqLY(oNfoso!UzuhZ#Ac6@ec@3p@e zEnf_&c6x24ffXIsvSN2Wx=8gNie3!<9jQ6u?35M$HJ^gAU)tF9Vmi!>sO@?b4;uw9QXram)iwxj|l ziPe&_htmjBx-xBq~9NC|gh4F5qoK1^G_nVs1#YfLx3@#P71zalB9j8CxY69!#z;!VVCPCeO^j|IpXD2wjfjjBXL!)2Gq|hctQCZG1_z| zLhqq-4`S@CveCxPVL2rz^@w{J)Xa4%jbbR{`=$r9O&Ul=_Se87Crz7>j_on9QU}wxOpPhe$;wL@ZP1p_lLPW!ItByy(3~}ON2R* zY2APCe(U?+`m%|)zUQ4UTl>EEz5V(v?|SchUU&WV@4bG-o8I-#_q=t>4cEWD`yFq6 z*Tma5URKww3h2E**?;f;p1*tNKOX*U>z};y6Catm^Y>ph_?~~f{>=R8m;CM<{`Zgm z!oT{FN3LD<#+&}%zx(j1-#l;Aj+eY<;)O5V_7@-j=e3{y#kV~0KQ6xLLzmrn%jBES ze&qT;U-9YpeBasC7rcAL|VU_?BT(^XMf|m|9N)BAOEj23(p(zl5ENU4(?rb z=FdNS^V#3({FN0qz3UY##@jx<;`i_U=d=IM=+-mue*52^z4VEe6~ET@i6#45_~u9c z=*$KG-|wCM<-h)+GnaPUv*L|gn^*Lm`o|R?xN+$0%fGbe?27k%rscl>6^Q@0qg|+pJyHx^3kfR&~E|RqNVSEw@=W$iETl`^)wJ zwpPD^@=esgpj6-f<5AyE*%beFP@hh){O;kOUd#+-Z{{h@kFH$1s_o|H;Z@x$U%#sD z%2ln$q0W*iPlx$;h_+v}?eMChk(*n#owxBLD?i%2@)N6CK6A(M+oIirRB!&_GiP7O zxEk7hwP-g+l_@uyUV7&2AJ`7sACdM)o7b*tzqw`Ys*Y{vyU$E{$oMA8*btDao_5n`kUo!i&70_4S*3IJs{JdQKX(2F_1&&|ZZAzw(y!%p=#by` z`{+vg-Q0ZjszXgHH(gL0%$3~Y57~w|>J06VUl{Gr(fJ!!9ejTC?_Icr($|6_>3A3% z?LTwo?ET`qdey@#U%&0VpZjR@%`I>K$jX(wLd!!`P0;QH?M_!^*I-}JIc?=R2C*5>AqF2zS0vp5D? zR;+kv!|~vyCgAr^Bmeo0?CZS8p0L8|2?Z^gGS2^M5bT^U0>>n^vAX zzk{x+6YDpfIr|qXBhjtt@9I^HP0fGy+;iw^%NymB5bZ~+wp{LasHyqJ^UvwmO_x2= z<0^AGSva?j(58ck<5!m$)9|Xk<}alSy zZywgu_x%mOwv;l2LIX0CBvX^IL@9+*$xu<6OhpPMDvAbErliOag(4b|NR$*ZG-yyN zMUzBjh{9ga>HGQo?&o)Z@9*#a=lSosKIgj5^?IN8UVEQ?)>(V+wbxpEpL6U#@pX=n zKTybTF1VhG{#XA0mp^~YbaYRVl>e(dJ`u`j5wr(KLAL+>-TYlfQcsbx|0IvyQ9=Kb ztL~5zS;1a+!{C1_<6m+m^f@9TF$VuTGyl$KAmoeA<0RhAwEsgcgh%gE{6F$_llltz zygv)t(cjPgcfTvNy#MNP{~8lx1-hD6PP>IX-R1w?r*(IoTO|7${x9NgSt}4|4ca(q zH2g*T-}^J66I2ix-SAgkgns^CLzh6-El1+wfJrG)hw%H~pFwDbJwE>16O0tdbc8&z zWkg52^B72~2;JSk+Ul0S+iEwBy+CtZpjq)3jk$4fk3Awi=J%HDnVa3+nRL?x3N!@* z&BecHRBZk(C-}d$`8a`Qo1jrvDChbNbEnfvA@eW(nI9Uf0EDcYW!=ASSOS*;VqF$cl`|{-Hn5K>=9ia zB4RD{Z6E(`OWiVYM4;2EA)4?X=vx09U7A3rQ!9ke3wea{{?dJf@+^e5x#+*q)d+O4 z0^QmFK)3#Hx?w_^xDXmL>>bg(zvxE(>pgAneoKGVUqwha5z@c@mG1tpzQFNcdd#E0 zi}{znXfDtxy{Es%dO;!h>wO#itDx@txC`mdLb`o-IYyGvY|$POo8TVq-30%7ejz

F#@Ve_ho| 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\|ABMnOQp*5EEsMOoaPIUF8 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 u+r+ks&Ze>Oe|;=Lip9 getSubDeviceSessions(long recipientId) { + return sessionStore.getSubDeviceSessions(recipientId); + } + + @Override + public void storeSession(long recipientId, int deviceId, SessionRecord record) { + sessionStore.storeSession(recipientId, deviceId, record); + } + + @Override + public boolean containsSession(long recipientId, int deviceId) { + return sessionStore.containsSession(recipientId, deviceId); + } + + @Override + public void deleteSession(long recipientId, int deviceId) { + sessionStore.deleteSession(recipientId, deviceId); + } + + @Override + public void deleteAllSessions(long recipientId) { + sessionStore.deleteAllSessions(recipientId); + } + + @Override + public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException { + return signedPreKeyStore.loadSignedPreKey(signedPreKeyId); + } + + @Override + public List loadSignedPreKeys() { + return signedPreKeyStore.loadSignedPreKeys(); + } + + @Override + public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) { + signedPreKeyStore.storeSignedPreKey(signedPreKeyId, record); + } + + @Override + public boolean containsSignedPreKey(int signedPreKeyId) { + return signedPreKeyStore.containsSignedPreKey(signedPreKeyId); + } + + @Override + public void removeSignedPreKey(int signedPreKeyId) { + signedPreKeyStore.removeSignedPreKey(signedPreKeyId); + } +} diff --git a/src/androidTest/java/org/whispersystems/test/InMemoryIdentityKeyStore.java b/src/androidTest/java/org/whispersystems/test/InMemoryIdentityKeyStore.java new file mode 100644 index 000000000..8a2e1d8f3 --- /dev/null +++ b/src/androidTest/java/org/whispersystems/test/InMemoryIdentityKeyStore.java @@ -0,0 +1,53 @@ +package org.whispersystems.test; + +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.IdentityKeyPair; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.state.IdentityKeyStore; + +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.HashMap; +import java.util.Map; + +public class InMemoryIdentityKeyStore implements IdentityKeyStore { + + private final Map trustedKeys = new HashMap<>(); + + private final IdentityKeyPair identityKeyPair; + private final int localRegistrationId; + + public InMemoryIdentityKeyStore() { + try { + ECKeyPair identityKeyPairKeys = Curve.generateKeyPair(); + + this.identityKeyPair = new IdentityKeyPair(new IdentityKey(identityKeyPairKeys.getPublicKey()), + identityKeyPairKeys.getPrivateKey()); + this.localRegistrationId = SecureRandom.getInstance("SHA1PRNG").nextInt(16380) + 1; + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + @Override + public IdentityKeyPair getIdentityKeyPair() { + return identityKeyPair; + } + + @Override + public int getLocalRegistrationId() { + return localRegistrationId; + } + + @Override + public void saveIdentity(long recipientId, IdentityKey identityKey) { + trustedKeys.put(recipientId, identityKey); + } + + @Override + public boolean isTrustedIdentity(long recipientId, IdentityKey identityKey) { + IdentityKey trusted = trustedKeys.get(recipientId); + return (trusted == null || trusted.equals(identityKey)); + } +} diff --git a/src/androidTest/java/org/whispersystems/test/InMemoryPreKeyStore.java b/src/androidTest/java/org/whispersystems/test/InMemoryPreKeyStore.java new file mode 100644 index 000000000..7c3cea08b --- /dev/null +++ b/src/androidTest/java/org/whispersystems/test/InMemoryPreKeyStore.java @@ -0,0 +1,42 @@ +package org.whispersystems.test; + +import org.whispersystems.libaxolotl.InvalidKeyIdException; +import org.whispersystems.libaxolotl.state.PreKeyRecord; +import org.whispersystems.libaxolotl.state.PreKeyStore; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class InMemoryPreKeyStore implements PreKeyStore { + + private final Map store = new HashMap<>(); + + @Override + public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException { + try { + if (!store.containsKey(preKeyId)) { + throw new InvalidKeyIdException("No such prekeyrecord!"); + } + + return new PreKeyRecord(store.get(preKeyId)); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + @Override + public void storePreKey(int preKeyId, PreKeyRecord record) { + store.put(preKeyId, record.serialize()); + } + + @Override + public boolean containsPreKey(int preKeyId) { + return store.containsKey(preKeyId); + } + + @Override + public void removePreKey(int preKeyId) { + store.remove(preKeyId); + } +} diff --git a/src/androidTest/java/org/whispersystems/test/InMemorySessionStore.java b/src/androidTest/java/org/whispersystems/test/InMemorySessionStore.java new file mode 100644 index 000000000..0d148ab03 --- /dev/null +++ b/src/androidTest/java/org/whispersystems/test/InMemorySessionStore.java @@ -0,0 +1,68 @@ +package org.whispersystems.test; + +import org.whispersystems.libaxolotl.state.SessionRecord; +import org.whispersystems.libaxolotl.state.SessionStore; +import org.whispersystems.libaxolotl.util.Pair; + +import java.io.IOException; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class InMemorySessionStore implements SessionStore { + + private Map, byte[]> sessions = new HashMap<>(); + + public InMemorySessionStore() {} + + @Override + public synchronized SessionRecord loadSession(long recipientId, int deviceId) { + try { + if (containsSession(recipientId, deviceId)) { + return new SessionRecord(sessions.get(new Pair<>(recipientId, deviceId))); + } else { + return new SessionRecord(); + } + } catch (IOException e) { + throw new AssertionError(e); + } + } + + @Override + public synchronized List getSubDeviceSessions(long recipientId) { + List deviceIds = new LinkedList<>(); + + for (Pair key : sessions.keySet()) { + if (key.first() == recipientId) { + deviceIds.add(key.second()); + } + } + + return deviceIds; + } + + @Override + public synchronized void storeSession(long recipientId, int deviceId, SessionRecord record) { + sessions.put(new Pair<>(recipientId, deviceId), record.serialize()); + } + + @Override + public synchronized boolean containsSession(long recipientId, int deviceId) { + return sessions.containsKey(new Pair<>(recipientId, deviceId)); + } + + @Override + public synchronized void deleteSession(long recipientId, int deviceId) { + sessions.remove(new Pair<>(recipientId, deviceId)); + } + + @Override + public synchronized void deleteAllSessions(long recipientId) { + for (Pair key : sessions.keySet()) { + if (key.first() == recipientId) { + sessions.remove(key); + } + } + } +} diff --git a/src/androidTest/java/org/whispersystems/test/InMemorySignedPreKeyStore.java b/src/androidTest/java/org/whispersystems/test/InMemorySignedPreKeyStore.java new file mode 100644 index 000000000..7449eecea --- /dev/null +++ b/src/androidTest/java/org/whispersystems/test/InMemorySignedPreKeyStore.java @@ -0,0 +1,59 @@ +package org.whispersystems.test; + +import org.whispersystems.libaxolotl.InvalidKeyIdException; +import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; +import org.whispersystems.libaxolotl.state.SignedPreKeyStore; + +import java.io.IOException; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class InMemorySignedPreKeyStore implements SignedPreKeyStore { + + private final Map store = new HashMap<>(); + + @Override + public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException { + try { + if (!store.containsKey(signedPreKeyId)) { + throw new InvalidKeyIdException("No such signedprekeyrecord! " + signedPreKeyId); + } + + return new SignedPreKeyRecord(store.get(signedPreKeyId)); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + @Override + public List loadSignedPreKeys() { + try { + List results = new LinkedList<>(); + + for (byte[] serialized : store.values()) { + results.add(new SignedPreKeyRecord(serialized)); + } + + return results; + } catch (IOException e) { + throw new AssertionError(e); + } + } + + @Override + public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) { + store.put(signedPreKeyId, record.serialize()); + } + + @Override + public boolean containsSignedPreKey(int signedPreKeyId) { + return store.containsKey(signedPreKeyId); + } + + @Override + public void removeSignedPreKey(int signedPreKeyId) { + store.remove(signedPreKeyId); + } +} diff --git a/src/androidTest/java/org/whispersystems/test/SessionBuilderTest.java b/src/androidTest/java/org/whispersystems/test/SessionBuilderTest.java new file mode 100644 index 000000000..802f401dc --- /dev/null +++ b/src/androidTest/java/org/whispersystems/test/SessionBuilderTest.java @@ -0,0 +1,601 @@ +package org.whispersystems.test; + +import android.test.AndroidTestCase; + +import org.whispersystems.libaxolotl.DuplicateMessageException; +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.InvalidKeyIdException; +import org.whispersystems.libaxolotl.InvalidMessageException; +import org.whispersystems.libaxolotl.InvalidVersionException; +import org.whispersystems.libaxolotl.LegacyMessageException; +import org.whispersystems.libaxolotl.NoSessionException; +import org.whispersystems.libaxolotl.SessionBuilder; +import org.whispersystems.libaxolotl.SessionCipher; +import org.whispersystems.libaxolotl.StaleKeyExchangeException; +import org.whispersystems.libaxolotl.UntrustedIdentityException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.protocol.CiphertextMessage; +import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage; +import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage; +import org.whispersystems.libaxolotl.protocol.WhisperMessage; +import org.whispersystems.libaxolotl.state.AxolotlStore; +import org.whispersystems.libaxolotl.state.IdentityKeyStore; +import org.whispersystems.libaxolotl.state.PreKeyBundle; +import org.whispersystems.libaxolotl.state.PreKeyRecord; +import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; +import org.whispersystems.libaxolotl.util.Pair; + +import java.util.HashSet; +import java.util.Set; + +public class SessionBuilderTest extends AndroidTestCase { + + private static final long ALICE_RECIPIENT_ID = 5L; + private static final long BOB_RECIPIENT_ID = 2L; + + public void testBasicPreKeyV2() + throws InvalidKeyException, InvalidVersionException, InvalidMessageException, InvalidKeyIdException, DuplicateMessageException, LegacyMessageException, UntrustedIdentityException, NoSessionException { + AxolotlStore aliceStore = new InMemoryAxolotlStore(); + SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_RECIPIENT_ID, 1); + + AxolotlStore bobStore = new InMemoryAxolotlStore(); + ECKeyPair bobPreKeyPair = Curve.generateKeyPair(); + PreKeyBundle bobPreKey = new PreKeyBundle(bobStore.getLocalRegistrationId(), 1, + 31337, bobPreKeyPair.getPublicKey(), + 0, null, null, + bobStore.getIdentityKeyPair().getPublicKey()); + + aliceSessionBuilder.process(bobPreKey); + + assertTrue(aliceStore.containsSession(BOB_RECIPIENT_ID, 1)); + assertTrue(aliceStore.loadSession(BOB_RECIPIENT_ID, 1).getSessionState().getSessionVersion() == 2); + + String originalMessage = "L'homme est condamné à être libre"; + SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_RECIPIENT_ID, 1); + CiphertextMessage outgoingMessage = aliceSessionCipher.encrypt(originalMessage.getBytes()); + + assertTrue(outgoingMessage.getType() == CiphertextMessage.PREKEY_TYPE); + + PreKeyWhisperMessage incomingMessage = new PreKeyWhisperMessage(outgoingMessage.serialize()); + bobStore.storePreKey(31337, new PreKeyRecord(bobPreKey.getPreKeyId(), bobPreKeyPair)); + + SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_RECIPIENT_ID, 1); + byte[] plaintext = bobSessionCipher.decrypt(incomingMessage); + + assertTrue(bobStore.containsSession(ALICE_RECIPIENT_ID, 1)); + assertTrue(bobStore.loadSession(ALICE_RECIPIENT_ID, 1).getSessionState().getSessionVersion() == 2); + assertTrue(originalMessage.equals(new String(plaintext))); + + CiphertextMessage bobOutgoingMessage = bobSessionCipher.encrypt(originalMessage.getBytes()); + assertTrue(bobOutgoingMessage.getType() == CiphertextMessage.WHISPER_TYPE); + + byte[] alicePlaintext = aliceSessionCipher.decrypt((WhisperMessage)bobOutgoingMessage); + assertTrue(new String(alicePlaintext).equals(originalMessage)); + + runInteraction(aliceStore, bobStore); + + aliceStore = new InMemoryAxolotlStore(); + aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_RECIPIENT_ID, 1); + aliceSessionCipher = new SessionCipher(aliceStore, BOB_RECIPIENT_ID, 1); + + bobPreKeyPair = Curve.generateKeyPair(); + bobPreKey = new PreKeyBundle(bobStore.getLocalRegistrationId(), + 1, 31338, bobPreKeyPair.getPublicKey(), + 0, null, null, bobStore.getIdentityKeyPair().getPublicKey()); + + bobStore.storePreKey(31338, new PreKeyRecord(bobPreKey.getPreKeyId(), bobPreKeyPair)); + aliceSessionBuilder.process(bobPreKey); + + outgoingMessage = aliceSessionCipher.encrypt(originalMessage.getBytes()); + + try { + bobSessionCipher.decrypt(new PreKeyWhisperMessage(outgoingMessage.serialize())); + throw new AssertionError("shouldn't be trusted!"); + } catch (UntrustedIdentityException uie) { + bobStore.saveIdentity(ALICE_RECIPIENT_ID, new PreKeyWhisperMessage(outgoingMessage.serialize()).getIdentityKey()); + } + + plaintext = bobSessionCipher.decrypt(new PreKeyWhisperMessage(outgoingMessage.serialize())); + + assertTrue(new String(plaintext).equals(originalMessage)); + + bobPreKey = new PreKeyBundle(bobStore.getLocalRegistrationId(), 1, + 31337, Curve.generateKeyPair().getPublicKey(), + 0, null, null, + aliceStore.getIdentityKeyPair().getPublicKey()); + + try { + aliceSessionBuilder.process(bobPreKey); + throw new AssertionError("shoulnd't be trusted!"); + } catch (UntrustedIdentityException uie) { + // good + } + } + + public void testBasicPreKeyV3() + throws InvalidKeyException, InvalidVersionException, InvalidMessageException, InvalidKeyIdException, DuplicateMessageException, LegacyMessageException, UntrustedIdentityException, NoSessionException { + AxolotlStore aliceStore = new InMemoryAxolotlStore(); + SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_RECIPIENT_ID, 1); + + final AxolotlStore bobStore = new InMemoryAxolotlStore(); + ECKeyPair bobPreKeyPair = Curve.generateKeyPair(); + ECKeyPair bobSignedPreKeyPair = Curve.generateKeyPair(); + byte[] bobSignedPreKeySignature = Curve.calculateSignature(bobStore.getIdentityKeyPair().getPrivateKey(), + bobSignedPreKeyPair.getPublicKey().serialize()); + + PreKeyBundle bobPreKey = new PreKeyBundle(bobStore.getLocalRegistrationId(), 1, + 31337, bobPreKeyPair.getPublicKey(), + 22, bobSignedPreKeyPair.getPublicKey(), + bobSignedPreKeySignature, + bobStore.getIdentityKeyPair().getPublicKey()); + + aliceSessionBuilder.process(bobPreKey); + + assertTrue(aliceStore.containsSession(BOB_RECIPIENT_ID, 1)); + assertTrue(aliceStore.loadSession(BOB_RECIPIENT_ID, 1).getSessionState().getSessionVersion() == 3); + + final String originalMessage = "L'homme est condamné à être libre"; + SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_RECIPIENT_ID, 1); + CiphertextMessage outgoingMessage = aliceSessionCipher.encrypt(originalMessage.getBytes()); + + assertTrue(outgoingMessage.getType() == CiphertextMessage.PREKEY_TYPE); + + PreKeyWhisperMessage incomingMessage = new PreKeyWhisperMessage(outgoingMessage.serialize()); + bobStore.storePreKey(31337, new PreKeyRecord(bobPreKey.getPreKeyId(), bobPreKeyPair)); + bobStore.storeSignedPreKey(22, new SignedPreKeyRecord(22, System.currentTimeMillis(), bobSignedPreKeyPair, bobSignedPreKeySignature)); + + SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_RECIPIENT_ID, 1); + byte[] plaintext = bobSessionCipher.decrypt(incomingMessage, new SessionCipher.DecryptionCallback() { + @Override + public void handlePlaintext(byte[] plaintext) { + assertTrue(originalMessage.equals(new String(plaintext))); + assertFalse(bobStore.containsSession(ALICE_RECIPIENT_ID, 1)); + } + }); + + assertTrue(bobStore.containsSession(ALICE_RECIPIENT_ID, 1)); + assertTrue(bobStore.loadSession(ALICE_RECIPIENT_ID, 1).getSessionState().getSessionVersion() == 3); + assertTrue(bobStore.loadSession(ALICE_RECIPIENT_ID, 1).getSessionState().getAliceBaseKey() != null); + assertTrue(originalMessage.equals(new String(plaintext))); + + CiphertextMessage bobOutgoingMessage = bobSessionCipher.encrypt(originalMessage.getBytes()); + assertTrue(bobOutgoingMessage.getType() == CiphertextMessage.WHISPER_TYPE); + + byte[] alicePlaintext = aliceSessionCipher.decrypt(new WhisperMessage(bobOutgoingMessage.serialize())); + assertTrue(new String(alicePlaintext).equals(originalMessage)); + + runInteraction(aliceStore, bobStore); + + aliceStore = new InMemoryAxolotlStore(); + aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_RECIPIENT_ID, 1); + aliceSessionCipher = new SessionCipher(aliceStore, BOB_RECIPIENT_ID, 1); + + bobPreKeyPair = Curve.generateKeyPair(); + bobSignedPreKeyPair = Curve.generateKeyPair(); + bobSignedPreKeySignature = Curve.calculateSignature(bobStore.getIdentityKeyPair().getPrivateKey(), bobSignedPreKeyPair.getPublicKey().serialize()); + bobPreKey = new PreKeyBundle(bobStore.getLocalRegistrationId(), + 1, 31338, bobPreKeyPair.getPublicKey(), + 23, bobSignedPreKeyPair.getPublicKey(), bobSignedPreKeySignature, + bobStore.getIdentityKeyPair().getPublicKey()); + + bobStore.storePreKey(31338, new PreKeyRecord(bobPreKey.getPreKeyId(), bobPreKeyPair)); + bobStore.storeSignedPreKey(23, new SignedPreKeyRecord(23, System.currentTimeMillis(), bobSignedPreKeyPair, bobSignedPreKeySignature)); + aliceSessionBuilder.process(bobPreKey); + + outgoingMessage = aliceSessionCipher.encrypt(originalMessage.getBytes()); + + try { + plaintext = bobSessionCipher.decrypt(new PreKeyWhisperMessage(outgoingMessage.serialize())); + throw new AssertionError("shouldn't be trusted!"); + } catch (UntrustedIdentityException uie) { + bobStore.saveIdentity(ALICE_RECIPIENT_ID, new PreKeyWhisperMessage(outgoingMessage.serialize()).getIdentityKey()); + } + + plaintext = bobSessionCipher.decrypt(new PreKeyWhisperMessage(outgoingMessage.serialize())); + assertTrue(new String(plaintext).equals(originalMessage)); + + bobPreKey = new PreKeyBundle(bobStore.getLocalRegistrationId(), 1, + 31337, Curve.generateKeyPair().getPublicKey(), + 23, bobSignedPreKeyPair.getPublicKey(), bobSignedPreKeySignature, + aliceStore.getIdentityKeyPair().getPublicKey()); + + try { + aliceSessionBuilder.process(bobPreKey); + throw new AssertionError("shoulnd't be trusted!"); + } catch (UntrustedIdentityException uie) { + // good + } + } + + public void testBadSignedPreKeySignature() throws InvalidKeyException, UntrustedIdentityException { + AxolotlStore aliceStore = new InMemoryAxolotlStore(); + SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_RECIPIENT_ID, 1); + + IdentityKeyStore bobIdentityKeyStore = new InMemoryIdentityKeyStore(); + + ECKeyPair bobPreKeyPair = Curve.generateKeyPair(); + ECKeyPair bobSignedPreKeyPair = Curve.generateKeyPair(); + byte[] bobSignedPreKeySignature = Curve.calculateSignature(bobIdentityKeyStore.getIdentityKeyPair().getPrivateKey(), + bobSignedPreKeyPair.getPublicKey().serialize()); + + + for (int i=0;i> aliceOutOfOrderMessages = new HashSet<>(); + + for (int i=0;i<10;i++) { + String loopingMessage = ("What do we mean by saying that existence precedes essence? " + + "We mean that man first of all exists, encounters himself, " + + "surges up in the world--and defines himself aftward. " + i); + CiphertextMessage aliceLoopingMessage = aliceSessionCipher.encrypt(loopingMessage.getBytes()); + + aliceOutOfOrderMessages.add(new Pair<>(loopingMessage, aliceLoopingMessage)); + } + + for (int i=0;i<10;i++) { + String loopingMessage = ("What do we mean by saying that existence precedes essence? " + + "We mean that man first of all exists, encounters himself, " + + "surges up in the world--and defines himself aftward. " + i); + CiphertextMessage aliceLoopingMessage = aliceSessionCipher.encrypt(loopingMessage.getBytes()); + + byte[] loopingPlaintext = bobSessionCipher.decrypt(new WhisperMessage(aliceLoopingMessage.serialize())); + assertTrue(new String(loopingPlaintext).equals(loopingMessage)); + } + + for (int i=0;i<10;i++) { + String loopingMessage = ("You can only desire based on what you know: " + i); + CiphertextMessage bobLoopingMessage = bobSessionCipher.encrypt(loopingMessage.getBytes()); + + byte[] loopingPlaintext = aliceSessionCipher.decrypt(new WhisperMessage(bobLoopingMessage.serialize())); + assertTrue(new String(loopingPlaintext).equals(loopingMessage)); + } + + for (Pair aliceOutOfOrderMessage : aliceOutOfOrderMessages) { + byte[] outOfOrderPlaintext = bobSessionCipher.decrypt(new WhisperMessage(aliceOutOfOrderMessage.second().serialize())); + assertTrue(new String(outOfOrderPlaintext).equals(aliceOutOfOrderMessage.first())); + } + } + + +} diff --git a/src/androidTest/java/org/whispersystems/test/SessionCipherTest.java b/src/androidTest/java/org/whispersystems/test/SessionCipherTest.java new file mode 100644 index 000000000..52b5fa898 --- /dev/null +++ b/src/androidTest/java/org/whispersystems/test/SessionCipherTest.java @@ -0,0 +1,206 @@ +package org.whispersystems.test; + +import android.test.AndroidTestCase; + +import org.whispersystems.libaxolotl.DuplicateMessageException; +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.IdentityKeyPair; +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.InvalidMessageException; +import org.whispersystems.libaxolotl.LegacyMessageException; +import org.whispersystems.libaxolotl.NoSessionException; +import org.whispersystems.libaxolotl.SessionCipher; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.protocol.CiphertextMessage; +import org.whispersystems.libaxolotl.protocol.WhisperMessage; +import org.whispersystems.libaxolotl.ratchet.AliceAxolotlParameters; +import org.whispersystems.libaxolotl.ratchet.BobAxolotlParameters; +import org.whispersystems.libaxolotl.ratchet.RatchetingSession; +import org.whispersystems.libaxolotl.state.AxolotlStore; +import org.whispersystems.libaxolotl.state.SessionRecord; +import org.whispersystems.libaxolotl.state.SessionState; +import org.whispersystems.libaxolotl.util.guava.Optional; + +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Random; + + +public class SessionCipherTest extends AndroidTestCase { + + public void testBasicSessionV2() + throws InvalidKeyException, DuplicateMessageException, + LegacyMessageException, InvalidMessageException, NoSuchAlgorithmException, NoSessionException + { + SessionRecord aliceSessionRecord = new SessionRecord(); + SessionRecord bobSessionRecord = new SessionRecord(); + + initializeSessionsV2(aliceSessionRecord.getSessionState(), bobSessionRecord.getSessionState()); + runInteraction(aliceSessionRecord, bobSessionRecord); + } + + public void testBasicSessionV3() + throws InvalidKeyException, DuplicateMessageException, + LegacyMessageException, InvalidMessageException, NoSuchAlgorithmException, NoSessionException + { + SessionRecord aliceSessionRecord = new SessionRecord(); + SessionRecord bobSessionRecord = new SessionRecord(); + + initializeSessionsV3(aliceSessionRecord.getSessionState(), bobSessionRecord.getSessionState()); + runInteraction(aliceSessionRecord, bobSessionRecord); + } + + private void runInteraction(SessionRecord aliceSessionRecord, SessionRecord bobSessionRecord) + throws DuplicateMessageException, LegacyMessageException, InvalidMessageException, NoSuchAlgorithmException, NoSessionException { + AxolotlStore aliceStore = new InMemoryAxolotlStore(); + AxolotlStore bobStore = new InMemoryAxolotlStore(); + + aliceStore.storeSession(2L, 1, aliceSessionRecord); + bobStore.storeSession(3L, 1, bobSessionRecord); + + SessionCipher aliceCipher = new SessionCipher(aliceStore, 2L, 1); + SessionCipher bobCipher = new SessionCipher(bobStore, 3L, 1); + + byte[] alicePlaintext = "This is a plaintext message.".getBytes(); + CiphertextMessage message = aliceCipher.encrypt(alicePlaintext); + byte[] bobPlaintext = bobCipher.decrypt(new WhisperMessage(message.serialize())); + + assertTrue(Arrays.equals(alicePlaintext, bobPlaintext)); + + byte[] bobReply = "This is a message from Bob.".getBytes(); + CiphertextMessage reply = bobCipher.encrypt(bobReply); + byte[] receivedReply = aliceCipher.decrypt(new WhisperMessage(reply.serialize())); + + assertTrue(Arrays.equals(bobReply, receivedReply)); + + List aliceCiphertextMessages = new ArrayList<>(); + List alicePlaintextMessages = new ArrayList<>(); + + for (int i=0;i<50;i++) { + alicePlaintextMessages.add(("смерть за смерть " + i).getBytes()); + aliceCiphertextMessages.add(aliceCipher.encrypt(("смерть за смерть " + i).getBytes())); + } + + long seed = System.currentTimeMillis(); + + Collections.shuffle(aliceCiphertextMessages, new Random(seed)); + Collections.shuffle(alicePlaintextMessages, new Random(seed)); + + for (int i=0;i bobCiphertextMessages = new ArrayList<>(); + List bobPlaintextMessages = new ArrayList<>(); + + for (int i=0;i<20;i++) { + bobPlaintextMessages.add(("смерть за смерть " + i).getBytes()); + bobCiphertextMessages.add(bobCipher.encrypt(("смерть за смерть " + i).getBytes())); + } + + seed = System.currentTimeMillis(); + + Collections.shuffle(bobCiphertextMessages, new Random(seed)); + Collections.shuffle(bobPlaintextMessages, new Random(seed)); + + for (int i=0;iabsent()) + .create(); + + BobAxolotlParameters bobParameters = BobAxolotlParameters.newBuilder() + .setOurIdentityKey(bobIdentityKey) + .setOurOneTimePreKey(Optional.absent()) + .setOurRatchetKey(bobEphemeralKey) + .setOurSignedPreKey(bobBaseKey) + .setTheirBaseKey(aliceBaseKey.getPublicKey()) + .setTheirIdentityKey(aliceIdentityKey.getPublicKey()) + .create(); + + RatchetingSession.initializeSession(aliceSessionState, 2, aliceParameters); + RatchetingSession.initializeSession(bobSessionState, 2, bobParameters); + } + + private void initializeSessionsV3(SessionState aliceSessionState, SessionState bobSessionState) + throws InvalidKeyException + { + ECKeyPair aliceIdentityKeyPair = Curve.generateKeyPair(); + IdentityKeyPair aliceIdentityKey = new IdentityKeyPair(new IdentityKey(aliceIdentityKeyPair.getPublicKey()), + aliceIdentityKeyPair.getPrivateKey()); + ECKeyPair aliceBaseKey = Curve.generateKeyPair(); + ECKeyPair aliceEphemeralKey = Curve.generateKeyPair(); + + ECKeyPair alicePreKey = aliceBaseKey; + + ECKeyPair bobIdentityKeyPair = Curve.generateKeyPair(); + IdentityKeyPair bobIdentityKey = new IdentityKeyPair(new IdentityKey(bobIdentityKeyPair.getPublicKey()), + bobIdentityKeyPair.getPrivateKey()); + ECKeyPair bobBaseKey = Curve.generateKeyPair(); + ECKeyPair bobEphemeralKey = bobBaseKey; + + ECKeyPair bobPreKey = Curve.generateKeyPair(); + + AliceAxolotlParameters aliceParameters = AliceAxolotlParameters.newBuilder() + .setOurBaseKey(aliceBaseKey) + .setOurIdentityKey(aliceIdentityKey) + .setTheirOneTimePreKey(Optional.absent()) + .setTheirRatchetKey(bobEphemeralKey.getPublicKey()) + .setTheirSignedPreKey(bobBaseKey.getPublicKey()) + .setTheirIdentityKey(bobIdentityKey.getPublicKey()) + .create(); + + BobAxolotlParameters bobParameters = BobAxolotlParameters.newBuilder() + .setOurRatchetKey(bobEphemeralKey) + .setOurSignedPreKey(bobBaseKey) + .setOurOneTimePreKey(Optional.absent()) + .setOurIdentityKey(bobIdentityKey) + .setTheirIdentityKey(aliceIdentityKey.getPublicKey()) + .setTheirBaseKey(aliceBaseKey.getPublicKey()) + .create(); + + RatchetingSession.initializeSession(aliceSessionState, 3, aliceParameters); + RatchetingSession.initializeSession(bobSessionState, 3, bobParameters); + } +} diff --git a/src/androidTest/java/org/whispersystems/test/SimultaneousInitiateTests.java b/src/androidTest/java/org/whispersystems/test/SimultaneousInitiateTests.java new file mode 100644 index 000000000..dffde57c6 --- /dev/null +++ b/src/androidTest/java/org/whispersystems/test/SimultaneousInitiateTests.java @@ -0,0 +1,501 @@ +package org.whispersystems.test; + +import android.test.AndroidTestCase; +import android.util.Log; + +import org.whispersystems.libaxolotl.DuplicateMessageException; +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.InvalidKeyIdException; +import org.whispersystems.libaxolotl.InvalidMessageException; +import org.whispersystems.libaxolotl.InvalidVersionException; +import org.whispersystems.libaxolotl.LegacyMessageException; +import org.whispersystems.libaxolotl.NoSessionException; +import org.whispersystems.libaxolotl.SessionBuilder; +import org.whispersystems.libaxolotl.SessionCipher; +import org.whispersystems.libaxolotl.UntrustedIdentityException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.protocol.CiphertextMessage; +import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage; +import org.whispersystems.libaxolotl.protocol.WhisperMessage; +import org.whispersystems.libaxolotl.state.AxolotlStore; +import org.whispersystems.libaxolotl.state.PreKeyBundle; +import org.whispersystems.libaxolotl.state.PreKeyRecord; +import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; +import org.whispersystems.libaxolotl.util.Medium; + +import java.util.Arrays; +import java.util.Random; + +public class SimultaneousInitiateTests extends AndroidTestCase { + + private static final long BOB_RECIPENT_ID = 12345; + private static final long ALICE_RECIPIENT_ID = 6789; + + private static final ECKeyPair aliceSignedPreKey = Curve.generateKeyPair(); + private static final ECKeyPair bobSignedPreKey = Curve.generateKeyPair(); + + private static final int aliceSignedPreKeyId = new Random().nextInt(Medium.MAX_VALUE); + private static final int bobSignedPreKeyId = new Random().nextInt(Medium.MAX_VALUE); + + public void testBasicSimultaneousInitiate() + throws InvalidKeyException, UntrustedIdentityException, InvalidVersionException, + InvalidMessageException, DuplicateMessageException, LegacyMessageException, + InvalidKeyIdException, NoSessionException + { + AxolotlStore aliceStore = new InMemoryAxolotlStore(); + AxolotlStore bobStore = new InMemoryAxolotlStore(); + + PreKeyBundle alicePreKeyBundle = createAlicePreKeyBundle(aliceStore); + PreKeyBundle bobPreKeyBundle = createBobPreKeyBundle(bobStore); + + SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_RECIPENT_ID, 1); + SessionBuilder bobSessionBuilder = new SessionBuilder(bobStore, ALICE_RECIPIENT_ID, 1); + + SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_RECIPENT_ID, 1); + SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_RECIPIENT_ID, 1); + + aliceSessionBuilder.process(bobPreKeyBundle); + bobSessionBuilder.process(alicePreKeyBundle); + + CiphertextMessage messageForBob = aliceSessionCipher.encrypt("hey there".getBytes()); + CiphertextMessage messageForAlice = bobSessionCipher.encrypt("sample message".getBytes()); + + assertTrue(messageForBob.getType() == CiphertextMessage.PREKEY_TYPE); + assertTrue(messageForAlice.getType() == CiphertextMessage.PREKEY_TYPE); + + assertFalse(isSessionIdEqual(aliceStore, bobStore)); + + byte[] alicePlaintext = aliceSessionCipher.decrypt(new PreKeyWhisperMessage(messageForAlice.serialize())); + byte[] bobPlaintext = bobSessionCipher.decrypt(new PreKeyWhisperMessage(messageForBob.serialize())); + + assertTrue(new String(alicePlaintext).equals("sample message")); + assertTrue(new String(bobPlaintext).equals("hey there")); + + assertTrue(aliceStore.loadSession(BOB_RECIPENT_ID, 1).getSessionState().getSessionVersion() == 3); + assertTrue(bobStore.loadSession(ALICE_RECIPIENT_ID, 1).getSessionState().getSessionVersion() == 3); + + assertFalse(isSessionIdEqual(aliceStore, bobStore)); + + CiphertextMessage aliceResponse = aliceSessionCipher.encrypt("second message".getBytes()); + + assertTrue(aliceResponse.getType() == CiphertextMessage.WHISPER_TYPE); + + byte[] responsePlaintext = bobSessionCipher.decrypt(new WhisperMessage(aliceResponse.serialize())); + + assertTrue(new String(responsePlaintext).equals("second message")); + assertTrue(isSessionIdEqual(aliceStore, bobStore)); + + CiphertextMessage finalMessage = bobSessionCipher.encrypt("third message".getBytes()); + + assertTrue(finalMessage.getType() == CiphertextMessage.WHISPER_TYPE); + + byte[] finalPlaintext = aliceSessionCipher.decrypt(new WhisperMessage(finalMessage.serialize())); + + assertTrue(new String(finalPlaintext).equals("third message")); + assertTrue(isSessionIdEqual(aliceStore, bobStore)); + } + + public void testLostSimultaneousInitiate() throws InvalidKeyException, UntrustedIdentityException, InvalidVersionException, InvalidMessageException, DuplicateMessageException, LegacyMessageException, InvalidKeyIdException, NoSessionException { + AxolotlStore aliceStore = new InMemoryAxolotlStore(); + AxolotlStore bobStore = new InMemoryAxolotlStore(); + + PreKeyBundle alicePreKeyBundle = createAlicePreKeyBundle(aliceStore); + PreKeyBundle bobPreKeyBundle = createBobPreKeyBundle(bobStore); + + SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_RECIPENT_ID, 1); + SessionBuilder bobSessionBuilder = new SessionBuilder(bobStore, ALICE_RECIPIENT_ID, 1); + + SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_RECIPENT_ID, 1); + SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_RECIPIENT_ID, 1); + + aliceSessionBuilder.process(bobPreKeyBundle); + bobSessionBuilder.process(alicePreKeyBundle); + + CiphertextMessage messageForBob = aliceSessionCipher.encrypt("hey there".getBytes()); + CiphertextMessage messageForAlice = bobSessionCipher.encrypt("sample message".getBytes()); + + assertTrue(messageForBob.getType() == CiphertextMessage.PREKEY_TYPE); + assertTrue(messageForAlice.getType() == CiphertextMessage.PREKEY_TYPE); + + assertFalse(isSessionIdEqual(aliceStore, bobStore)); + + byte[] bobPlaintext = bobSessionCipher.decrypt(new PreKeyWhisperMessage(messageForBob.serialize())); + + assertTrue(new String(bobPlaintext).equals("hey there")); + assertTrue(bobStore.loadSession(ALICE_RECIPIENT_ID, 1).getSessionState().getSessionVersion() == 3); + + CiphertextMessage aliceResponse = aliceSessionCipher.encrypt("second message".getBytes()); + + assertTrue(aliceResponse.getType() == CiphertextMessage.PREKEY_TYPE); + + byte[] responsePlaintext = bobSessionCipher.decrypt(new PreKeyWhisperMessage(aliceResponse.serialize())); + + assertTrue(new String(responsePlaintext).equals("second message")); + assertTrue(isSessionIdEqual(aliceStore, bobStore)); + + CiphertextMessage finalMessage = bobSessionCipher.encrypt("third message".getBytes()); + + assertTrue(finalMessage.getType() == CiphertextMessage.WHISPER_TYPE); + + byte[] finalPlaintext = aliceSessionCipher.decrypt(new WhisperMessage(finalMessage.serialize())); + + assertTrue(new String(finalPlaintext).equals("third message")); + assertTrue(isSessionIdEqual(aliceStore, bobStore)); + } + + public void testSimultaneousInitiateLostMessage() + throws InvalidKeyException, UntrustedIdentityException, InvalidVersionException, + InvalidMessageException, DuplicateMessageException, LegacyMessageException, + InvalidKeyIdException, NoSessionException + { + AxolotlStore aliceStore = new InMemoryAxolotlStore(); + AxolotlStore bobStore = new InMemoryAxolotlStore(); + + PreKeyBundle alicePreKeyBundle = createAlicePreKeyBundle(aliceStore); + PreKeyBundle bobPreKeyBundle = createBobPreKeyBundle(bobStore); + + SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_RECIPENT_ID, 1); + SessionBuilder bobSessionBuilder = new SessionBuilder(bobStore, ALICE_RECIPIENT_ID, 1); + + SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_RECIPENT_ID, 1); + SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_RECIPIENT_ID, 1); + + aliceSessionBuilder.process(bobPreKeyBundle); + bobSessionBuilder.process(alicePreKeyBundle); + + CiphertextMessage messageForBob = aliceSessionCipher.encrypt("hey there".getBytes()); + CiphertextMessage messageForAlice = bobSessionCipher.encrypt("sample message".getBytes()); + + assertTrue(messageForBob.getType() == CiphertextMessage.PREKEY_TYPE); + assertTrue(messageForAlice.getType() == CiphertextMessage.PREKEY_TYPE); + + assertFalse(isSessionIdEqual(aliceStore, bobStore)); + + byte[] alicePlaintext = aliceSessionCipher.decrypt(new PreKeyWhisperMessage(messageForAlice.serialize())); + byte[] bobPlaintext = bobSessionCipher.decrypt(new PreKeyWhisperMessage(messageForBob.serialize())); + + assertTrue(new String(alicePlaintext).equals("sample message")); + assertTrue(new String(bobPlaintext).equals("hey there")); + + assertTrue(aliceStore.loadSession(BOB_RECIPENT_ID, 1).getSessionState().getSessionVersion() == 3); + assertTrue(bobStore.loadSession(ALICE_RECIPIENT_ID, 1).getSessionState().getSessionVersion() == 3); + + assertFalse(isSessionIdEqual(aliceStore, bobStore)); + + CiphertextMessage aliceResponse = aliceSessionCipher.encrypt("second message".getBytes()); + + assertTrue(aliceResponse.getType() == CiphertextMessage.WHISPER_TYPE); + +// byte[] responsePlaintext = bobSessionCipher.decrypt(new WhisperMessage(aliceResponse.serialize())); +// +// assertTrue(new String(responsePlaintext).equals("second message")); +// assertTrue(isSessionIdEqual(aliceStore, bobStore)); + assertFalse(isSessionIdEqual(aliceStore, bobStore)); + + CiphertextMessage finalMessage = bobSessionCipher.encrypt("third message".getBytes()); + + assertTrue(finalMessage.getType() == CiphertextMessage.WHISPER_TYPE); + + byte[] finalPlaintext = aliceSessionCipher.decrypt(new WhisperMessage(finalMessage.serialize())); + + assertTrue(new String(finalPlaintext).equals("third message")); + assertTrue(isSessionIdEqual(aliceStore, bobStore)); + } + + public void testSimultaneousInitiateRepeatedMessages() + throws InvalidKeyException, UntrustedIdentityException, InvalidVersionException, + InvalidMessageException, DuplicateMessageException, LegacyMessageException, + InvalidKeyIdException, NoSessionException + { + AxolotlStore aliceStore = new InMemoryAxolotlStore(); + AxolotlStore bobStore = new InMemoryAxolotlStore(); + + PreKeyBundle alicePreKeyBundle = createAlicePreKeyBundle(aliceStore); + PreKeyBundle bobPreKeyBundle = createBobPreKeyBundle(bobStore); + + SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_RECIPENT_ID, 1); + SessionBuilder bobSessionBuilder = new SessionBuilder(bobStore, ALICE_RECIPIENT_ID, 1); + + SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_RECIPENT_ID, 1); + SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_RECIPIENT_ID, 1); + + aliceSessionBuilder.process(bobPreKeyBundle); + bobSessionBuilder.process(alicePreKeyBundle); + + CiphertextMessage messageForBob = aliceSessionCipher.encrypt("hey there".getBytes()); + CiphertextMessage messageForAlice = bobSessionCipher.encrypt("sample message".getBytes()); + + assertTrue(messageForBob.getType() == CiphertextMessage.PREKEY_TYPE); + assertTrue(messageForAlice.getType() == CiphertextMessage.PREKEY_TYPE); + + assertFalse(isSessionIdEqual(aliceStore, bobStore)); + + byte[] alicePlaintext = aliceSessionCipher.decrypt(new PreKeyWhisperMessage(messageForAlice.serialize())); + byte[] bobPlaintext = bobSessionCipher.decrypt(new PreKeyWhisperMessage(messageForBob.serialize())); + + assertTrue(new String(alicePlaintext).equals("sample message")); + assertTrue(new String(bobPlaintext).equals("hey there")); + + assertTrue(aliceStore.loadSession(BOB_RECIPENT_ID, 1).getSessionState().getSessionVersion() == 3); + assertTrue(bobStore.loadSession(ALICE_RECIPIENT_ID, 1).getSessionState().getSessionVersion() == 3); + + assertFalse(isSessionIdEqual(aliceStore, bobStore)); + + for (int i=0;i<50;i++) { + Log.w("SimultaneousInitiateTests", "Iteration: " + i); + CiphertextMessage messageForBobRepeat = aliceSessionCipher.encrypt("hey there".getBytes()); + CiphertextMessage messageForAliceRepeat = bobSessionCipher.encrypt("sample message".getBytes()); + + assertTrue(messageForBobRepeat.getType() == CiphertextMessage.WHISPER_TYPE); + assertTrue(messageForAliceRepeat.getType() == CiphertextMessage.WHISPER_TYPE); + + assertFalse(isSessionIdEqual(aliceStore, bobStore)); + + byte[] alicePlaintextRepeat = aliceSessionCipher.decrypt(new WhisperMessage(messageForAliceRepeat.serialize())); + byte[] bobPlaintextRepeat = bobSessionCipher.decrypt(new WhisperMessage(messageForBobRepeat.serialize())); + + assertTrue(new String(alicePlaintextRepeat).equals("sample message")); + assertTrue(new String(bobPlaintextRepeat).equals("hey there")); + + assertFalse(isSessionIdEqual(aliceStore, bobStore)); + } + + CiphertextMessage aliceResponse = aliceSessionCipher.encrypt("second message".getBytes()); + + assertTrue(aliceResponse.getType() == CiphertextMessage.WHISPER_TYPE); + + byte[] responsePlaintext = bobSessionCipher.decrypt(new WhisperMessage(aliceResponse.serialize())); + + assertTrue(new String(responsePlaintext).equals("second message")); + assertTrue(isSessionIdEqual(aliceStore, bobStore)); + + CiphertextMessage finalMessage = bobSessionCipher.encrypt("third message".getBytes()); + + assertTrue(finalMessage.getType() == CiphertextMessage.WHISPER_TYPE); + + byte[] finalPlaintext = aliceSessionCipher.decrypt(new WhisperMessage(finalMessage.serialize())); + + assertTrue(new String(finalPlaintext).equals("third message")); + assertTrue(isSessionIdEqual(aliceStore, bobStore)); + } + + public void testRepeatedSimultaneousInitiateRepeatedMessages() + throws InvalidKeyException, UntrustedIdentityException, InvalidVersionException, + InvalidMessageException, DuplicateMessageException, LegacyMessageException, + InvalidKeyIdException, NoSessionException + { + AxolotlStore aliceStore = new InMemoryAxolotlStore(); + AxolotlStore bobStore = new InMemoryAxolotlStore(); + + + SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_RECIPENT_ID, 1); + SessionBuilder bobSessionBuilder = new SessionBuilder(bobStore, ALICE_RECIPIENT_ID, 1); + + SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_RECIPENT_ID, 1); + SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_RECIPIENT_ID, 1); + + for (int i=0;i<15;i++) { + PreKeyBundle alicePreKeyBundle = createAlicePreKeyBundle(aliceStore); + PreKeyBundle bobPreKeyBundle = createBobPreKeyBundle(bobStore); + + aliceSessionBuilder.process(bobPreKeyBundle); + bobSessionBuilder.process(alicePreKeyBundle); + + CiphertextMessage messageForBob = aliceSessionCipher.encrypt("hey there".getBytes()); + CiphertextMessage messageForAlice = bobSessionCipher.encrypt("sample message".getBytes()); + + assertTrue(messageForBob.getType() == CiphertextMessage.PREKEY_TYPE); + assertTrue(messageForAlice.getType() == CiphertextMessage.PREKEY_TYPE); + + assertFalse(isSessionIdEqual(aliceStore, bobStore)); + + byte[] alicePlaintext = aliceSessionCipher.decrypt(new PreKeyWhisperMessage(messageForAlice.serialize())); + byte[] bobPlaintext = bobSessionCipher.decrypt(new PreKeyWhisperMessage(messageForBob.serialize())); + + assertTrue(new String(alicePlaintext).equals("sample message")); + assertTrue(new String(bobPlaintext).equals("hey there")); + + assertTrue(aliceStore.loadSession(BOB_RECIPENT_ID, 1).getSessionState().getSessionVersion() == 3); + assertTrue(bobStore.loadSession(ALICE_RECIPIENT_ID, 1).getSessionState().getSessionVersion() == 3); + + assertFalse(isSessionIdEqual(aliceStore, bobStore)); + } + + for (int i=0;i<50;i++) { + Log.w("SimultaneousInitiateTests", "Iteration: " + i); + CiphertextMessage messageForBobRepeat = aliceSessionCipher.encrypt("hey there".getBytes()); + CiphertextMessage messageForAliceRepeat = bobSessionCipher.encrypt("sample message".getBytes()); + + assertTrue(messageForBobRepeat.getType() == CiphertextMessage.WHISPER_TYPE); + assertTrue(messageForAliceRepeat.getType() == CiphertextMessage.WHISPER_TYPE); + + assertFalse(isSessionIdEqual(aliceStore, bobStore)); + + byte[] alicePlaintextRepeat = aliceSessionCipher.decrypt(new WhisperMessage(messageForAliceRepeat.serialize())); + byte[] bobPlaintextRepeat = bobSessionCipher.decrypt(new WhisperMessage(messageForBobRepeat.serialize())); + + assertTrue(new String(alicePlaintextRepeat).equals("sample message")); + assertTrue(new String(bobPlaintextRepeat).equals("hey there")); + + assertFalse(isSessionIdEqual(aliceStore, bobStore)); + } + + CiphertextMessage aliceResponse = aliceSessionCipher.encrypt("second message".getBytes()); + + assertTrue(aliceResponse.getType() == CiphertextMessage.WHISPER_TYPE); + + byte[] responsePlaintext = bobSessionCipher.decrypt(new WhisperMessage(aliceResponse.serialize())); + + assertTrue(new String(responsePlaintext).equals("second message")); + assertTrue(isSessionIdEqual(aliceStore, bobStore)); + + CiphertextMessage finalMessage = bobSessionCipher.encrypt("third message".getBytes()); + + assertTrue(finalMessage.getType() == CiphertextMessage.WHISPER_TYPE); + + byte[] finalPlaintext = aliceSessionCipher.decrypt(new WhisperMessage(finalMessage.serialize())); + + assertTrue(new String(finalPlaintext).equals("third message")); + assertTrue(isSessionIdEqual(aliceStore, bobStore)); + } + + public void testRepeatedSimultaneousInitiateLostMessageRepeatedMessages() + throws InvalidKeyException, UntrustedIdentityException, InvalidVersionException, + InvalidMessageException, DuplicateMessageException, LegacyMessageException, + InvalidKeyIdException, NoSessionException + { + AxolotlStore aliceStore = new InMemoryAxolotlStore(); + AxolotlStore bobStore = new InMemoryAxolotlStore(); + + + SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_RECIPENT_ID, 1); + SessionBuilder bobSessionBuilder = new SessionBuilder(bobStore, ALICE_RECIPIENT_ID, 1); + + SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_RECIPENT_ID, 1); + SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_RECIPIENT_ID, 1); + +// PreKeyBundle aliceLostPreKeyBundle = createAlicePreKeyBundle(aliceStore); + PreKeyBundle bobLostPreKeyBundle = createBobPreKeyBundle(bobStore); + + aliceSessionBuilder.process(bobLostPreKeyBundle); +// bobSessionBuilder.process(aliceLostPreKeyBundle); + + CiphertextMessage lostMessageForBob = aliceSessionCipher.encrypt("hey there".getBytes()); +// CiphertextMessage lostMessageForAlice = bobSessionCipher.encrypt("sample message".getBytes()); + + for (int i=0;i<15;i++) { + PreKeyBundle alicePreKeyBundle = createAlicePreKeyBundle(aliceStore); + PreKeyBundle bobPreKeyBundle = createBobPreKeyBundle(bobStore); + + aliceSessionBuilder.process(bobPreKeyBundle); + bobSessionBuilder.process(alicePreKeyBundle); + + CiphertextMessage messageForBob = aliceSessionCipher.encrypt("hey there".getBytes()); + CiphertextMessage messageForAlice = bobSessionCipher.encrypt("sample message".getBytes()); + + assertTrue(messageForBob.getType() == CiphertextMessage.PREKEY_TYPE); + assertTrue(messageForAlice.getType() == CiphertextMessage.PREKEY_TYPE); + + assertFalse(isSessionIdEqual(aliceStore, bobStore)); + + byte[] alicePlaintext = aliceSessionCipher.decrypt(new PreKeyWhisperMessage(messageForAlice.serialize())); + byte[] bobPlaintext = bobSessionCipher.decrypt(new PreKeyWhisperMessage(messageForBob.serialize())); + + assertTrue(new String(alicePlaintext).equals("sample message")); + assertTrue(new String(bobPlaintext).equals("hey there")); + + assertTrue(aliceStore.loadSession(BOB_RECIPENT_ID, 1).getSessionState().getSessionVersion() == 3); + assertTrue(bobStore.loadSession(ALICE_RECIPIENT_ID, 1).getSessionState().getSessionVersion() == 3); + + assertFalse(isSessionIdEqual(aliceStore, bobStore)); + } + + for (int i=0;i<50;i++) { + Log.w("SimultaneousInitiateTests", "Iteration: " + i); + CiphertextMessage messageForBobRepeat = aliceSessionCipher.encrypt("hey there".getBytes()); + CiphertextMessage messageForAliceRepeat = bobSessionCipher.encrypt("sample message".getBytes()); + + assertTrue(messageForBobRepeat.getType() == CiphertextMessage.WHISPER_TYPE); + assertTrue(messageForAliceRepeat.getType() == CiphertextMessage.WHISPER_TYPE); + + assertFalse(isSessionIdEqual(aliceStore, bobStore)); + + byte[] alicePlaintextRepeat = aliceSessionCipher.decrypt(new WhisperMessage(messageForAliceRepeat.serialize())); + byte[] bobPlaintextRepeat = bobSessionCipher.decrypt(new WhisperMessage(messageForBobRepeat.serialize())); + + assertTrue(new String(alicePlaintextRepeat).equals("sample message")); + assertTrue(new String(bobPlaintextRepeat).equals("hey there")); + + assertFalse(isSessionIdEqual(aliceStore, bobStore)); + } + + CiphertextMessage aliceResponse = aliceSessionCipher.encrypt("second message".getBytes()); + + assertTrue(aliceResponse.getType() == CiphertextMessage.WHISPER_TYPE); + + byte[] responsePlaintext = bobSessionCipher.decrypt(new WhisperMessage(aliceResponse.serialize())); + + assertTrue(new String(responsePlaintext).equals("second message")); + assertTrue(isSessionIdEqual(aliceStore, bobStore)); + + CiphertextMessage finalMessage = bobSessionCipher.encrypt("third message".getBytes()); + + assertTrue(finalMessage.getType() == CiphertextMessage.WHISPER_TYPE); + + byte[] finalPlaintext = aliceSessionCipher.decrypt(new WhisperMessage(finalMessage.serialize())); + + assertTrue(new String(finalPlaintext).equals("third message")); + assertTrue(isSessionIdEqual(aliceStore, bobStore)); + + byte[] lostMessagePlaintext = bobSessionCipher.decrypt(new PreKeyWhisperMessage(lostMessageForBob.serialize())); + assertTrue(new String(lostMessagePlaintext).equals("hey there")); + + assertFalse(isSessionIdEqual(aliceStore, bobStore)); + + CiphertextMessage blastFromThePast = bobSessionCipher.encrypt("unexpected!".getBytes()); + byte[] blastFromThePastPlaintext = aliceSessionCipher.decrypt(new WhisperMessage(blastFromThePast.serialize())); + + assertTrue(new String(blastFromThePastPlaintext).equals("unexpected!")); + assertTrue(isSessionIdEqual(aliceStore, bobStore)); + } + + private boolean isSessionIdEqual(AxolotlStore aliceStore, AxolotlStore bobStore) { + return Arrays.equals(aliceStore.loadSession(BOB_RECIPENT_ID, 1).getSessionState().getAliceBaseKey(), + bobStore.loadSession(ALICE_RECIPIENT_ID, 1).getSessionState().getAliceBaseKey()); + } + + private PreKeyBundle createAlicePreKeyBundle(AxolotlStore aliceStore) throws InvalidKeyException { + ECKeyPair aliceUnsignedPreKey = Curve.generateKeyPair(); + int aliceUnsignedPreKeyId = new Random().nextInt(Medium.MAX_VALUE); + byte[] aliceSignature = Curve.calculateSignature(aliceStore.getIdentityKeyPair().getPrivateKey(), + aliceSignedPreKey.getPublicKey().serialize()); + + PreKeyBundle alicePreKeyBundle = new PreKeyBundle(1, 1, + aliceUnsignedPreKeyId, aliceUnsignedPreKey.getPublicKey(), + aliceSignedPreKeyId, aliceSignedPreKey.getPublicKey(), + aliceSignature, aliceStore.getIdentityKeyPair().getPublicKey()); + + aliceStore.storeSignedPreKey(aliceSignedPreKeyId, new SignedPreKeyRecord(aliceSignedPreKeyId, System.currentTimeMillis(), aliceSignedPreKey, aliceSignature)); + aliceStore.storePreKey(aliceUnsignedPreKeyId, new PreKeyRecord(aliceUnsignedPreKeyId, aliceUnsignedPreKey)); + + return alicePreKeyBundle; + } + + private PreKeyBundle createBobPreKeyBundle(AxolotlStore bobStore) throws InvalidKeyException { + ECKeyPair bobUnsignedPreKey = Curve.generateKeyPair(); + int bobUnsignedPreKeyId = new Random().nextInt(Medium.MAX_VALUE); + byte[] bobSignature = Curve.calculateSignature(bobStore.getIdentityKeyPair().getPrivateKey(), + bobSignedPreKey.getPublicKey().serialize()); + + PreKeyBundle bobPreKeyBundle = new PreKeyBundle(1, 1, + bobUnsignedPreKeyId, bobUnsignedPreKey.getPublicKey(), + bobSignedPreKeyId, bobSignedPreKey.getPublicKey(), + bobSignature, bobStore.getIdentityKeyPair().getPublicKey()); + + bobStore.storeSignedPreKey(bobSignedPreKeyId, new SignedPreKeyRecord(bobSignedPreKeyId, System.currentTimeMillis(), bobSignedPreKey, bobSignature)); + bobStore.storePreKey(bobUnsignedPreKeyId, new PreKeyRecord(bobUnsignedPreKeyId, bobUnsignedPreKey)); + + return bobPreKeyBundle; + } +} diff --git a/src/androidTest/java/org/whispersystems/test/ecc/Curve25519Test.java b/src/androidTest/java/org/whispersystems/test/ecc/Curve25519Test.java new file mode 100644 index 000000000..4b8ce2ea1 --- /dev/null +++ b/src/androidTest/java/org/whispersystems/test/ecc/Curve25519Test.java @@ -0,0 +1,154 @@ +package org.whispersystems.test.ecc; + +import android.test.AndroidTestCase; + +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.ecc.ECPrivateKey; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; + +import java.util.Arrays; + + +public class Curve25519Test extends AndroidTestCase { + + public void testAgreement() throws InvalidKeyException { + + byte[] alicePublic = {(byte) 0x05, (byte) 0x1b, (byte) 0xb7, (byte) 0x59, (byte) 0x66, + (byte) 0xf2, (byte) 0xe9, (byte) 0x3a, (byte) 0x36, (byte) 0x91, + (byte) 0xdf, (byte) 0xff, (byte) 0x94, (byte) 0x2b, (byte) 0xb2, + (byte) 0xa4, (byte) 0x66, (byte) 0xa1, (byte) 0xc0, (byte) 0x8b, + (byte) 0x8d, (byte) 0x78, (byte) 0xca, (byte) 0x3f, (byte) 0x4d, + (byte) 0x6d, (byte) 0xf8, (byte) 0xb8, (byte) 0xbf, (byte) 0xa2, + (byte) 0xe4, (byte) 0xee, (byte) 0x28}; + + byte[] alicePrivate = {(byte) 0xc8, (byte) 0x06, (byte) 0x43, (byte) 0x9d, (byte) 0xc9, + (byte) 0xd2, (byte) 0xc4, (byte) 0x76, (byte) 0xff, (byte) 0xed, + (byte) 0x8f, (byte) 0x25, (byte) 0x80, (byte) 0xc0, (byte) 0x88, + (byte) 0x8d, (byte) 0x58, (byte) 0xab, (byte) 0x40, (byte) 0x6b, + (byte) 0xf7, (byte) 0xae, (byte) 0x36, (byte) 0x98, (byte) 0x87, + (byte) 0x90, (byte) 0x21, (byte) 0xb9, (byte) 0x6b, (byte) 0xb4, + (byte) 0xbf, (byte) 0x59}; + + byte[] bobPublic = {(byte) 0x05, (byte) 0x65, (byte) 0x36, (byte) 0x14, (byte) 0x99, + (byte) 0x3d, (byte) 0x2b, (byte) 0x15, (byte) 0xee, (byte) 0x9e, + (byte) 0x5f, (byte) 0xd3, (byte) 0xd8, (byte) 0x6c, (byte) 0xe7, + (byte) 0x19, (byte) 0xef, (byte) 0x4e, (byte) 0xc1, (byte) 0xda, + (byte) 0xae, (byte) 0x18, (byte) 0x86, (byte) 0xa8, (byte) 0x7b, + (byte) 0x3f, (byte) 0x5f, (byte) 0xa9, (byte) 0x56, (byte) 0x5a, + (byte) 0x27, (byte) 0xa2, (byte) 0x2f}; + + byte[] bobPrivate = {(byte) 0xb0, (byte) 0x3b, (byte) 0x34, (byte) 0xc3, (byte) 0x3a, + (byte) 0x1c, (byte) 0x44, (byte) 0xf2, (byte) 0x25, (byte) 0xb6, + (byte) 0x62, (byte) 0xd2, (byte) 0xbf, (byte) 0x48, (byte) 0x59, + (byte) 0xb8, (byte) 0x13, (byte) 0x54, (byte) 0x11, (byte) 0xfa, + (byte) 0x7b, (byte) 0x03, (byte) 0x86, (byte) 0xd4, (byte) 0x5f, + (byte) 0xb7, (byte) 0x5d, (byte) 0xc5, (byte) 0xb9, (byte) 0x1b, + (byte) 0x44, (byte) 0x66}; + + byte[] shared = {(byte) 0x32, (byte) 0x5f, (byte) 0x23, (byte) 0x93, (byte) 0x28, + (byte) 0x94, (byte) 0x1c, (byte) 0xed, (byte) 0x6e, (byte) 0x67, + (byte) 0x3b, (byte) 0x86, (byte) 0xba, (byte) 0x41, (byte) 0x01, + (byte) 0x74, (byte) 0x48, (byte) 0xe9, (byte) 0x9b, (byte) 0x64, + (byte) 0x9a, (byte) 0x9c, (byte) 0x38, (byte) 0x06, (byte) 0xc1, + (byte) 0xdd, (byte) 0x7c, (byte) 0xa4, (byte) 0xc4, (byte) 0x77, + (byte) 0xe6, (byte) 0x29}; + + ECPublicKey alicePublicKey = Curve.decodePoint(alicePublic, 0); + ECPrivateKey alicePrivateKey = Curve.decodePrivatePoint(alicePrivate); + + ECPublicKey bobPublicKey = Curve.decodePoint(bobPublic, 0); + ECPrivateKey bobPrivateKey = Curve.decodePrivatePoint(bobPrivate); + + byte[] sharedOne = Curve.calculateAgreement(alicePublicKey, bobPrivateKey); + byte[] sharedTwo = Curve.calculateAgreement(bobPublicKey, alicePrivateKey); + + assertTrue(Arrays.equals(sharedOne, shared)); + assertTrue(Arrays.equals(sharedTwo, shared)); + } + + public void testRandomAgreements() throws InvalidKeyException { + for (int i=0;i<50;i++) { + ECKeyPair alice = Curve.generateKeyPair(); + ECKeyPair bob = Curve.generateKeyPair(); + + byte[] sharedAlice = Curve.calculateAgreement(bob.getPublicKey(), alice.getPrivateKey()); + byte[] sharedBob = Curve.calculateAgreement(alice.getPublicKey(), bob.getPrivateKey()); + + assertTrue(Arrays.equals(sharedAlice, sharedBob)); + } + } + + public void testSignature() throws InvalidKeyException { + byte[] aliceIdentityPrivate = {(byte)0xc0, (byte)0x97, (byte)0x24, (byte)0x84, (byte)0x12, + (byte)0xe5, (byte)0x8b, (byte)0xf0, (byte)0x5d, (byte)0xf4, + (byte)0x87, (byte)0x96, (byte)0x82, (byte)0x05, (byte)0x13, + (byte)0x27, (byte)0x94, (byte)0x17, (byte)0x8e, (byte)0x36, + (byte)0x76, (byte)0x37, (byte)0xf5, (byte)0x81, (byte)0x8f, + (byte)0x81, (byte)0xe0, (byte)0xe6, (byte)0xce, (byte)0x73, + (byte)0xe8, (byte)0x65}; + + byte[] aliceIdentityPublic = {(byte)0x05, (byte)0xab, (byte)0x7e, (byte)0x71, (byte)0x7d, + (byte)0x4a, (byte)0x16, (byte)0x3b, (byte)0x7d, (byte)0x9a, + (byte)0x1d, (byte)0x80, (byte)0x71, (byte)0xdf, (byte)0xe9, + (byte)0xdc, (byte)0xf8, (byte)0xcd, (byte)0xcd, (byte)0x1c, + (byte)0xea, (byte)0x33, (byte)0x39, (byte)0xb6, (byte)0x35, + (byte)0x6b, (byte)0xe8, (byte)0x4d, (byte)0x88, (byte)0x7e, + (byte)0x32, (byte)0x2c, (byte)0x64}; + + byte[] aliceEphemeralPublic = {(byte)0x05, (byte)0xed, (byte)0xce, (byte)0x9d, (byte)0x9c, + (byte)0x41, (byte)0x5c, (byte)0xa7, (byte)0x8c, (byte)0xb7, + (byte)0x25, (byte)0x2e, (byte)0x72, (byte)0xc2, (byte)0xc4, + (byte)0xa5, (byte)0x54, (byte)0xd3, (byte)0xeb, (byte)0x29, + (byte)0x48, (byte)0x5a, (byte)0x0e, (byte)0x1d, (byte)0x50, + (byte)0x31, (byte)0x18, (byte)0xd1, (byte)0xa8, (byte)0x2d, + (byte)0x99, (byte)0xfb, (byte)0x4a}; + + byte[] aliceSignature = {(byte)0x5d, (byte)0xe8, (byte)0x8c, (byte)0xa9, (byte)0xa8, + (byte)0x9b, (byte)0x4a, (byte)0x11, (byte)0x5d, (byte)0xa7, + (byte)0x91, (byte)0x09, (byte)0xc6, (byte)0x7c, (byte)0x9c, + (byte)0x74, (byte)0x64, (byte)0xa3, (byte)0xe4, (byte)0x18, + (byte)0x02, (byte)0x74, (byte)0xf1, (byte)0xcb, (byte)0x8c, + (byte)0x63, (byte)0xc2, (byte)0x98, (byte)0x4e, (byte)0x28, + (byte)0x6d, (byte)0xfb, (byte)0xed, (byte)0xe8, (byte)0x2d, + (byte)0xeb, (byte)0x9d, (byte)0xcd, (byte)0x9f, (byte)0xae, + (byte)0x0b, (byte)0xfb, (byte)0xb8, (byte)0x21, (byte)0x56, + (byte)0x9b, (byte)0x3d, (byte)0x90, (byte)0x01, (byte)0xbd, + (byte)0x81, (byte)0x30, (byte)0xcd, (byte)0x11, (byte)0xd4, + (byte)0x86, (byte)0xce, (byte)0xf0, (byte)0x47, (byte)0xbd, + (byte)0x60, (byte)0xb8, (byte)0x6e, (byte)0x88}; + + ECPrivateKey alicePrivateKey = Curve.decodePrivatePoint(aliceIdentityPrivate); + ECPublicKey alicePublicKey = Curve.decodePoint(aliceIdentityPublic, 0); + ECPublicKey aliceEphemeral = Curve.decodePoint(aliceEphemeralPublic, 0); + + if (!Curve.verifySignature(alicePublicKey, aliceEphemeral.serialize(), aliceSignature)) { + throw new AssertionError("Sig verification failed!"); + } + + for (int i=0;i ciphertexts = new ArrayList<>(100); + + for (int i=0;i<100;i++) { + ciphertexts.add(aliceGroupCipher.encrypt("up the punks".getBytes())); + } + + while (ciphertexts.size() > 0) { + int index = randomInt() % ciphertexts.size(); + byte[] ciphertext = ciphertexts.remove(index); + byte[] plaintext = bobGroupCipher.decrypt(ciphertext); + + assertTrue(new String(plaintext).equals("up the punks")); + } + } + + public void testEncryptNoSession() { + InMemorySenderKeyStore aliceStore = new InMemorySenderKeyStore(); + GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, "groupWithBobInIt"); + try { + aliceGroupCipher.encrypt("up the punks".getBytes()); + throw new AssertionError("Should have failed!"); + } catch (NoSessionException nse) { + // good + } + } + + private int randomInt() { + try { + return SecureRandom.getInstance("SHA1PRNG").nextInt(Integer.MAX_VALUE); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } +} diff --git a/src/androidTest/java/org/whispersystems/test/groups/InMemorySenderKeyStore.java b/src/androidTest/java/org/whispersystems/test/groups/InMemorySenderKeyStore.java new file mode 100644 index 000000000..450d802d9 --- /dev/null +++ b/src/androidTest/java/org/whispersystems/test/groups/InMemorySenderKeyStore.java @@ -0,0 +1,33 @@ +package org.whispersystems.test.groups; + +import org.whispersystems.libaxolotl.groups.state.SenderKeyRecord; +import org.whispersystems.libaxolotl.groups.state.SenderKeyStore; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class InMemorySenderKeyStore implements SenderKeyStore { + + private final Map store = new HashMap<>(); + + @Override + public void storeSenderKey(String senderKeyId, SenderKeyRecord record) { + store.put(senderKeyId, record); + } + + @Override + public SenderKeyRecord loadSenderKey(String senderKeyId) { + try { + SenderKeyRecord record = store.get(senderKeyId); + + if (record == null) { + return new SenderKeyRecord(); + } else { + return new SenderKeyRecord(record.serialize()); + } + } catch (IOException e) { + throw new AssertionError(e); + } + } +} diff --git a/src/androidTest/java/org/whispersystems/test/kdf/HKDFTest.java b/src/androidTest/java/org/whispersystems/test/kdf/HKDFTest.java new file mode 100644 index 000000000..079bc4fa5 --- /dev/null +++ b/src/androidTest/java/org/whispersystems/test/kdf/HKDFTest.java @@ -0,0 +1,139 @@ +package org.whispersystems.test.kdf; + +import android.test.AndroidTestCase; + +import org.whispersystems.libaxolotl.kdf.HKDF; + +import java.util.Arrays; + +public class HKDFTest extends AndroidTestCase { + + public void testVectorV3() { + byte[] ikm = {0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b}; + + byte[] salt = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c}; + + byte[] info = {(byte) 0xf0, (byte) 0xf1, (byte) 0xf2, (byte) 0xf3, (byte) 0xf4, + (byte) 0xf5, (byte) 0xf6, (byte) 0xf7, (byte) 0xf8, (byte) 0xf9}; + + byte[] 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}; + + byte[] actualOutput = HKDF.createFor(3).deriveSecrets(ikm, salt, info, 42); + + assertTrue(Arrays.equals(okm, actualOutput)); + } + + public void testVectorLongV3() { + byte[] ikm = {(byte) 0x00, (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) 0x21, (byte) 0x22, + (byte) 0x23, (byte) 0x24, (byte) 0x25, (byte) 0x26, (byte) 0x27, + (byte) 0x28, (byte) 0x29, (byte) 0x2a, (byte) 0x2b, (byte) 0x2c, + (byte) 0x2d, (byte) 0x2e, (byte) 0x2f, (byte) 0x30, (byte) 0x31, + (byte) 0x32, (byte) 0x33, (byte) 0x34, (byte) 0x35, (byte) 0x36, + (byte) 0x37, (byte) 0x38, (byte) 0x39, (byte) 0x3a, (byte) 0x3b, + (byte) 0x3c, (byte) 0x3d, (byte) 0x3e, (byte) 0x3f, (byte) 0x40, + (byte) 0x41, (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, + (byte) 0x46, (byte) 0x47, (byte) 0x48, (byte) 0x49, (byte) 0x4a, + (byte) 0x4b, (byte) 0x4c, (byte) 0x4d, (byte) 0x4e, (byte) 0x4f}; + + byte[] salt = {(byte) 0x60, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, + (byte) 0x65, (byte) 0x66, (byte) 0x67, (byte) 0x68, (byte) 0x69, + (byte) 0x6a, (byte) 0x6b, (byte) 0x6c, (byte) 0x6d, (byte) 0x6e, + (byte) 0x6f, (byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, + (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77, (byte) 0x78, + (byte) 0x79, (byte) 0x7a, (byte) 0x7b, (byte) 0x7c, (byte) 0x7d, + (byte) 0x7e, (byte) 0x7f, (byte) 0x80, (byte) 0x81, (byte) 0x82, + (byte) 0x83, (byte) 0x84, (byte) 0x85, (byte) 0x86, (byte) 0x87, + (byte) 0x88, (byte) 0x89, (byte) 0x8a, (byte) 0x8b, (byte) 0x8c, + (byte) 0x8d, (byte) 0x8e, (byte) 0x8f, (byte) 0x90, (byte) 0x91, + (byte) 0x92, (byte) 0x93, (byte) 0x94, (byte) 0x95, (byte) 0x96, + (byte) 0x97, (byte) 0x98, (byte) 0x99, (byte) 0x9a, (byte) 0x9b, + (byte) 0x9c, (byte) 0x9d, (byte) 0x9e, (byte) 0x9f, (byte) 0xa0, + (byte) 0xa1, (byte) 0xa2, (byte) 0xa3, (byte) 0xa4, (byte) 0xa5, + (byte) 0xa6, (byte) 0xa7, (byte) 0xa8, (byte) 0xa9, (byte) 0xaa, + (byte) 0xab, (byte) 0xac, (byte) 0xad, (byte) 0xae, (byte) 0xaf}; + + byte[] info = {(byte) 0xb0, (byte) 0xb1, (byte) 0xb2, (byte) 0xb3, (byte) 0xb4, + (byte) 0xb5, (byte) 0xb6, (byte) 0xb7, (byte) 0xb8, (byte) 0xb9, + (byte) 0xba, (byte) 0xbb, (byte) 0xbc, (byte) 0xbd, (byte) 0xbe, + (byte) 0xbf, (byte) 0xc0, (byte) 0xc1, (byte) 0xc2, (byte) 0xc3, + (byte) 0xc4, (byte) 0xc5, (byte) 0xc6, (byte) 0xc7, (byte) 0xc8, + (byte) 0xc9, (byte) 0xca, (byte) 0xcb, (byte) 0xcc, (byte) 0xcd, + (byte) 0xce, (byte) 0xcf, (byte) 0xd0, (byte) 0xd1, (byte) 0xd2, + (byte) 0xd3, (byte) 0xd4, (byte) 0xd5, (byte) 0xd6, (byte) 0xd7, + (byte) 0xd8, (byte) 0xd9, (byte) 0xda, (byte) 0xdb, (byte) 0xdc, + (byte) 0xdd, (byte) 0xde, (byte) 0xdf, (byte) 0xe0, (byte) 0xe1, + (byte) 0xe2, (byte) 0xe3, (byte) 0xe4, (byte) 0xe5, (byte) 0xe6, + (byte) 0xe7, (byte) 0xe8, (byte) 0xe9, (byte) 0xea, (byte) 0xeb, + (byte) 0xec, (byte) 0xed, (byte) 0xee, (byte) 0xef, (byte) 0xf0, + (byte) 0xf1, (byte) 0xf2, (byte) 0xf3, (byte) 0xf4, (byte) 0xf5, + (byte) 0xf6, (byte) 0xf7, (byte) 0xf8, (byte) 0xf9, (byte) 0xfa, + (byte) 0xfb, (byte) 0xfc, (byte) 0xfd, (byte) 0xfe, (byte) 0xff}; + + byte[] okm = {(byte) 0xb1, (byte) 0x1e, (byte) 0x39, (byte) 0x8d, (byte) 0xc8, + (byte) 0x03, (byte) 0x27, (byte) 0xa1, (byte) 0xc8, (byte) 0xe7, + (byte) 0xf7, (byte) 0x8c, (byte) 0x59, (byte) 0x6a, (byte) 0x49, + (byte) 0x34, (byte) 0x4f, (byte) 0x01, (byte) 0x2e, (byte) 0xda, + (byte) 0x2d, (byte) 0x4e, (byte) 0xfa, (byte) 0xd8, (byte) 0xa0, + (byte) 0x50, (byte) 0xcc, (byte) 0x4c, (byte) 0x19, (byte) 0xaf, + (byte) 0xa9, (byte) 0x7c, (byte) 0x59, (byte) 0x04, (byte) 0x5a, + (byte) 0x99, (byte) 0xca, (byte) 0xc7, (byte) 0x82, (byte) 0x72, + (byte) 0x71, (byte) 0xcb, (byte) 0x41, (byte) 0xc6, (byte) 0x5e, + (byte) 0x59, (byte) 0x0e, (byte) 0x09, (byte) 0xda, (byte) 0x32, + (byte) 0x75, (byte) 0x60, (byte) 0x0c, (byte) 0x2f, (byte) 0x09, + (byte) 0xb8, (byte) 0x36, (byte) 0x77, (byte) 0x93, (byte) 0xa9, + (byte) 0xac, (byte) 0xa3, (byte) 0xdb, (byte) 0x71, (byte) 0xcc, + (byte) 0x30, (byte) 0xc5, (byte) 0x81, (byte) 0x79, (byte) 0xec, + (byte) 0x3e, (byte) 0x87, (byte) 0xc1, (byte) 0x4c, (byte) 0x01, + (byte) 0xd5, (byte) 0xc1, (byte) 0xf3, (byte) 0x43, (byte) 0x4f, + (byte) 0x1d, (byte) 0x87}; + + byte[] actualOutput = HKDF.createFor(3).deriveSecrets(ikm, salt, info, 82); + assertTrue(Arrays.equals(okm, actualOutput)); + } + + public void testVectorV2() { + byte[] ikm = {0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b}; + + byte[] salt = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c}; + + byte[] info = {(byte)0xf0, (byte)0xf1, (byte)0xf2, (byte)0xf3, (byte)0xf4, + (byte)0xf5, (byte)0xf6, (byte)0xf7, (byte)0xf8, (byte)0xf9}; + + byte[] okm = {(byte)0x6e, (byte)0xc2, (byte)0x55, (byte)0x6d, (byte)0x5d, + (byte)0x7b, (byte)0x1d, (byte)0x81, (byte)0xde, (byte)0xe4, + (byte)0x22, (byte)0x2a, (byte)0xd7, (byte)0x48, (byte)0x36, + (byte)0x95, (byte)0xdd, (byte)0xc9, (byte)0x8f, (byte)0x4f, + (byte)0x5f, (byte)0xab, (byte)0xc0, (byte)0xe0, (byte)0x20, + (byte)0x5d, (byte)0xc2, (byte)0xef, (byte)0x87, (byte)0x52, + (byte)0xd4, (byte)0x1e, (byte)0x04, (byte)0xe2, (byte)0xe2, + (byte)0x11, (byte)0x01, (byte)0xc6, (byte)0x8f, (byte)0xf0, + (byte)0x93, (byte)0x94, (byte)0xb8, (byte)0xad, (byte)0x0b, + (byte)0xdc, (byte)0xb9, (byte)0x60, (byte)0x9c, (byte)0xd4, + (byte)0xee, (byte)0x82, (byte)0xac, (byte)0x13, (byte)0x19, + (byte)0x9b, (byte)0x4a, (byte)0xa9, (byte)0xfd, (byte)0xa8, + (byte)0x99, (byte)0xda, (byte)0xeb, (byte)0xec}; + + byte[] actualOutput = HKDF.createFor(2).deriveSecrets(ikm, salt, info, 64); + assertTrue(Arrays.equals(okm, actualOutput)); + } +} diff --git a/src/androidTest/java/org/whispersystems/test/ratchet/ChainKeyTest.java b/src/androidTest/java/org/whispersystems/test/ratchet/ChainKeyTest.java new file mode 100644 index 000000000..1197f3ebe --- /dev/null +++ b/src/androidTest/java/org/whispersystems/test/ratchet/ChainKeyTest.java @@ -0,0 +1,59 @@ +package org.whispersystems.test.ratchet; + +import android.test.AndroidTestCase; + +import org.whispersystems.libaxolotl.kdf.HKDF; +import org.whispersystems.libaxolotl.ratchet.ChainKey; + +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +public class ChainKeyTest extends AndroidTestCase { + + public void testChainKeyDerivationV2() throws NoSuchAlgorithmException { + + byte[] seed = {(byte) 0x8a, (byte) 0xb7, (byte) 0x2d, (byte) 0x6f, (byte) 0x4c, + (byte) 0xc5, (byte) 0xac, (byte) 0x0d, (byte) 0x38, (byte) 0x7e, + (byte) 0xaf, (byte) 0x46, (byte) 0x33, (byte) 0x78, (byte) 0xdd, + (byte) 0xb2, (byte) 0x8e, (byte) 0xdd, (byte) 0x07, (byte) 0x38, + (byte) 0x5b, (byte) 0x1c, (byte) 0xb0, (byte) 0x12, (byte) 0x50, + (byte) 0xc7, (byte) 0x15, (byte) 0x98, (byte) 0x2e, (byte) 0x7a, + (byte) 0xd4, (byte) 0x8f}; + + byte[] messageKey = {(byte) 0x02, (byte) 0xa9, (byte) 0xaa, (byte) 0x6c, (byte) 0x7d, + (byte) 0xbd, (byte) 0x64, (byte) 0xf9, (byte) 0xd3, (byte) 0xaa, + (byte) 0x92, (byte) 0xf9, (byte) 0x2a, (byte) 0x27, (byte) 0x7b, + (byte) 0xf5, (byte) 0x46, (byte) 0x09, (byte) 0xda, (byte) 0xdf, + (byte) 0x0b, (byte) 0x00, (byte) 0x82, (byte) 0x8a, (byte) 0xcf, + (byte) 0xc6, (byte) 0x1e, (byte) 0x3c, (byte) 0x72, (byte) 0x4b, + (byte) 0x84, (byte) 0xa7}; + + byte[] macKey = {(byte) 0xbf, (byte) 0xbe, (byte) 0x5e, (byte) 0xfb, (byte) 0x60, + (byte) 0x30, (byte) 0x30, (byte) 0x52, (byte) 0x67, (byte) 0x42, + (byte) 0xe3, (byte) 0xee, (byte) 0x89, (byte) 0xc7, (byte) 0x02, + (byte) 0x4e, (byte) 0x88, (byte) 0x4e, (byte) 0x44, (byte) 0x0f, + (byte) 0x1f, (byte) 0xf3, (byte) 0x76, (byte) 0xbb, (byte) 0x23, + (byte) 0x17, (byte) 0xb2, (byte) 0xd6, (byte) 0x4d, (byte) 0xeb, + (byte) 0x7c, (byte) 0x83}; + + byte[] nextChainKey = {(byte) 0x28, (byte) 0xe8, (byte) 0xf8, (byte) 0xfe, (byte) 0xe5, + (byte) 0x4b, (byte) 0x80, (byte) 0x1e, (byte) 0xef, (byte) 0x7c, + (byte) 0x5c, (byte) 0xfb, (byte) 0x2f, (byte) 0x17, (byte) 0xf3, + (byte) 0x2c, (byte) 0x7b, (byte) 0x33, (byte) 0x44, (byte) 0x85, + (byte) 0xbb, (byte) 0xb7, (byte) 0x0f, (byte) 0xac, (byte) 0x6e, + (byte) 0xc1, (byte) 0x03, (byte) 0x42, (byte) 0xa2, (byte) 0x46, + (byte) 0xd1, (byte) 0x5d}; + + ChainKey chainKey = new ChainKey(HKDF.createFor(2), seed, 0); + + assertTrue(Arrays.equals(chainKey.getKey(), seed)); + assertTrue(Arrays.equals(chainKey.getMessageKeys().getCipherKey().getEncoded(), messageKey)); + assertTrue(Arrays.equals(chainKey.getMessageKeys().getMacKey().getEncoded(), macKey)); + assertTrue(Arrays.equals(chainKey.getNextChainKey().getKey(), nextChainKey)); + assertTrue(chainKey.getIndex() == 0); + assertTrue(chainKey.getMessageKeys().getCounter() == 0); + assertTrue(chainKey.getNextChainKey().getIndex() == 1); + assertTrue(chainKey.getNextChainKey().getMessageKeys().getCounter() == 1); + } + +} diff --git a/src/androidTest/java/org/whispersystems/test/ratchet/RatchetingSessionTest.java b/src/androidTest/java/org/whispersystems/test/ratchet/RatchetingSessionTest.java new file mode 100644 index 000000000..311aff906 --- /dev/null +++ b/src/androidTest/java/org/whispersystems/test/ratchet/RatchetingSessionTest.java @@ -0,0 +1,236 @@ +package org.whispersystems.test.ratchet; + +import android.test.AndroidTestCase; +import android.util.Log; + +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.IdentityKeyPair; +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.ecc.ECPrivateKey; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.ratchet.AliceAxolotlParameters; +import org.whispersystems.libaxolotl.ratchet.BobAxolotlParameters; +import org.whispersystems.libaxolotl.ratchet.RatchetingSession; +import org.whispersystems.libaxolotl.state.SessionState; +import org.whispersystems.libaxolotl.util.Hex; +import org.whispersystems.libaxolotl.util.guava.Optional; + +import java.util.Arrays; + +public class RatchetingSessionTest extends AndroidTestCase { + + public void testRatchetingSessionAsBob() throws InvalidKeyException { + byte[] bobPublic = {(byte) 0x05, (byte) 0x2c, (byte) 0xb4, (byte) 0x97, + (byte) 0x76, (byte) 0xb8, (byte) 0x77, (byte) 0x02, + (byte) 0x05, (byte) 0x74, (byte) 0x5a, (byte) 0x3a, + (byte) 0x6e, (byte) 0x24, (byte) 0xf5, (byte) 0x79, + (byte) 0xcd, (byte) 0xb4, (byte) 0xba, (byte) 0x7a, + (byte) 0x89, (byte) 0x04, (byte) 0x10, (byte) 0x05, + (byte) 0x92, (byte) 0x8e, (byte) 0xbb, (byte) 0xad, + (byte) 0xc9, (byte) 0xc0, (byte) 0x5a, (byte) 0xd4, + (byte) 0x58}; + + byte[] bobPrivate = {(byte) 0xa1, (byte) 0xca, (byte) 0xb4, (byte) 0x8f, + (byte) 0x7c, (byte) 0x89, (byte) 0x3f, (byte) 0xaf, + (byte) 0xa9, (byte) 0x88, (byte) 0x0a, (byte) 0x28, + (byte) 0xc3, (byte) 0xb4, (byte) 0x99, (byte) 0x9d, + (byte) 0x28, (byte) 0xd6, (byte) 0x32, (byte) 0x95, + (byte) 0x62, (byte) 0xd2, (byte) 0x7a, (byte) 0x4e, + (byte) 0xa4, (byte) 0xe2, (byte) 0x2e, (byte) 0x9f, + (byte) 0xf1, (byte) 0xbd, (byte) 0xd6, (byte) 0x5a}; + + byte[] bobIdentityPublic = {(byte) 0x05, (byte) 0xf1, (byte) 0xf4, (byte) 0x38, + (byte) 0x74, (byte) 0xf6, (byte) 0x96, (byte) 0x69, + (byte) 0x56, (byte) 0xc2, (byte) 0xdd, (byte) 0x47, + (byte) 0x3f, (byte) 0x8f, (byte) 0xa1, (byte) 0x5a, + (byte) 0xde, (byte) 0xb7, (byte) 0x1d, (byte) 0x1c, + (byte) 0xb9, (byte) 0x91, (byte) 0xb2, (byte) 0x34, + (byte) 0x16, (byte) 0x92, (byte) 0x32, (byte) 0x4c, + (byte) 0xef, (byte) 0xb1, (byte) 0xc5, (byte) 0xe6, + (byte) 0x26}; + + byte[] bobIdentityPrivate = {(byte) 0x48, (byte) 0x75, (byte) 0xcc, (byte) 0x69, + (byte) 0xdd, (byte) 0xf8, (byte) 0xea, (byte) 0x07, + (byte) 0x19, (byte) 0xec, (byte) 0x94, (byte) 0x7d, + (byte) 0x61, (byte) 0x08, (byte) 0x11, (byte) 0x35, + (byte) 0x86, (byte) 0x8d, (byte) 0x5f, (byte) 0xd8, + (byte) 0x01, (byte) 0xf0, (byte) 0x2c, (byte) 0x02, + (byte) 0x25, (byte) 0xe5, (byte) 0x16, (byte) 0xdf, + (byte) 0x21, (byte) 0x56, (byte) 0x60, (byte) 0x5e}; + + byte[] aliceBasePublic = {(byte) 0x05, (byte) 0x47, (byte) 0x2d, (byte) 0x1f, + (byte) 0xb1, (byte) 0xa9, (byte) 0x86, (byte) 0x2c, + (byte) 0x3a, (byte) 0xf6, (byte) 0xbe, (byte) 0xac, + (byte) 0xa8, (byte) 0x92, (byte) 0x02, (byte) 0x77, + (byte) 0xe2, (byte) 0xb2, (byte) 0x6f, (byte) 0x4a, + (byte) 0x79, (byte) 0x21, (byte) 0x3e, (byte) 0xc7, + (byte) 0xc9, (byte) 0x06, (byte) 0xae, (byte) 0xb3, + (byte) 0x5e, (byte) 0x03, (byte) 0xcf, (byte) 0x89, + (byte) 0x50}; + + byte[] aliceEphemeralPublic = {(byte) 0x05, (byte) 0x6c, (byte) 0x3e, (byte) 0x0d, + (byte) 0x1f, (byte) 0x52, (byte) 0x02, (byte) 0x83, + (byte) 0xef, (byte) 0xcc, (byte) 0x55, (byte) 0xfc, + (byte) 0xa5, (byte) 0xe6, (byte) 0x70, (byte) 0x75, + (byte) 0xb9, (byte) 0x04, (byte) 0x00, (byte) 0x7f, + (byte) 0x18, (byte) 0x81, (byte) 0xd1, (byte) 0x51, + (byte) 0xaf, (byte) 0x76, (byte) 0xdf, (byte) 0x18, + (byte) 0xc5, (byte) 0x1d, (byte) 0x29, (byte) 0xd3, + (byte) 0x4b}; + + byte[] aliceIdentityPublic = {(byte) 0x05, (byte) 0xb4, (byte) 0xa8, (byte) 0x45, + (byte) 0x56, (byte) 0x60, (byte) 0xad, (byte) 0xa6, + (byte) 0x5b, (byte) 0x40, (byte) 0x10, (byte) 0x07, + (byte) 0xf6, (byte) 0x15, (byte) 0xe6, (byte) 0x54, + (byte) 0x04, (byte) 0x17, (byte) 0x46, (byte) 0x43, + (byte) 0x2e, (byte) 0x33, (byte) 0x39, (byte) 0xc6, + (byte) 0x87, (byte) 0x51, (byte) 0x49, (byte) 0xbc, + (byte) 0xee, (byte) 0xfc, (byte) 0xb4, (byte) 0x2b, + (byte) 0x4a}; + + byte[] senderChain = {(byte)0xd2, (byte)0x2f, (byte)0xd5, (byte)0x6d, (byte)0x3f, + (byte)0xec, (byte)0x81, (byte)0x9c, (byte)0xf4, (byte)0xc3, + (byte)0xd5, (byte)0x0c, (byte)0x56, (byte)0xed, (byte)0xfb, + (byte)0x1c, (byte)0x28, (byte)0x0a, (byte)0x1b, (byte)0x31, + (byte)0x96, (byte)0x45, (byte)0x37, (byte)0xf1, (byte)0xd1, + (byte)0x61, (byte)0xe1, (byte)0xc9, (byte)0x31, (byte)0x48, + (byte)0xe3, (byte)0x6b}; + + IdentityKey bobIdentityKeyPublic = new IdentityKey(bobIdentityPublic, 0); + ECPrivateKey bobIdentityKeyPrivate = Curve.decodePrivatePoint(bobIdentityPrivate); + IdentityKeyPair bobIdentityKey = new IdentityKeyPair(bobIdentityKeyPublic, bobIdentityKeyPrivate); + ECPublicKey bobEphemeralPublicKey = Curve.decodePoint(bobPublic, 0); + ECPrivateKey bobEphemeralPrivateKey = Curve.decodePrivatePoint(bobPrivate); + ECKeyPair bobEphemeralKey = new ECKeyPair(bobEphemeralPublicKey, bobEphemeralPrivateKey); + ECKeyPair bobBaseKey = bobEphemeralKey; + + ECPublicKey aliceBasePublicKey = Curve.decodePoint(aliceBasePublic, 0); + ECPublicKey aliceEphemeralPublicKey = Curve.decodePoint(aliceEphemeralPublic, 0); + IdentityKey aliceIdentityPublicKey = new IdentityKey(aliceIdentityPublic, 0); + + BobAxolotlParameters parameters = BobAxolotlParameters.newBuilder() + .setOurIdentityKey(bobIdentityKey) + .setOurSignedPreKey(bobBaseKey) + .setOurRatchetKey(bobEphemeralKey) + .setOurOneTimePreKey(Optional.absent()) + .setTheirIdentityKey(aliceIdentityPublicKey) + .setTheirBaseKey(aliceBasePublicKey) + .create(); + + SessionState session = new SessionState(); + + RatchetingSession.initializeSession(session, 2, parameters); + + assertTrue(session.getLocalIdentityKey().equals(bobIdentityKey.getPublicKey())); + assertTrue(session.getRemoteIdentityKey().equals(aliceIdentityPublicKey)); + assertTrue(Arrays.equals(session.getSenderChainKey().getKey(), senderChain)); + } + + public void testRatchetingSessionAsAlice() throws InvalidKeyException { + byte[] bobPublic = {(byte) 0x05, (byte) 0x2c, (byte) 0xb4, (byte) 0x97, (byte) 0x76, + (byte) 0xb8, (byte) 0x77, (byte) 0x02, (byte) 0x05, (byte) 0x74, + (byte) 0x5a, (byte) 0x3a, (byte) 0x6e, (byte) 0x24, (byte) 0xf5, + (byte) 0x79, (byte) 0xcd, (byte) 0xb4, (byte) 0xba, (byte) 0x7a, + (byte) 0x89, (byte) 0x04, (byte) 0x10, (byte) 0x05, (byte) 0x92, + (byte) 0x8e, (byte) 0xbb, (byte) 0xad, (byte) 0xc9, (byte) 0xc0, + (byte) 0x5a, (byte) 0xd4, (byte) 0x58}; + + byte[] bobIdentityPublic = {(byte) 0x05, (byte) 0xf1, (byte) 0xf4, (byte) 0x38, (byte) 0x74, + (byte) 0xf6, (byte) 0x96, (byte) 0x69, (byte) 0x56, (byte) 0xc2, + (byte) 0xdd, (byte) 0x47, (byte) 0x3f, (byte) 0x8f, (byte) 0xa1, + (byte) 0x5a, (byte) 0xde, (byte) 0xb7, (byte) 0x1d, (byte) 0x1c, + (byte) 0xb9, (byte) 0x91, (byte) 0xb2, (byte) 0x34, (byte) 0x16, + (byte) 0x92, (byte) 0x32, (byte) 0x4c, (byte) 0xef, (byte) 0xb1, + (byte) 0xc5, (byte) 0xe6, (byte) 0x26}; + + byte[] aliceBasePublic = {(byte) 0x05, (byte) 0x47, (byte) 0x2d, (byte) 0x1f, (byte) 0xb1, + (byte) 0xa9, (byte) 0x86, (byte) 0x2c, (byte) 0x3a, (byte) 0xf6, + (byte) 0xbe, (byte) 0xac, (byte) 0xa8, (byte) 0x92, (byte) 0x02, + (byte) 0x77, (byte) 0xe2, (byte) 0xb2, (byte) 0x6f, (byte) 0x4a, + (byte) 0x79, (byte) 0x21, (byte) 0x3e, (byte) 0xc7, (byte) 0xc9, + (byte) 0x06, (byte) 0xae, (byte) 0xb3, (byte) 0x5e, (byte) 0x03, + (byte) 0xcf, (byte) 0x89, (byte) 0x50}; + + byte[] aliceBasePrivate = {(byte) 0x11, (byte) 0xae, (byte) 0x7c, (byte) 0x64, (byte) 0xd1, + (byte) 0xe6, (byte) 0x1c, (byte) 0xd5, (byte) 0x96, (byte) 0xb7, + (byte) 0x6a, (byte) 0x0d, (byte) 0xb5, (byte) 0x01, (byte) 0x26, + (byte) 0x73, (byte) 0x39, (byte) 0x1c, (byte) 0xae, (byte) 0x66, + (byte) 0xed, (byte) 0xbf, (byte) 0xcf, (byte) 0x07, (byte) 0x3b, + (byte) 0x4d, (byte) 0xa8, (byte) 0x05, (byte) 0x16, (byte) 0xa4, + (byte) 0x74, (byte) 0x49}; + + byte[] aliceEphemeralPublic = {(byte) 0x05, (byte) 0x6c, (byte) 0x3e, (byte) 0x0d, (byte) 0x1f, + (byte) 0x52, (byte) 0x02, (byte) 0x83, (byte) 0xef, (byte) 0xcc, + (byte) 0x55, (byte) 0xfc, (byte) 0xa5, (byte) 0xe6, (byte) 0x70, + (byte) 0x75, (byte) 0xb9, (byte) 0x04, (byte) 0x00, (byte) 0x7f, + (byte) 0x18, (byte) 0x81, (byte) 0xd1, (byte) 0x51, (byte) 0xaf, + (byte) 0x76, (byte) 0xdf, (byte) 0x18, (byte) 0xc5, (byte) 0x1d, + (byte) 0x29, (byte) 0xd3, (byte) 0x4b}; + + byte[] aliceEphemeralPrivate = {(byte) 0xd1, (byte) 0xba, (byte) 0x38, (byte) 0xce, (byte) 0xa9, + (byte) 0x17, (byte) 0x43, (byte) 0xd3, (byte) 0x39, (byte) 0x39, + (byte) 0xc3, (byte) 0x3c, (byte) 0x84, (byte) 0x98, (byte) 0x65, + (byte) 0x09, (byte) 0x28, (byte) 0x01, (byte) 0x61, (byte) 0xb8, + (byte) 0xb6, (byte) 0x0f, (byte) 0xc7, (byte) 0x87, (byte) 0x0c, + (byte) 0x59, (byte) 0x9c, (byte) 0x1d, (byte) 0x46, (byte) 0x20, + (byte) 0x12, (byte) 0x48}; + + byte[] aliceIdentityPublic = {(byte) 0x05, (byte) 0xb4, (byte) 0xa8, (byte) 0x45, (byte) 0x56, + (byte) 0x60, (byte) 0xad, (byte) 0xa6, (byte) 0x5b, (byte) 0x40, + (byte) 0x10, (byte) 0x07, (byte) 0xf6, (byte) 0x15, (byte) 0xe6, + (byte) 0x54, (byte) 0x04, (byte) 0x17, (byte) 0x46, (byte) 0x43, + (byte) 0x2e, (byte) 0x33, (byte) 0x39, (byte) 0xc6, (byte) 0x87, + (byte) 0x51, (byte) 0x49, (byte) 0xbc, (byte) 0xee, (byte) 0xfc, + (byte) 0xb4, (byte) 0x2b, (byte) 0x4a}; + + byte[] aliceIdentityPrivate = {(byte) 0x90, (byte) 0x40, (byte) 0xf0, (byte) 0xd4, (byte) 0xe0, + (byte) 0x9c, (byte) 0xf3, (byte) 0x8f, (byte) 0x6d, (byte) 0xc7, + (byte) 0xc1, (byte) 0x37, (byte) 0x79, (byte) 0xc9, (byte) 0x08, + (byte) 0xc0, (byte) 0x15, (byte) 0xa1, (byte) 0xda, (byte) 0x4f, + (byte) 0xa7, (byte) 0x87, (byte) 0x37, (byte) 0xa0, (byte) 0x80, + (byte) 0xeb, (byte) 0x0a, (byte) 0x6f, (byte) 0x4f, (byte) 0x5f, + (byte) 0x8f, (byte) 0x58}; + + byte[] receiverChain = {(byte) 0xd2, (byte) 0x2f, (byte) 0xd5, (byte) 0x6d, (byte) 0x3f, + (byte) 0xec, (byte) 0x81, (byte) 0x9c, (byte) 0xf4, (byte) 0xc3, + (byte) 0xd5, (byte) 0x0c, (byte) 0x56, (byte) 0xed, (byte) 0xfb, + (byte) 0x1c, (byte) 0x28, (byte) 0x0a, (byte) 0x1b, (byte) 0x31, + (byte) 0x96, (byte) 0x45, (byte) 0x37, (byte) 0xf1, (byte) 0xd1, + (byte) 0x61, (byte) 0xe1, (byte) 0xc9, (byte) 0x31, (byte) 0x48, + (byte) 0xe3, (byte) 0x6b}; + + IdentityKey bobIdentityKey = new IdentityKey(bobIdentityPublic, 0); + ECPublicKey bobEphemeralPublicKey = Curve.decodePoint(bobPublic, 0); + ECPublicKey bobBasePublicKey = bobEphemeralPublicKey; + ECPublicKey aliceBasePublicKey = Curve.decodePoint(aliceBasePublic, 0); + ECPrivateKey aliceBasePrivateKey = Curve.decodePrivatePoint(aliceBasePrivate); + ECKeyPair aliceBaseKey = new ECKeyPair(aliceBasePublicKey, aliceBasePrivateKey); + ECPublicKey aliceEphemeralPublicKey = Curve.decodePoint(aliceEphemeralPublic, 0); + ECPrivateKey aliceEphemeralPrivateKey = Curve.decodePrivatePoint(aliceEphemeralPrivate); + ECKeyPair aliceEphemeralKey = new ECKeyPair(aliceEphemeralPublicKey, aliceEphemeralPrivateKey); + IdentityKey aliceIdentityPublicKey = new IdentityKey(aliceIdentityPublic, 0); + ECPrivateKey aliceIdentityPrivateKey = Curve.decodePrivatePoint(aliceIdentityPrivate); + IdentityKeyPair aliceIdentityKey = new IdentityKeyPair(aliceIdentityPublicKey, aliceIdentityPrivateKey); + + SessionState session = new SessionState(); + + AliceAxolotlParameters parameters = AliceAxolotlParameters.newBuilder() + .setOurBaseKey(aliceBaseKey) + .setOurIdentityKey(aliceIdentityKey) + .setTheirIdentityKey(bobIdentityKey) + .setTheirSignedPreKey(bobBasePublicKey) + .setTheirRatchetKey(bobEphemeralPublicKey) + .setTheirOneTimePreKey(Optional.absent()) + .create(); + + RatchetingSession.initializeSession(session, 2, parameters); + + assertTrue(session.getLocalIdentityKey().equals(aliceIdentityKey.getPublicKey())); + assertTrue(session.getRemoteIdentityKey().equals(bobIdentityKey)); + assertTrue(Arrays.equals(session.getReceiverChainKey(bobEphemeralPublicKey).getKey(), + receiverChain)); + + } +} diff --git a/src/androidTest/java/org/whispersystems/test/ratchet/RootKeyTest.java b/src/androidTest/java/org/whispersystems/test/ratchet/RootKeyTest.java new file mode 100644 index 000000000..c48ef23d2 --- /dev/null +++ b/src/androidTest/java/org/whispersystems/test/ratchet/RootKeyTest.java @@ -0,0 +1,84 @@ +package org.whispersystems.test.ratchet; + +import android.test.AndroidTestCase; + +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.ecc.ECPrivateKey; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.kdf.HKDF; +import org.whispersystems.libaxolotl.ratchet.ChainKey; +import org.whispersystems.libaxolotl.ratchet.RootKey; +import org.whispersystems.libaxolotl.util.Pair; + +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +public class RootKeyTest extends AndroidTestCase { + + public void testRootKeyDerivationV2() throws NoSuchAlgorithmException, InvalidKeyException { + byte[] rootKeySeed = {(byte) 0x7b, (byte) 0xa6, (byte) 0xde, (byte) 0xbc, (byte) 0x2b, + (byte) 0xc1, (byte) 0xbb, (byte) 0xf9, (byte) 0x1a, (byte) 0xbb, + (byte) 0xc1, (byte) 0x36, (byte) 0x74, (byte) 0x04, (byte) 0x17, + (byte) 0x6c, (byte) 0xa6, (byte) 0x23, (byte) 0x09, (byte) 0x5b, + (byte) 0x7e, (byte) 0xc6, (byte) 0x6b, (byte) 0x45, (byte) 0xf6, + (byte) 0x02, (byte) 0xd9, (byte) 0x35, (byte) 0x38, (byte) 0x94, + (byte) 0x2d, (byte) 0xcc}; + + byte[] alicePublic = {(byte) 0x05, (byte) 0xee, (byte) 0x4f, (byte) 0xa6, (byte) 0xcd, + (byte) 0xc0, (byte) 0x30, (byte) 0xdf, (byte) 0x49, (byte) 0xec, + (byte) 0xd0, (byte) 0xba, (byte) 0x6c, (byte) 0xfc, (byte) 0xff, + (byte) 0xb2, (byte) 0x33, (byte) 0xd3, (byte) 0x65, (byte) 0xa2, + (byte) 0x7f, (byte) 0xad, (byte) 0xbe, (byte) 0xff, (byte) 0x77, + (byte) 0xe9, (byte) 0x63, (byte) 0xfc, (byte) 0xb1, (byte) 0x62, + (byte) 0x22, (byte) 0xe1, (byte) 0x3a}; + + byte[] alicePrivate = {(byte) 0x21, (byte) 0x68, (byte) 0x22, (byte) 0xec, (byte) 0x67, + (byte) 0xeb, (byte) 0x38, (byte) 0x04, (byte) 0x9e, (byte) 0xba, + (byte) 0xe7, (byte) 0xb9, (byte) 0x39, (byte) 0xba, (byte) 0xea, + (byte) 0xeb, (byte) 0xb1, (byte) 0x51, (byte) 0xbb, (byte) 0xb3, + (byte) 0x2d, (byte) 0xb8, (byte) 0x0f, (byte) 0xd3, (byte) 0x89, + (byte) 0x24, (byte) 0x5a, (byte) 0xc3, (byte) 0x7a, (byte) 0x94, + (byte) 0x8e, (byte) 0x50}; + + byte[] bobPublic = {(byte) 0x05, (byte) 0xab, (byte) 0xb8, (byte) 0xeb, (byte) 0x29, + (byte) 0xcc, (byte) 0x80, (byte) 0xb4, (byte) 0x71, (byte) 0x09, + (byte) 0xa2, (byte) 0x26, (byte) 0x5a, (byte) 0xbe, (byte) 0x97, + (byte) 0x98, (byte) 0x48, (byte) 0x54, (byte) 0x06, (byte) 0xe3, + (byte) 0x2d, (byte) 0xa2, (byte) 0x68, (byte) 0x93, (byte) 0x4a, + (byte) 0x95, (byte) 0x55, (byte) 0xe8, (byte) 0x47, (byte) 0x57, + (byte) 0x70, (byte) 0x8a, (byte) 0x30}; + + byte[] nextRoot = {(byte) 0xb1, (byte) 0x14, (byte) 0xf5, (byte) 0xde, (byte) 0x28, + (byte) 0x01, (byte) 0x19, (byte) 0x85, (byte) 0xe6, (byte) 0xeb, + (byte) 0xa2, (byte) 0x5d, (byte) 0x50, (byte) 0xe7, (byte) 0xec, + (byte) 0x41, (byte) 0xa9, (byte) 0xb0, (byte) 0x2f, (byte) 0x56, + (byte) 0x93, (byte) 0xc5, (byte) 0xc7, (byte) 0x88, (byte) 0xa6, + (byte) 0x3a, (byte) 0x06, (byte) 0xd2, (byte) 0x12, (byte) 0xa2, + (byte) 0xf7, (byte) 0x31}; + + byte[] nextChain = {(byte) 0x9d, (byte) 0x7d, (byte) 0x24, (byte) 0x69, (byte) 0xbc, + (byte) 0x9a, (byte) 0xe5, (byte) 0x3e, (byte) 0xe9, (byte) 0x80, + (byte) 0x5a, (byte) 0xa3, (byte) 0x26, (byte) 0x4d, (byte) 0x24, + (byte) 0x99, (byte) 0xa3, (byte) 0xac, (byte) 0xe8, (byte) 0x0f, + (byte) 0x4c, (byte) 0xca, (byte) 0xe2, (byte) 0xda, (byte) 0x13, + (byte) 0x43, (byte) 0x0c, (byte) 0x5c, (byte) 0x55, (byte) 0xb5, + (byte) 0xca, (byte) 0x5f}; + + ECPublicKey alicePublicKey = Curve.decodePoint(alicePublic, 0); + ECPrivateKey alicePrivateKey = Curve.decodePrivatePoint(alicePrivate); + ECKeyPair aliceKeyPair = new ECKeyPair(alicePublicKey, alicePrivateKey); + + ECPublicKey bobPublicKey = Curve.decodePoint(bobPublic, 0); + RootKey rootKey = new RootKey(HKDF.createFor(2), rootKeySeed); + + Pair rootKeyChainKeyPair = rootKey.createChain(bobPublicKey, aliceKeyPair); + RootKey nextRootKey = rootKeyChainKeyPair.first(); + ChainKey nextChainKey = rootKeyChainKeyPair.second(); + + assertTrue(Arrays.equals(rootKey.getKeyBytes(), rootKeySeed)); + assertTrue(Arrays.equals(nextRootKey.getKeyBytes(), nextRoot)); + assertTrue(Arrays.equals(nextChainKey.getKey(), nextChain)); + } +} diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml new file mode 100644 index 000000000..28d281688 --- /dev/null +++ b/src/main/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/src/main/java/org/whispersystems/libaxolotl/DuplicateMessageException.java b/src/main/java/org/whispersystems/libaxolotl/DuplicateMessageException.java new file mode 100644 index 000000000..913c01212 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/DuplicateMessageException.java @@ -0,0 +1,7 @@ +package org.whispersystems.libaxolotl; + +public class DuplicateMessageException extends Exception { + public DuplicateMessageException(String s) { + super(s); + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/IdentityKey.java b/src/main/java/org/whispersystems/libaxolotl/IdentityKey.java new file mode 100644 index 000000000..35af3394a --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/IdentityKey.java @@ -0,0 +1,66 @@ +/** + * Copyright (C) 2014 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.whispersystems.libaxolotl; + + +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.util.Hex; + +/** + * A class for representing an identity key. + * + * @author Moxie Marlinspike + */ + +public class IdentityKey { + + private final ECPublicKey publicKey; + + public IdentityKey(ECPublicKey publicKey) { + this.publicKey = publicKey; + } + + public IdentityKey(byte[] bytes, int offset) throws InvalidKeyException { + this.publicKey = Curve.decodePoint(bytes, offset); + } + + public ECPublicKey getPublicKey() { + return publicKey; + } + + public byte[] serialize() { + return publicKey.serialize(); + } + + public String getFingerprint() { + return Hex.toString(publicKey.serialize()); + } + + @Override + public boolean equals(Object other) { + if (other == null) return false; + if (!(other instanceof IdentityKey)) return false; + + return publicKey.equals(((IdentityKey) other).getPublicKey()); + } + + @Override + public int hashCode() { + return publicKey.hashCode(); + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/IdentityKeyPair.java b/src/main/java/org/whispersystems/libaxolotl/IdentityKeyPair.java new file mode 100644 index 000000000..c47df4bcd --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/IdentityKeyPair.java @@ -0,0 +1,66 @@ +/** + * Copyright (C) 2013 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.whispersystems.libaxolotl; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECPrivateKey; + +import static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure; + +/** + * Holder for public and private identity key pair. + * + * @author Moxie Marlinspike + */ +public class IdentityKeyPair { + + private final IdentityKey publicKey; + private final ECPrivateKey privateKey; + + public IdentityKeyPair(IdentityKey publicKey, ECPrivateKey privateKey) { + this.publicKey = publicKey; + this.privateKey = privateKey; + } + + public IdentityKeyPair(byte[] serialized) throws InvalidKeyException { + try { + IdentityKeyPairStructure structure = IdentityKeyPairStructure.parseFrom(serialized); + this.publicKey = new IdentityKey(structure.getPublicKey().toByteArray(), 0); + this.privateKey = Curve.decodePrivatePoint(structure.getPrivateKey().toByteArray()); + } catch (InvalidProtocolBufferException e) { + throw new InvalidKeyException(e); + } + } + + public IdentityKey getPublicKey() { + return publicKey; + } + + public ECPrivateKey getPrivateKey() { + return privateKey; + } + + public byte[] serialize() { + return IdentityKeyPairStructure.newBuilder() + .setPublicKey(ByteString.copyFrom(publicKey.serialize())) + .setPrivateKey(ByteString.copyFrom(privateKey.serialize())) + .build().toByteArray(); + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/InvalidKeyException.java b/src/main/java/org/whispersystems/libaxolotl/InvalidKeyException.java new file mode 100644 index 000000000..bb70df168 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/InvalidKeyException.java @@ -0,0 +1,34 @@ +/** + * Copyright (C) 2014 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.whispersystems.libaxolotl; + +public class InvalidKeyException extends Exception { + + public InvalidKeyException() {} + + public InvalidKeyException(String detailMessage) { + super(detailMessage); + } + + public InvalidKeyException(Throwable throwable) { + super(throwable); + } + + public InvalidKeyException(String detailMessage, Throwable throwable) { + super(detailMessage, throwable); + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/InvalidKeyIdException.java b/src/main/java/org/whispersystems/libaxolotl/InvalidKeyIdException.java new file mode 100644 index 000000000..c7c6bc016 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/InvalidKeyIdException.java @@ -0,0 +1,27 @@ +/** + * Copyright (C) 2014 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.whispersystems.libaxolotl; + +public class InvalidKeyIdException extends Exception { + public InvalidKeyIdException(String detailMessage) { + super(detailMessage); + } + + public InvalidKeyIdException(Throwable throwable) { + super(throwable); + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/InvalidMacException.java b/src/main/java/org/whispersystems/libaxolotl/InvalidMacException.java new file mode 100644 index 000000000..0afc6996e --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/InvalidMacException.java @@ -0,0 +1,28 @@ +/** + * Copyright (C) 2014 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.whispersystems.libaxolotl; + +public class InvalidMacException extends Exception { + + public InvalidMacException(String detailMessage) { + super(detailMessage); + } + + public InvalidMacException(Throwable throwable) { + super(throwable); + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/InvalidMessageException.java b/src/main/java/org/whispersystems/libaxolotl/InvalidMessageException.java new file mode 100644 index 000000000..063eb594a --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/InvalidMessageException.java @@ -0,0 +1,40 @@ +/** + * Copyright (C) 2014 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.whispersystems.libaxolotl; + +import java.util.List; + +public class InvalidMessageException extends Exception { + + public InvalidMessageException() {} + + public InvalidMessageException(String detailMessage) { + super(detailMessage); + } + + public InvalidMessageException(Throwable throwable) { + super(throwable); + } + + public InvalidMessageException(String detailMessage, Throwable throwable) { + super(detailMessage, throwable); + } + + public InvalidMessageException(String detailMessage, List exceptions) { + super(detailMessage, exceptions.get(0)); + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/InvalidVersionException.java b/src/main/java/org/whispersystems/libaxolotl/InvalidVersionException.java new file mode 100644 index 000000000..a13244436 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/InvalidVersionException.java @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2014 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.whispersystems.libaxolotl; + +public class InvalidVersionException extends Exception { + public InvalidVersionException(String detailMessage) { + super(detailMessage); + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/LegacyMessageException.java b/src/main/java/org/whispersystems/libaxolotl/LegacyMessageException.java new file mode 100644 index 000000000..dadde7540 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/LegacyMessageException.java @@ -0,0 +1,7 @@ +package org.whispersystems.libaxolotl; + +public class LegacyMessageException extends Exception { + public LegacyMessageException(String s) { + super(s); + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/NoSessionException.java b/src/main/java/org/whispersystems/libaxolotl/NoSessionException.java new file mode 100644 index 000000000..162035542 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/NoSessionException.java @@ -0,0 +1,11 @@ +package org.whispersystems.libaxolotl; + +public class NoSessionException extends Exception { + public NoSessionException(String s) { + super(s); + } + + public NoSessionException(Exception nested) { + super(nested); + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/SessionBuilder.java b/src/main/java/org/whispersystems/libaxolotl/SessionBuilder.java new file mode 100644 index 000000000..2d1c89692 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/SessionBuilder.java @@ -0,0 +1,411 @@ +package org.whispersystems.libaxolotl; + +import android.util.Log; + +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.protocol.CiphertextMessage; +import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage; +import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage; +import org.whispersystems.libaxolotl.ratchet.AliceAxolotlParameters; +import org.whispersystems.libaxolotl.ratchet.BobAxolotlParameters; +import org.whispersystems.libaxolotl.ratchet.RatchetingSession; +import org.whispersystems.libaxolotl.ratchet.SymmetricAxolotlParameters; +import org.whispersystems.libaxolotl.state.AxolotlStore; +import org.whispersystems.libaxolotl.state.IdentityKeyStore; +import org.whispersystems.libaxolotl.state.PreKeyBundle; +import org.whispersystems.libaxolotl.state.PreKeyStore; +import org.whispersystems.libaxolotl.state.SessionRecord; +import org.whispersystems.libaxolotl.state.SessionState; +import org.whispersystems.libaxolotl.state.SessionStore; +import org.whispersystems.libaxolotl.state.SignedPreKeyStore; +import org.whispersystems.libaxolotl.util.KeyHelper; +import org.whispersystems.libaxolotl.util.Medium; +import org.whispersystems.libaxolotl.util.guava.Optional; + +/** + * SessionBuilder is responsible for setting up encrypted sessions. + * Once a session has been established, {@link org.whispersystems.libaxolotl.SessionCipher} + * can be used to encrypt/decrypt messages in that session. + *

+ * Sessions are built from one of three different possible vectors: + *

    + *
  1. A {@link org.whispersystems.libaxolotl.state.PreKeyBundle} retrieved from a server.
  2. + *
  3. A {@link org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage} received from a client.
  4. + *
  5. A {@link org.whispersystems.libaxolotl.protocol.KeyExchangeMessage} sent to or received from a client.
  6. + *
+ * + * Sessions are constructed per recipientId + deviceId tuple. Remote logical users are identified + * by their recipientId, and each logical recipientId can have multiple physical devices. + * + * @author Moxie Marlinspike + */ +public class SessionBuilder { + + private static final String TAG = SessionBuilder.class.getSimpleName(); + + private final SessionStore sessionStore; + private final PreKeyStore preKeyStore; + private final SignedPreKeyStore signedPreKeyStore; + private final IdentityKeyStore identityKeyStore; + private final long recipientId; + private final int deviceId; + + /** + * Constructs a SessionBuilder. + * + * @param sessionStore The {@link org.whispersystems.libaxolotl.state.SessionStore} to store the constructed session in. + * @param preKeyStore The {@link org.whispersystems.libaxolotl.state.PreKeyStore} where the client's local {@link org.whispersystems.libaxolotl.state.PreKeyRecord}s are stored. + * @param identityKeyStore The {@link org.whispersystems.libaxolotl.state.IdentityKeyStore} containing the client's identity key information. + * @param recipientId The recipient ID of the remote user to build a session with. + * @param deviceId The device ID of the remote user's physical device. + */ + public SessionBuilder(SessionStore sessionStore, + PreKeyStore preKeyStore, + SignedPreKeyStore signedPreKeyStore, + IdentityKeyStore identityKeyStore, + long recipientId, int deviceId) + { + this.sessionStore = sessionStore; + this.preKeyStore = preKeyStore; + this.signedPreKeyStore = signedPreKeyStore; + this.identityKeyStore = identityKeyStore; + this.recipientId = recipientId; + this.deviceId = deviceId; + } + + /** + * Constructs a SessionBuilder + * @param store The {@link org.whispersystems.libaxolotl.state.AxolotlStore} to store all state information in. + * @param recipientId The recipient ID of the remote user to build a session with. + * @param deviceId The device ID of the remote user's physical device. + */ + public SessionBuilder(AxolotlStore store, long recipientId, int deviceId) { + this(store, store, store, store, recipientId, deviceId); + } + + /** + * Build a new session from a received {@link org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage}. + * + * After a session is constructed in this way, the embedded {@link org.whispersystems.libaxolotl.protocol.WhisperMessage} + * can be decrypted. + * + * @param message The received {@link org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage}. + * @throws org.whispersystems.libaxolotl.InvalidKeyIdException when there is no local + * {@link org.whispersystems.libaxolotl.state.PreKeyRecord} + * that corresponds to the PreKey ID in + * the message. + * @throws org.whispersystems.libaxolotl.InvalidKeyException when the message is formatted incorrectly. + * @throws org.whispersystems.libaxolotl.UntrustedIdentityException when the {@link IdentityKey} of the sender is untrusted. + */ + /*package*/ Optional process(SessionRecord sessionRecord, PreKeyWhisperMessage message) + throws InvalidKeyIdException, InvalidKeyException, UntrustedIdentityException + { + int messageVersion = message.getMessageVersion(); + IdentityKey theirIdentityKey = message.getIdentityKey(); + + Optional unsignedPreKeyId; + + if (!identityKeyStore.isTrustedIdentity(recipientId, theirIdentityKey)) { + throw new UntrustedIdentityException(); + } + + switch (messageVersion) { + case 2: unsignedPreKeyId = processV2(sessionRecord, message); break; + case 3: unsignedPreKeyId = processV3(sessionRecord, message); break; + default: throw new AssertionError("Unknown version: " + messageVersion); + } + + identityKeyStore.saveIdentity(recipientId, theirIdentityKey); + return unsignedPreKeyId; + } + + private Optional processV3(SessionRecord sessionRecord, PreKeyWhisperMessage message) + throws UntrustedIdentityException, InvalidKeyIdException, InvalidKeyException + { + + if (sessionRecord.hasSessionState(message.getMessageVersion(), message.getBaseKey().serialize())) { + Log.w(TAG, "We've already setup a session for this V3 message, letting bundled message fall through..."); + return Optional.absent(); + } + + ECKeyPair ourSignedPreKey = signedPreKeyStore.loadSignedPreKey(message.getSignedPreKeyId()).getKeyPair(); + + BobAxolotlParameters.Builder parameters = BobAxolotlParameters.newBuilder(); + + parameters.setTheirBaseKey(message.getBaseKey()) + .setTheirIdentityKey(message.getIdentityKey()) + .setOurIdentityKey(identityKeyStore.getIdentityKeyPair()) + .setOurSignedPreKey(ourSignedPreKey) + .setOurRatchetKey(ourSignedPreKey); + + if (message.getPreKeyId().isPresent()) { + parameters.setOurOneTimePreKey(Optional.of(preKeyStore.loadPreKey(message.getPreKeyId().get()).getKeyPair())); + } else { + parameters.setOurOneTimePreKey(Optional.absent()); + } + + if (!sessionRecord.isFresh()) sessionRecord.archiveCurrentState(); + + RatchetingSession.initializeSession(sessionRecord.getSessionState(), message.getMessageVersion(), parameters.create()); + + sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId()); + sessionRecord.getSessionState().setRemoteRegistrationId(message.getRegistrationId()); + sessionRecord.getSessionState().setAliceBaseKey(message.getBaseKey().serialize()); + + if (message.getPreKeyId().isPresent() && message.getPreKeyId().get() != Medium.MAX_VALUE) { + return message.getPreKeyId(); + } else { + return Optional.absent(); + } + } + + private Optional processV2(SessionRecord sessionRecord, PreKeyWhisperMessage message) + throws UntrustedIdentityException, InvalidKeyIdException, InvalidKeyException + { + if (!message.getPreKeyId().isPresent()) { + throw new InvalidKeyIdException("V2 message requires one time prekey id!"); + } + + if (!preKeyStore.containsPreKey(message.getPreKeyId().get()) && + sessionStore.containsSession(recipientId, deviceId)) + { + Log.w(TAG, "We've already processed the prekey part of this V2 session, letting bundled message fall through..."); + return Optional.absent(); + } + + ECKeyPair ourPreKey = preKeyStore.loadPreKey(message.getPreKeyId().get()).getKeyPair(); + + BobAxolotlParameters.Builder parameters = BobAxolotlParameters.newBuilder(); + + parameters.setOurIdentityKey(identityKeyStore.getIdentityKeyPair()) + .setOurSignedPreKey(ourPreKey) + .setOurRatchetKey(ourPreKey) + .setOurOneTimePreKey(Optional.absent()) + .setTheirIdentityKey(message.getIdentityKey()) + .setTheirBaseKey(message.getBaseKey()); + + if (!sessionRecord.isFresh()) sessionRecord.archiveCurrentState(); + + RatchetingSession.initializeSession(sessionRecord.getSessionState(), message.getMessageVersion(), parameters.create()); + + sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId()); + sessionRecord.getSessionState().setRemoteRegistrationId(message.getRegistrationId()); + sessionRecord.getSessionState().setAliceBaseKey(message.getBaseKey().serialize()); + + if (message.getPreKeyId().get() != Medium.MAX_VALUE) { + return message.getPreKeyId(); + } else { + return Optional.absent(); + } + } + + /** + * Build a new session from a {@link org.whispersystems.libaxolotl.state.PreKeyBundle} retrieved from + * a server. + * + * @param preKey A PreKey for the destination recipient, retrieved from a server. + * @throws InvalidKeyException when the {@link org.whispersystems.libaxolotl.state.PreKeyBundle} is + * badly formatted. + * @throws org.whispersystems.libaxolotl.UntrustedIdentityException when the sender's + * {@link IdentityKey} is not + * trusted. + */ + public void process(PreKeyBundle preKey) throws InvalidKeyException, UntrustedIdentityException { + synchronized (SessionCipher.SESSION_LOCK) { + if (!identityKeyStore.isTrustedIdentity(recipientId, preKey.getIdentityKey())) { + throw new UntrustedIdentityException(); + } + + if (preKey.getSignedPreKey() != null && + !Curve.verifySignature(preKey.getIdentityKey().getPublicKey(), + preKey.getSignedPreKey().serialize(), + preKey.getSignedPreKeySignature())) + { + throw new InvalidKeyException("Invalid signature on device key!"); + } + + if (preKey.getSignedPreKey() == null && preKey.getPreKey() == null) { + throw new InvalidKeyException("Both signed and unsigned prekeys are absent!"); + } + + boolean supportsV3 = preKey.getSignedPreKey() != null; + SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId); + ECKeyPair ourBaseKey = Curve.generateKeyPair(); + ECPublicKey theirSignedPreKey = supportsV3 ? preKey.getSignedPreKey() : preKey.getPreKey(); + Optional theirOneTimePreKey = Optional.fromNullable(preKey.getPreKey()); + Optional theirOneTimePreKeyId = theirOneTimePreKey.isPresent() ? Optional.of(preKey.getPreKeyId()) : + Optional.absent(); + + AliceAxolotlParameters.Builder parameters = AliceAxolotlParameters.newBuilder(); + + parameters.setOurBaseKey(ourBaseKey) + .setOurIdentityKey(identityKeyStore.getIdentityKeyPair()) + .setTheirIdentityKey(preKey.getIdentityKey()) + .setTheirSignedPreKey(theirSignedPreKey) + .setTheirRatchetKey(theirSignedPreKey) + .setTheirOneTimePreKey(supportsV3 ? theirOneTimePreKey : Optional.absent()); + + if (!sessionRecord.isFresh()) sessionRecord.archiveCurrentState(); + + RatchetingSession.initializeSession(sessionRecord.getSessionState(), + supportsV3 ? 3 : 2, + parameters.create()); + + sessionRecord.getSessionState().setUnacknowledgedPreKeyMessage(theirOneTimePreKeyId, preKey.getSignedPreKeyId(), ourBaseKey.getPublicKey()); + sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId()); + sessionRecord.getSessionState().setRemoteRegistrationId(preKey.getRegistrationId()); + sessionRecord.getSessionState().setAliceBaseKey(ourBaseKey.getPublicKey().serialize()); + + sessionStore.storeSession(recipientId, deviceId, sessionRecord); + identityKeyStore.saveIdentity(recipientId, preKey.getIdentityKey()); + } + } + + /** + * Build a new session from a {@link org.whispersystems.libaxolotl.protocol.KeyExchangeMessage} + * received from a remote client. + * + * @param message The received KeyExchangeMessage. + * @return The KeyExchangeMessage to respond with, or null if no response is necessary. + * @throws InvalidKeyException if the received KeyExchangeMessage is badly formatted. + */ + public KeyExchangeMessage process(KeyExchangeMessage message) + throws InvalidKeyException, UntrustedIdentityException, StaleKeyExchangeException + { + synchronized (SessionCipher.SESSION_LOCK) { + if (!identityKeyStore.isTrustedIdentity(recipientId, message.getIdentityKey())) { + throw new UntrustedIdentityException(); + } + + KeyExchangeMessage responseMessage = null; + + if (message.isInitiate()) responseMessage = processInitiate(message); + else processResponse(message); + + return responseMessage; + } + } + + private KeyExchangeMessage processInitiate(KeyExchangeMessage message) throws InvalidKeyException { + int flags = KeyExchangeMessage.RESPONSE_FLAG; + SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId); + + if (message.getVersion() >= 3 && + !Curve.verifySignature(message.getIdentityKey().getPublicKey(), + message.getBaseKey().serialize(), + message.getBaseKeySignature())) + { + throw new InvalidKeyException("Bad signature!"); + } + + SymmetricAxolotlParameters.Builder builder = SymmetricAxolotlParameters.newBuilder(); + + if (!sessionRecord.getSessionState().hasPendingKeyExchange()) { + builder.setOurIdentityKey(identityKeyStore.getIdentityKeyPair()) + .setOurBaseKey(Curve.generateKeyPair()) + .setOurRatchetKey(Curve.generateKeyPair()); + } else { + builder.setOurIdentityKey(sessionRecord.getSessionState().getPendingKeyExchangeIdentityKey()) + .setOurBaseKey(sessionRecord.getSessionState().getPendingKeyExchangeBaseKey()) + .setOurRatchetKey(sessionRecord.getSessionState().getPendingKeyExchangeRatchetKey()); + flags |= KeyExchangeMessage.SIMULTAENOUS_INITIATE_FLAG; + } + + builder.setTheirBaseKey(message.getBaseKey()) + .setTheirRatchetKey(message.getRatchetKey()) + .setTheirIdentityKey(message.getIdentityKey()); + + SymmetricAxolotlParameters parameters = builder.create(); + + if (!sessionRecord.isFresh()) sessionRecord.archiveCurrentState(); + + RatchetingSession.initializeSession(sessionRecord.getSessionState(), + Math.min(message.getMaxVersion(), CiphertextMessage.CURRENT_VERSION), + parameters); + + sessionStore.storeSession(recipientId, deviceId, sessionRecord); + identityKeyStore.saveIdentity(recipientId, message.getIdentityKey()); + + byte[] baseKeySignature = Curve.calculateSignature(parameters.getOurIdentityKey().getPrivateKey(), + parameters.getOurBaseKey().getPublicKey().serialize()); + + return new KeyExchangeMessage(sessionRecord.getSessionState().getSessionVersion(), + message.getSequence(), flags, + parameters.getOurBaseKey().getPublicKey(), + baseKeySignature, parameters.getOurRatchetKey().getPublicKey(), + parameters.getOurIdentityKey().getPublicKey()); + } + + private void processResponse(KeyExchangeMessage message) + throws StaleKeyExchangeException, InvalidKeyException + { + SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId); + SessionState sessionState = sessionRecord.getSessionState(); + boolean hasPendingKeyExchange = sessionState.hasPendingKeyExchange(); + boolean isSimultaneousInitiateResponse = message.isResponseForSimultaneousInitiate(); + + if (!hasPendingKeyExchange || sessionState.getPendingKeyExchangeSequence() != message.getSequence()) { + Log.w(TAG, "No matching sequence for response. Is simultaneous initiate response: " + isSimultaneousInitiateResponse); + if (!isSimultaneousInitiateResponse) throw new StaleKeyExchangeException(); + else return; + } + + SymmetricAxolotlParameters.Builder parameters = SymmetricAxolotlParameters.newBuilder(); + + parameters.setOurBaseKey(sessionRecord.getSessionState().getPendingKeyExchangeBaseKey()) + .setOurRatchetKey(sessionRecord.getSessionState().getPendingKeyExchangeRatchetKey()) + .setOurIdentityKey(sessionRecord.getSessionState().getPendingKeyExchangeIdentityKey()) + .setTheirBaseKey(message.getBaseKey()) + .setTheirRatchetKey(message.getRatchetKey()) + .setTheirIdentityKey(message.getIdentityKey()); + + if (!sessionRecord.isFresh()) sessionRecord.archiveCurrentState(); + + RatchetingSession.initializeSession(sessionRecord.getSessionState(), + Math.min(message.getMaxVersion(), CiphertextMessage.CURRENT_VERSION), + parameters.create()); + + if (sessionRecord.getSessionState().getSessionVersion() >= 3 && + !Curve.verifySignature(message.getIdentityKey().getPublicKey(), + message.getBaseKey().serialize(), + message.getBaseKeySignature())) + { + throw new InvalidKeyException("Base key signature doesn't match!"); + } + + sessionStore.storeSession(recipientId, deviceId, sessionRecord); + identityKeyStore.saveIdentity(recipientId, message.getIdentityKey()); + + } + + /** + * Initiate a new session by sending an initial KeyExchangeMessage to the recipient. + * + * @return the KeyExchangeMessage to deliver. + */ + public KeyExchangeMessage process() { + synchronized (SessionCipher.SESSION_LOCK) { + try { + int sequence = KeyHelper.getRandomSequence(65534) + 1; + int flags = KeyExchangeMessage.INITIATE_FLAG; + ECKeyPair baseKey = Curve.generateKeyPair(); + ECKeyPair ratchetKey = Curve.generateKeyPair(); + IdentityKeyPair identityKey = identityKeyStore.getIdentityKeyPair(); + byte[] baseKeySignature = Curve.calculateSignature(identityKey.getPrivateKey(), baseKey.getPublicKey().serialize()); + SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId); + + sessionRecord.getSessionState().setPendingKeyExchange(sequence, baseKey, ratchetKey, identityKey); + sessionStore.storeSession(recipientId, deviceId, sessionRecord); + + return new KeyExchangeMessage(2, sequence, flags, baseKey.getPublicKey(), baseKeySignature, + ratchetKey.getPublicKey(), identityKey.getPublicKey()); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + } + + +} diff --git a/src/main/java/org/whispersystems/libaxolotl/SessionCipher.java b/src/main/java/org/whispersystems/libaxolotl/SessionCipher.java new file mode 100644 index 000000000..381dedb88 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/SessionCipher.java @@ -0,0 +1,469 @@ +/** + * Copyright (C) 2013 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.whispersystems.libaxolotl; + +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.protocol.CiphertextMessage; +import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage; +import org.whispersystems.libaxolotl.protocol.WhisperMessage; +import org.whispersystems.libaxolotl.ratchet.ChainKey; +import org.whispersystems.libaxolotl.ratchet.MessageKeys; +import org.whispersystems.libaxolotl.ratchet.RootKey; +import org.whispersystems.libaxolotl.state.AxolotlStore; +import org.whispersystems.libaxolotl.state.IdentityKeyStore; +import org.whispersystems.libaxolotl.state.PreKeyStore; +import org.whispersystems.libaxolotl.state.SessionRecord; +import org.whispersystems.libaxolotl.state.SessionState; +import org.whispersystems.libaxolotl.state.SessionStore; +import org.whispersystems.libaxolotl.state.SignedPreKeyStore; +import org.whispersystems.libaxolotl.util.ByteUtil; +import org.whispersystems.libaxolotl.util.Pair; +import org.whispersystems.libaxolotl.util.guava.Optional; + +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import static org.whispersystems.libaxolotl.state.SessionState.UnacknowledgedPreKeyMessageItems; + +/** + * The main entry point for Axolotl encrypt/decrypt operations. + * + * Once a session has been established with {@link SessionBuilder}, + * this class can be used for all encrypt/decrypt operations within + * that session. + * + * @author Moxie Marlinspike + */ +public class SessionCipher { + + public static final Object SESSION_LOCK = new Object(); + + private final SessionStore sessionStore; + private final SessionBuilder sessionBuilder; + private final PreKeyStore preKeyStore; + private final long recipientId; + private final int deviceId; + + /** + * Construct a SessionCipher for encrypt/decrypt operations on a session. + * In order to use SessionCipher, a session must have already been created + * and stored using {@link SessionBuilder}. + * + * @param sessionStore The {@link SessionStore} that contains a session for this recipient. + * @param recipientId The remote ID that messages will be encrypted to or decrypted from. + * @param deviceId The device corresponding to the recipientId. + */ + public SessionCipher(SessionStore sessionStore, PreKeyStore preKeyStore, + SignedPreKeyStore signedPreKeyStore, IdentityKeyStore identityKeyStore, + long recipientId, int deviceId) + { + this.sessionStore = sessionStore; + this.recipientId = recipientId; + this.deviceId = deviceId; + this.preKeyStore = preKeyStore; + this.sessionBuilder = new SessionBuilder(sessionStore, preKeyStore, signedPreKeyStore, + identityKeyStore, recipientId, deviceId); + } + + public SessionCipher(AxolotlStore store, long recipientId, int deviceId) { + this(store, store, store, store, recipientId, deviceId); + } + + /** + * Encrypt a message. + * + * @param paddedMessage The plaintext message bytes, optionally padded to a constant multiple. + * @return A ciphertext message encrypted to the recipient+device tuple. + */ + public CiphertextMessage encrypt(byte[] paddedMessage) { + synchronized (SESSION_LOCK) { + SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId); + SessionState sessionState = sessionRecord.getSessionState(); + ChainKey chainKey = sessionState.getSenderChainKey(); + MessageKeys messageKeys = chainKey.getMessageKeys(); + ECPublicKey senderEphemeral = sessionState.getSenderRatchetKey(); + int previousCounter = sessionState.getPreviousCounter(); + int sessionVersion = sessionState.getSessionVersion(); + + byte[] ciphertextBody = getCiphertext(sessionVersion, messageKeys, paddedMessage); + CiphertextMessage ciphertextMessage = new WhisperMessage(sessionVersion, messageKeys.getMacKey(), + senderEphemeral, chainKey.getIndex(), + previousCounter, ciphertextBody, + sessionState.getLocalIdentityKey(), + sessionState.getRemoteIdentityKey()); + + if (sessionState.hasUnacknowledgedPreKeyMessage()) { + UnacknowledgedPreKeyMessageItems items = sessionState.getUnacknowledgedPreKeyMessageItems(); + int localRegistrationId = sessionState.getLocalRegistrationId(); + + ciphertextMessage = new PreKeyWhisperMessage(sessionVersion, localRegistrationId, items.getPreKeyId(), + items.getSignedPreKeyId(), items.getBaseKey(), + sessionState.getLocalIdentityKey(), + (WhisperMessage) ciphertextMessage); + } + + sessionState.setSenderChainKey(chainKey.getNextChainKey()); + sessionStore.storeSession(recipientId, deviceId, sessionRecord); + return ciphertextMessage; + } + } + + /** + * Decrypt a message. + * + * @param ciphertext The {@link PreKeyWhisperMessage} to decrypt. + * + * @return The plaintext. + * @throws InvalidMessageException if the input is not valid ciphertext. + * @throws DuplicateMessageException if the input is a message that has already been received. + * @throws LegacyMessageException if the input is a message formatted by a protocol version that + * is no longer supported. + * @throws InvalidKeyIdException when there is no local {@link org.whispersystems.libaxolotl.state.PreKeyRecord} + * that corresponds to the PreKey ID in the message. + * @throws InvalidKeyException when the message is formatted incorrectly. + * @throws UntrustedIdentityException when the {@link IdentityKey} of the sender is untrusted. + */ + public byte[] decrypt(PreKeyWhisperMessage ciphertext) + throws DuplicateMessageException, LegacyMessageException, InvalidMessageException, + InvalidKeyIdException, InvalidKeyException, UntrustedIdentityException + { + return decrypt(ciphertext, new NullDecryptionCallback()); + } + + /** + * Decrypt a message. + * + * @param ciphertext The {@link PreKeyWhisperMessage} to decrypt. + * @param callback A callback that is triggered after decryption is complete, + * but before the updated session state has been committed to the session + * DB. This allows some implementations to store the committed plaintext + * to a DB first, in case they are concerned with a crash happening between + * the time the session state is updated but before they're able to store + * the plaintext to disk. + * + * @return The plaintext. + * @throws InvalidMessageException if the input is not valid ciphertext. + * @throws DuplicateMessageException if the input is a message that has already been received. + * @throws LegacyMessageException if the input is a message formatted by a protocol version that + * is no longer supported. + * @throws InvalidKeyIdException when there is no local {@link org.whispersystems.libaxolotl.state.PreKeyRecord} + * that corresponds to the PreKey ID in the message. + * @throws InvalidKeyException when the message is formatted incorrectly. + * @throws UntrustedIdentityException when the {@link IdentityKey} of the sender is untrusted. + */ + public byte[] decrypt(PreKeyWhisperMessage ciphertext, DecryptionCallback callback) + throws DuplicateMessageException, LegacyMessageException, InvalidMessageException, + InvalidKeyIdException, InvalidKeyException, UntrustedIdentityException + { + synchronized (SESSION_LOCK) { + SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId); + Optional unsignedPreKeyId = sessionBuilder.process(sessionRecord, ciphertext); + byte[] plaintext = decrypt(sessionRecord, ciphertext.getWhisperMessage()); + + callback.handlePlaintext(plaintext); + + sessionStore.storeSession(recipientId, deviceId, sessionRecord); + + if (unsignedPreKeyId.isPresent()) { + preKeyStore.removePreKey(unsignedPreKeyId.get()); + } + + return plaintext; + } + } + + /** + * Decrypt a message. + * + * @param ciphertext The {@link WhisperMessage} to decrypt. + * + * @return The plaintext. + * @throws InvalidMessageException if the input is not valid ciphertext. + * @throws DuplicateMessageException if the input is a message that has already been received. + * @throws LegacyMessageException if the input is a message formatted by a protocol version that + * is no longer supported. + * @throws NoSessionException if there is no established session for this contact. + */ + public byte[] decrypt(WhisperMessage ciphertext) + throws InvalidMessageException, DuplicateMessageException, LegacyMessageException, + NoSessionException + { + return decrypt(ciphertext, new NullDecryptionCallback()); + } + + /** + * Decrypt a message. + * + * @param ciphertext The {@link WhisperMessage} to decrypt. + * @param callback A callback that is triggered after decryption is complete, + * but before the updated session state has been committed to the session + * DB. This allows some implementations to store the committed plaintext + * to a DB first, in case they are concerned with a crash happening between + * the time the session state is updated but before they're able to store + * the plaintext to disk. + * + * @return The plaintext. + * @throws InvalidMessageException if the input is not valid ciphertext. + * @throws DuplicateMessageException if the input is a message that has already been received. + * @throws LegacyMessageException if the input is a message formatted by a protocol version that + * is no longer supported. + * @throws NoSessionException if there is no established session for this contact. + */ + public byte[] decrypt(WhisperMessage ciphertext, DecryptionCallback callback) + throws InvalidMessageException, DuplicateMessageException, LegacyMessageException, + NoSessionException + { + synchronized (SESSION_LOCK) { + + if (!sessionStore.containsSession(recipientId, deviceId)) { + throw new NoSessionException("No session for: " + recipientId + ", " + deviceId); + } + + SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId); + byte[] plaintext = decrypt(sessionRecord, ciphertext); + + callback.handlePlaintext(plaintext); + + sessionStore.storeSession(recipientId, deviceId, sessionRecord); + + return plaintext; + } + } + + private byte[] decrypt(SessionRecord sessionRecord, WhisperMessage ciphertext) + throws DuplicateMessageException, LegacyMessageException, InvalidMessageException + { + synchronized (SESSION_LOCK) { + Iterator previousStates = sessionRecord.getPreviousSessionStates().iterator(); + List exceptions = new LinkedList<>(); + + try { + SessionState sessionState = new SessionState(sessionRecord.getSessionState()); + byte[] plaintext = decrypt(sessionState, ciphertext); + + sessionRecord.setState(sessionState); + return plaintext; + } catch (InvalidMessageException e) { + exceptions.add(e); + } + + while (previousStates.hasNext()) { + try { + SessionState promotedState = new SessionState(previousStates.next()); + byte[] plaintext = decrypt(promotedState, ciphertext); + + previousStates.remove(); + sessionRecord.promoteState(promotedState); + + return plaintext; + } catch (InvalidMessageException e) { + exceptions.add(e); + } + } + + throw new InvalidMessageException("No valid sessions.", exceptions); + } + } + + private byte[] decrypt(SessionState sessionState, WhisperMessage ciphertextMessage) + throws InvalidMessageException, DuplicateMessageException, LegacyMessageException + { + if (!sessionState.hasSenderChain()) { + throw new InvalidMessageException("Uninitialized session!"); + } + + if (ciphertextMessage.getMessageVersion() != sessionState.getSessionVersion()) { + throw new InvalidMessageException(String.format("Message version %d, but session version %d", + ciphertextMessage.getMessageVersion(), + sessionState.getSessionVersion())); + } + + int messageVersion = ciphertextMessage.getMessageVersion(); + ECPublicKey theirEphemeral = ciphertextMessage.getSenderRatchetKey(); + int counter = ciphertextMessage.getCounter(); + ChainKey chainKey = getOrCreateChainKey(sessionState, theirEphemeral); + MessageKeys messageKeys = getOrCreateMessageKeys(sessionState, theirEphemeral, + chainKey, counter); + + ciphertextMessage.verifyMac(messageVersion, + sessionState.getRemoteIdentityKey(), + sessionState.getLocalIdentityKey(), + messageKeys.getMacKey()); + + byte[] plaintext = getPlaintext(messageVersion, messageKeys, ciphertextMessage.getBody()); + + sessionState.clearUnacknowledgedPreKeyMessage(); + + return plaintext; + } + + public int getRemoteRegistrationId() { + synchronized (SESSION_LOCK) { + SessionRecord record = sessionStore.loadSession(recipientId, deviceId); + return record.getSessionState().getRemoteRegistrationId(); + } + } + + public int getSessionVersion() { + synchronized (SESSION_LOCK) { + if (!sessionStore.containsSession(recipientId, deviceId)) { + throw new IllegalStateException(String.format("No session for (%d, %d)!", recipientId, deviceId)); + } + + SessionRecord record = sessionStore.loadSession(recipientId, deviceId); + return record.getSessionState().getSessionVersion(); + } + } + + private ChainKey getOrCreateChainKey(SessionState sessionState, ECPublicKey theirEphemeral) + throws InvalidMessageException + { + try { + if (sessionState.hasReceiverChain(theirEphemeral)) { + return sessionState.getReceiverChainKey(theirEphemeral); + } else { + RootKey rootKey = sessionState.getRootKey(); + ECKeyPair ourEphemeral = sessionState.getSenderRatchetKeyPair(); + Pair receiverChain = rootKey.createChain(theirEphemeral, ourEphemeral); + ECKeyPair ourNewEphemeral = Curve.generateKeyPair(); + Pair senderChain = receiverChain.first().createChain(theirEphemeral, ourNewEphemeral); + + sessionState.setRootKey(senderChain.first()); + sessionState.addReceiverChain(theirEphemeral, receiverChain.second()); + sessionState.setPreviousCounter(Math.max(sessionState.getSenderChainKey().getIndex()-1, 0)); + sessionState.setSenderChain(ourNewEphemeral, senderChain.second()); + + return receiverChain.second(); + } + } catch (InvalidKeyException e) { + throw new InvalidMessageException(e); + } + } + + private MessageKeys getOrCreateMessageKeys(SessionState sessionState, + ECPublicKey theirEphemeral, + ChainKey chainKey, int counter) + throws InvalidMessageException, DuplicateMessageException + { + if (chainKey.getIndex() > counter) { + if (sessionState.hasMessageKeys(theirEphemeral, counter)) { + return sessionState.removeMessageKeys(theirEphemeral, counter); + } else { + throw new DuplicateMessageException("Received message with old counter: " + + chainKey.getIndex() + " , " + counter); + } + } + + if (counter - chainKey.getIndex() > 2000) { + throw new InvalidMessageException("Over 2000 messages into the future!"); + } + + while (chainKey.getIndex() < counter) { + MessageKeys messageKeys = chainKey.getMessageKeys(); + sessionState.setMessageKeys(theirEphemeral, messageKeys); + chainKey = chainKey.getNextChainKey(); + } + + sessionState.setReceiverChainKey(theirEphemeral, chainKey.getNextChainKey()); + return chainKey.getMessageKeys(); + } + + private byte[] getCiphertext(int version, MessageKeys messageKeys, byte[] plaintext) { + try { + Cipher cipher; + + if (version >= 3) { + cipher = getCipher(Cipher.ENCRYPT_MODE, messageKeys.getCipherKey(), messageKeys.getIv()); + } else { + cipher = getCipher(Cipher.ENCRYPT_MODE, messageKeys.getCipherKey(), messageKeys.getCounter()); + } + + return cipher.doFinal(plaintext); + } catch (IllegalBlockSizeException | BadPaddingException e) { + throw new AssertionError(e); + } + } + + private byte[] getPlaintext(int version, MessageKeys messageKeys, byte[] cipherText) + throws InvalidMessageException + { + try { + Cipher cipher; + + if (version >= 3) { + cipher = getCipher(Cipher.DECRYPT_MODE, messageKeys.getCipherKey(), messageKeys.getIv()); + } else { + cipher = getCipher(Cipher.DECRYPT_MODE, messageKeys.getCipherKey(), messageKeys.getCounter()); + } + + return cipher.doFinal(cipherText); + } catch (IllegalBlockSizeException | BadPaddingException e) { + throw new InvalidMessageException(e); + } + } + + private Cipher getCipher(int mode, SecretKeySpec key, int counter) { + try { + Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); + + byte[] ivBytes = new byte[16]; + ByteUtil.intToByteArray(ivBytes, 0, counter); + + IvParameterSpec iv = new IvParameterSpec(ivBytes); + cipher.init(mode, key, iv); + + return cipher; + } catch (NoSuchAlgorithmException | NoSuchPaddingException | java.security.InvalidKeyException | + InvalidAlgorithmParameterException e) + { + throw new AssertionError(e); + } + } + + private Cipher getCipher(int mode, SecretKeySpec key, IvParameterSpec iv) { + try { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(mode, key, iv); + return cipher; + } catch (NoSuchAlgorithmException | NoSuchPaddingException | java.security.InvalidKeyException | + InvalidAlgorithmParameterException e) + { + throw new AssertionError(e); + } + } + + public static interface DecryptionCallback { + public void handlePlaintext(byte[] plaintext); + } + + private static class NullDecryptionCallback implements DecryptionCallback { + @Override + public void handlePlaintext(byte[] plaintext) {} + } +} \ No newline at end of file diff --git a/src/main/java/org/whispersystems/libaxolotl/StaleKeyExchangeException.java b/src/main/java/org/whispersystems/libaxolotl/StaleKeyExchangeException.java new file mode 100644 index 000000000..f94a30087 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/StaleKeyExchangeException.java @@ -0,0 +1,4 @@ +package org.whispersystems.libaxolotl; + +public class StaleKeyExchangeException extends Throwable { +} diff --git a/src/main/java/org/whispersystems/libaxolotl/UntrustedIdentityException.java b/src/main/java/org/whispersystems/libaxolotl/UntrustedIdentityException.java new file mode 100644 index 000000000..f434b9230 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/UntrustedIdentityException.java @@ -0,0 +1,4 @@ +package org.whispersystems.libaxolotl; + +public class UntrustedIdentityException extends Exception { +} diff --git a/src/main/java/org/whispersystems/libaxolotl/ecc/Curve.java b/src/main/java/org/whispersystems/libaxolotl/ecc/Curve.java new file mode 100644 index 000000000..66b8cc3ab --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/ecc/Curve.java @@ -0,0 +1,78 @@ +/** + * Copyright (C) 2013 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.whispersystems.libaxolotl.ecc; + +import org.whispersystems.libaxolotl.InvalidKeyException; + +public class Curve { + + public static final int DJB_TYPE = 0x05; + + public static ECKeyPair generateKeyPair() { + return Curve25519.generateKeyPair(); + } + + public static ECPublicKey decodePoint(byte[] bytes, int offset) + throws InvalidKeyException + { + int type = bytes[offset]; + + if (type == DJB_TYPE) { + return Curve25519.decodePoint(bytes, offset); + } else { + throw new InvalidKeyException("Unknown key type: " + type); + } + } + + public static ECPrivateKey decodePrivatePoint(byte[] bytes) { + return new DjbECPrivateKey(bytes); + } + + public static byte[] calculateAgreement(ECPublicKey publicKey, ECPrivateKey privateKey) + throws InvalidKeyException + { + if (publicKey.getType() != privateKey.getType()) { + throw new InvalidKeyException("Public and private keys must be of the same type!"); + } + + if (publicKey.getType() == DJB_TYPE) { + return Curve25519.calculateAgreement(publicKey, privateKey); + } else { + throw new InvalidKeyException("Unknown type: " + publicKey.getType()); + } + } + + public static boolean verifySignature(ECPublicKey signingKey, byte[] message, byte[] signature) + throws InvalidKeyException + { + if (signingKey.getType() == DJB_TYPE) { + return Curve25519.verifySignature(signingKey, message, signature); + } else { + throw new InvalidKeyException("Unknown type: " + signingKey.getType()); + } + } + + public static byte[] calculateSignature(ECPrivateKey signingKey, byte[] message) + throws InvalidKeyException + { + if (signingKey.getType() == DJB_TYPE) { + return Curve25519.calculateSignature(signingKey, message); + } else { + throw new InvalidKeyException("Unknown type: " + signingKey.getType()); + } + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/ecc/Curve25519.java b/src/main/java/org/whispersystems/libaxolotl/ecc/Curve25519.java new file mode 100644 index 000000000..685a4a11c --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/ecc/Curve25519.java @@ -0,0 +1,98 @@ +/** + * Copyright (C) 2013 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.whispersystems.libaxolotl.ecc; + +import org.whispersystems.libaxolotl.InvalidKeyException; + +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +public class Curve25519 { + + static { + System.loadLibrary("curve25519"); + + try { + random = SecureRandom.getInstance("SHA1PRNG"); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + private static final SecureRandom random; + + private static native byte[] calculateAgreement(byte[] ourPrivate, byte[] theirPublic); + private static native byte[] generatePublicKey(byte[] privateKey); + private static native byte[] generatePrivateKey(byte[] random); + + private static native byte[] calculateSignature(byte[] random, byte[] privateKey, byte[] message); + private static native boolean verifySignature(byte[] publicKey, byte[] message, byte[] signature); + + public static ECKeyPair generateKeyPair() { + byte[] privateKey = generatePrivateKey(); + byte[] publicKey = generatePublicKey(privateKey); + + return new ECKeyPair(new DjbECPublicKey(publicKey), new DjbECPrivateKey(privateKey)); + } + + static byte[] calculateAgreement(ECPublicKey publicKey, ECPrivateKey privateKey) { + return calculateAgreement(((DjbECPrivateKey)privateKey).getPrivateKey(), + ((DjbECPublicKey)publicKey).getPublicKey()); + } + + static byte[] calculateSignature(ECPrivateKey privateKey, byte[] message) { + byte[] random = getRandom(64); + return calculateSignature(random, ((DjbECPrivateKey)privateKey).getPrivateKey(), message); + } + + static boolean verifySignature(ECPublicKey publicKey, byte[] message, byte[] signature) { + return verifySignature(((DjbECPublicKey)publicKey).getPublicKey(), message, signature); + } + + static ECPublicKey decodePoint(byte[] encoded, int offset) + throws InvalidKeyException + { + int type = encoded[offset] & 0xFF; + byte[] keyBytes = new byte[32]; + System.arraycopy(encoded, offset+1, keyBytes, 0, keyBytes.length); + + if (type != Curve.DJB_TYPE) { + throw new InvalidKeyException("Bad key type: " + type); + } + + return new DjbECPublicKey(keyBytes); + } + + private static byte[] generatePrivateKey() { + byte[] privateKey = new byte[32]; + random.nextBytes(privateKey); + + return generatePrivateKey(privateKey); + } + + private static byte[] getRandom(int size) { + try { + byte[] random = new byte[size]; + SecureRandom.getInstance("SHA1PRNG").nextBytes(random); + + return random; + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + +} diff --git a/src/main/java/org/whispersystems/libaxolotl/ecc/DjbECPrivateKey.java b/src/main/java/org/whispersystems/libaxolotl/ecc/DjbECPrivateKey.java new file mode 100644 index 000000000..ecb55b4ee --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/ecc/DjbECPrivateKey.java @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2013 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.whispersystems.libaxolotl.ecc; + +public class DjbECPrivateKey implements ECPrivateKey { + + private final byte[] privateKey; + + DjbECPrivateKey(byte[] privateKey) { + this.privateKey = privateKey; + } + + @Override + public byte[] serialize() { + return privateKey; + } + + @Override + public int getType() { + return Curve.DJB_TYPE; + } + + public byte[] getPrivateKey() { + return privateKey; + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/ecc/DjbECPublicKey.java b/src/main/java/org/whispersystems/libaxolotl/ecc/DjbECPublicKey.java new file mode 100644 index 000000000..9f66d4393 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/ecc/DjbECPublicKey.java @@ -0,0 +1,66 @@ +/** + * Copyright (C) 2013 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.whispersystems.libaxolotl.ecc; + +import org.whispersystems.libaxolotl.util.ByteUtil; + +import java.math.BigInteger; +import java.util.Arrays; + +public class DjbECPublicKey implements ECPublicKey { + + private final byte[] publicKey; + + DjbECPublicKey(byte[] publicKey) { + this.publicKey = publicKey; + } + + @Override + public byte[] serialize() { + byte[] type = {Curve.DJB_TYPE}; + return ByteUtil.combine(type, publicKey); + } + + @Override + public int getType() { + return Curve.DJB_TYPE; + } + + @Override + public boolean equals(Object other) { + if (other == null) return false; + if (!(other instanceof DjbECPublicKey)) return false; + + DjbECPublicKey that = (DjbECPublicKey)other; + return Arrays.equals(this.publicKey, that.publicKey); + } + + @Override + public int hashCode() { + return Arrays.hashCode(publicKey); + } + + @Override + public int compareTo(ECPublicKey another) { + return new BigInteger(publicKey).compareTo(new BigInteger(((DjbECPublicKey)another).publicKey)); + } + + public byte[] getPublicKey() { + return publicKey; + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/ecc/ECKeyPair.java b/src/main/java/org/whispersystems/libaxolotl/ecc/ECKeyPair.java new file mode 100644 index 000000000..bcaec927d --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/ecc/ECKeyPair.java @@ -0,0 +1,37 @@ +/** + * Copyright (C) 2013 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.whispersystems.libaxolotl.ecc; + +public class ECKeyPair { + + private final ECPublicKey publicKey; + private final ECPrivateKey privateKey; + + public ECKeyPair(ECPublicKey publicKey, ECPrivateKey privateKey) { + this.publicKey = publicKey; + this.privateKey = privateKey; + } + + public ECPublicKey getPublicKey() { + return publicKey; + } + + public ECPrivateKey getPrivateKey() { + return privateKey; + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/ecc/ECPrivateKey.java b/src/main/java/org/whispersystems/libaxolotl/ecc/ECPrivateKey.java new file mode 100644 index 000000000..922057de7 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/ecc/ECPrivateKey.java @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2013 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.whispersystems.libaxolotl.ecc; + +public interface ECPrivateKey { + public byte[] serialize(); + public int getType(); +} diff --git a/src/main/java/org/whispersystems/libaxolotl/ecc/ECPublicKey.java b/src/main/java/org/whispersystems/libaxolotl/ecc/ECPublicKey.java new file mode 100644 index 000000000..125768073 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/ecc/ECPublicKey.java @@ -0,0 +1,27 @@ +/** + * Copyright (C) 2013 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.whispersystems.libaxolotl.ecc; + +public interface ECPublicKey extends Comparable { + + public static final int KEY_SIZE = 33; + + public byte[] serialize(); + + public int getType(); +} diff --git a/src/main/java/org/whispersystems/libaxolotl/groups/GroupCipher.java b/src/main/java/org/whispersystems/libaxolotl/groups/GroupCipher.java new file mode 100644 index 000000000..43dac752e --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/groups/GroupCipher.java @@ -0,0 +1,146 @@ +package org.whispersystems.libaxolotl.groups; + +import org.whispersystems.libaxolotl.DuplicateMessageException; +import org.whispersystems.libaxolotl.InvalidKeyIdException; +import org.whispersystems.libaxolotl.InvalidMessageException; +import org.whispersystems.libaxolotl.LegacyMessageException; +import org.whispersystems.libaxolotl.NoSessionException; +import org.whispersystems.libaxolotl.groups.ratchet.SenderChainKey; +import org.whispersystems.libaxolotl.groups.ratchet.SenderMessageKey; +import org.whispersystems.libaxolotl.groups.state.SenderKeyRecord; +import org.whispersystems.libaxolotl.groups.state.SenderKeyState; +import org.whispersystems.libaxolotl.groups.state.SenderKeyStore; +import org.whispersystems.libaxolotl.protocol.SenderKeyMessage; + +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class GroupCipher { + + static final Object LOCK = new Object(); + + private final SenderKeyStore senderKeyStore; + private final String senderKeyId; + + public GroupCipher(SenderKeyStore senderKeyStore, String senderKeyId) { + this.senderKeyStore = senderKeyStore; + this.senderKeyId = senderKeyId; + } + + public byte[] encrypt(byte[] paddedPlaintext) throws NoSessionException { + synchronized (LOCK) { + try { + SenderKeyRecord record = senderKeyStore.loadSenderKey(senderKeyId); + SenderKeyState senderKeyState = record.getSenderKeyState(); + SenderMessageKey senderKey = senderKeyState.getSenderChainKey().getSenderMessageKey(); + byte[] ciphertext = getCipherText(senderKey.getIv(), senderKey.getCipherKey(), paddedPlaintext); + + SenderKeyMessage senderKeyMessage = new SenderKeyMessage(senderKeyState.getKeyId(), + senderKey.getIteration(), + ciphertext, + senderKeyState.getSigningKeyPrivate()); + + senderKeyState.setSenderChainKey(senderKeyState.getSenderChainKey().getNext()); + + senderKeyStore.storeSenderKey(senderKeyId, record); + + return senderKeyMessage.serialize(); + } catch (InvalidKeyIdException e) { + throw new NoSessionException(e); + } + } + } + + public byte[] decrypt(byte[] senderKeyMessageBytes) + throws LegacyMessageException, InvalidMessageException, DuplicateMessageException + { + synchronized (LOCK) { + try { + SenderKeyRecord record = senderKeyStore.loadSenderKey(senderKeyId); + SenderKeyMessage senderKeyMessage = new SenderKeyMessage(senderKeyMessageBytes); + SenderKeyState senderKeyState = record.getSenderKeyState(senderKeyMessage.getKeyId()); + + senderKeyMessage.verifySignature(senderKeyState.getSigningKeyPublic()); + + SenderMessageKey senderKey = getSenderKey(senderKeyState, senderKeyMessage.getIteration()); + + byte[] plaintext = getPlainText(senderKey.getIv(), senderKey.getCipherKey(), senderKeyMessage.getCipherText()); + + senderKeyStore.storeSenderKey(senderKeyId, record); + + return plaintext; + } catch (org.whispersystems.libaxolotl.InvalidKeyException | InvalidKeyIdException e) { + throw new InvalidMessageException(e); + } + } + } + + private SenderMessageKey getSenderKey(SenderKeyState senderKeyState, int iteration) + throws DuplicateMessageException, InvalidMessageException + { + SenderChainKey senderChainKey = senderKeyState.getSenderChainKey(); + + if (senderChainKey.getIteration() > iteration) { + if (senderKeyState.hasSenderMessageKey(iteration)) { + return senderKeyState.removeSenderMessageKey(iteration); + } else { + throw new DuplicateMessageException("Received message with old counter: " + + senderChainKey.getIteration() + " , " + iteration); + } + } + + if (senderChainKey.getIteration() - iteration > 2000) { + throw new InvalidMessageException("Over 2000 messages into the future!"); + } + + while (senderChainKey.getIteration() < iteration) { + senderKeyState.addSenderMessageKey(senderChainKey.getSenderMessageKey()); + senderChainKey = senderChainKey.getNext(); + } + + senderKeyState.setSenderChainKey(senderChainKey.getNext()); + return senderChainKey.getSenderMessageKey(); + } + + private byte[] getPlainText(byte[] iv, byte[] key, byte[] ciphertext) + throws InvalidMessageException + { + try { + IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + + cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), ivParameterSpec); + + return cipher.doFinal(ciphertext); + } catch (NoSuchAlgorithmException | NoSuchPaddingException | java.security.InvalidKeyException | + InvalidAlgorithmParameterException e) + { + throw new AssertionError(e); + } catch (IllegalBlockSizeException | BadPaddingException e) { + throw new InvalidMessageException(e); + } + } + + private byte[] getCipherText(byte[] iv, byte[] key, byte[] plaintext) { + try { + IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), ivParameterSpec); + + return cipher.doFinal(plaintext); + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException | + IllegalBlockSizeException | BadPaddingException | java.security.InvalidKeyException e) + { + throw new AssertionError(e); + } + } + +} diff --git a/src/main/java/org/whispersystems/libaxolotl/groups/GroupSessionBuilder.java b/src/main/java/org/whispersystems/libaxolotl/groups/GroupSessionBuilder.java new file mode 100644 index 000000000..8b73484b2 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/groups/GroupSessionBuilder.java @@ -0,0 +1,38 @@ +package org.whispersystems.libaxolotl.groups; + +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.groups.state.SenderKeyRecord; +import org.whispersystems.libaxolotl.groups.state.SenderKeyStore; +import org.whispersystems.libaxolotl.protocol.SenderKeyDistributionMessage; + +public class GroupSessionBuilder { + + private final SenderKeyStore senderKeyStore; + + public GroupSessionBuilder(SenderKeyStore senderKeyStore) { + this.senderKeyStore = senderKeyStore; + } + + public void process(String sender, SenderKeyDistributionMessage senderKeyDistributionMessage) { + synchronized (GroupCipher.LOCK) { + SenderKeyRecord senderKeyRecord = senderKeyStore.loadSenderKey(sender); + senderKeyRecord.addSenderKeyState(senderKeyDistributionMessage.getId(), + senderKeyDistributionMessage.getIteration(), + senderKeyDistributionMessage.getChainKey(), + senderKeyDistributionMessage.getSignatureKey()); + senderKeyStore.storeSenderKey(sender, senderKeyRecord); + } + } + + public SenderKeyDistributionMessage process(String groupId, int keyId, int iteration, + byte[] chainKey, ECKeyPair signatureKey) + { + synchronized (GroupCipher.LOCK) { + SenderKeyRecord senderKeyRecord = senderKeyStore.loadSenderKey(groupId); + senderKeyRecord.setSenderKeyState(keyId, iteration, chainKey, signatureKey); + senderKeyStore.storeSenderKey(groupId, senderKeyRecord); + + return new SenderKeyDistributionMessage(keyId, iteration, chainKey, signatureKey.getPublicKey()); + } + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/groups/ratchet/SenderChainKey.java b/src/main/java/org/whispersystems/libaxolotl/groups/ratchet/SenderChainKey.java new file mode 100644 index 000000000..71375923b --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/groups/ratchet/SenderChainKey.java @@ -0,0 +1,49 @@ +package org.whispersystems.libaxolotl.groups.ratchet; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +public class SenderChainKey { + + private static final byte[] MESSAGE_KEY_SEED = {0x01}; + private static final byte[] CHAIN_KEY_SEED = {0x02}; + + private final int iteration; + private final byte[] chainKey; + + public SenderChainKey(int iteration, byte[] chainKey) { + this.iteration = iteration; + this.chainKey = chainKey; + } + + public int getIteration() { + return iteration; + } + + public SenderMessageKey getSenderMessageKey() { + return new SenderMessageKey(iteration, getDerivative(MESSAGE_KEY_SEED, chainKey)); + } + + public SenderChainKey getNext() { + return new SenderChainKey(iteration + 1, getDerivative(CHAIN_KEY_SEED, chainKey)); + } + + public byte[] getSeed() { + return chainKey; + } + + private byte[] getDerivative(byte[] seed, byte[] key) { + try { + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(key, "HmacSHA256")); + + return mac.doFinal(seed); + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + throw new AssertionError(e); + } + } + +} diff --git a/src/main/java/org/whispersystems/libaxolotl/groups/ratchet/SenderMessageKey.java b/src/main/java/org/whispersystems/libaxolotl/groups/ratchet/SenderMessageKey.java new file mode 100644 index 000000000..8808a8e8f --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/groups/ratchet/SenderMessageKey.java @@ -0,0 +1,38 @@ +package org.whispersystems.libaxolotl.groups.ratchet; + +import org.whispersystems.libaxolotl.kdf.HKDFv3; +import org.whispersystems.libaxolotl.util.ByteUtil; + +public class SenderMessageKey { + + private final int iteration; + private final byte[] iv; + private final byte[] cipherKey; + private final byte[] seed; + + public SenderMessageKey(int iteration, byte[] seed) { + byte[] derivative = new HKDFv3().deriveSecrets(seed, "WhisperGroup".getBytes(), 48); + byte[][] parts = ByteUtil.split(derivative, 16, 32); + + this.iteration = iteration; + this.seed = seed; + this.iv = parts[0]; + this.cipherKey = parts[1]; + } + + public int getIteration() { + return iteration; + } + + public byte[] getIv() { + return iv; + } + + public byte[] getCipherKey() { + return cipherKey; + } + + public byte[] getSeed() { + return seed; + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyRecord.java b/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyRecord.java new file mode 100644 index 000000000..bb1ba952a --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyRecord.java @@ -0,0 +1,64 @@ +package org.whispersystems.libaxolotl.groups.state; + +import org.whispersystems.libaxolotl.InvalidKeyIdException; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.state.StorageProtos; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +import static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure; + +public class SenderKeyRecord { + + private List senderKeyStates = new LinkedList<>(); + + public SenderKeyRecord() {} + + public SenderKeyRecord(byte[] serialized) throws IOException { + SenderKeyRecordStructure senderKeyRecordStructure = SenderKeyRecordStructure.parseFrom(serialized); + + for (StorageProtos.SenderKeyStateStructure structure : senderKeyRecordStructure.getSenderKeyStatesList()) { + this.senderKeyStates.add(new SenderKeyState(structure)); + } + } + + public SenderKeyState getSenderKeyState() throws InvalidKeyIdException { + if (!senderKeyStates.isEmpty()) { + return senderKeyStates.get(0); + } else { + throw new InvalidKeyIdException("No key state in record!"); + } + } + + public SenderKeyState getSenderKeyState(int keyId) throws InvalidKeyIdException { + for (SenderKeyState state : senderKeyStates) { + if (state.getKeyId() == keyId) { + return state; + } + } + + throw new InvalidKeyIdException("No keys for: " + keyId); + } + + public void addSenderKeyState(int id, int iteration, byte[] chainKey, ECPublicKey signatureKey) { + senderKeyStates.add(new SenderKeyState(id, iteration, chainKey, signatureKey)); + } + + public void setSenderKeyState(int id, int iteration, byte[] chainKey, ECKeyPair signatureKey) { + senderKeyStates.clear(); + senderKeyStates.add(new SenderKeyState(id, iteration, chainKey, signatureKey)); + } + + public byte[] serialize() { + SenderKeyRecordStructure.Builder recordStructure = SenderKeyRecordStructure.newBuilder(); + + for (SenderKeyState senderKeyState : senderKeyStates) { + recordStructure.addSenderKeyStates(senderKeyState.getStructure()); + } + + return recordStructure.build().toByteArray(); + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyState.java b/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyState.java new file mode 100644 index 000000000..80498ce0b --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyState.java @@ -0,0 +1,144 @@ +package org.whispersystems.libaxolotl.groups.state; + +import com.google.protobuf.ByteString; + +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.ecc.ECPrivateKey; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.groups.ratchet.SenderChainKey; +import org.whispersystems.libaxolotl.groups.ratchet.SenderMessageKey; +import org.whispersystems.libaxolotl.util.guava.Optional; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure; + +public class SenderKeyState { + + private SenderKeyStateStructure senderKeyStateStructure; + + public SenderKeyState(int id, int iteration, byte[] chainKey, ECPublicKey signatureKey) { + this(id, iteration, chainKey, signatureKey, Optional.absent()); + } + + public SenderKeyState(int id, int iteration, byte[] chainKey, ECKeyPair signatureKey) { + this(id, iteration, chainKey, signatureKey.getPublicKey(), Optional.of(signatureKey.getPrivateKey())); + } + + private SenderKeyState(int id, int iteration, byte[] chainKey, + ECPublicKey signatureKeyPublic, + Optional signatureKeyPrivate) + { + SenderKeyStateStructure.SenderChainKey senderChainKeyStructure = + SenderKeyStateStructure.SenderChainKey.newBuilder() + .setIteration(iteration) + .setSeed(ByteString.copyFrom(chainKey)) + .build(); + + SenderKeyStateStructure.SenderSigningKey.Builder signingKeyStructure = + SenderKeyStateStructure.SenderSigningKey.newBuilder() + .setPublic(ByteString.copyFrom(signatureKeyPublic.serialize())); + + if (signatureKeyPrivate.isPresent()) { + signingKeyStructure.setPrivate(ByteString.copyFrom(signatureKeyPrivate.get().serialize())); + } + + this.senderKeyStateStructure = SenderKeyStateStructure.newBuilder() + .setSenderKeyId(id) + .setSenderChainKey(senderChainKeyStructure) + .setSenderSigningKey(signingKeyStructure) + .build(); + } + + public SenderKeyState(SenderKeyStateStructure senderKeyStateStructure) { + this.senderKeyStateStructure = senderKeyStateStructure; + } + + public int getKeyId() { + return senderKeyStateStructure.getSenderKeyId(); + } + + public SenderChainKey getSenderChainKey() { + return new SenderChainKey(senderKeyStateStructure.getSenderChainKey().getIteration(), + senderKeyStateStructure.getSenderChainKey().getSeed().toByteArray()); + } + + public void setSenderChainKey(SenderChainKey chainKey) { + SenderKeyStateStructure.SenderChainKey senderChainKeyStructure = + SenderKeyStateStructure.SenderChainKey.newBuilder() + .setIteration(chainKey.getIteration()) + .setSeed(ByteString.copyFrom(chainKey.getSeed())) + .build(); + + this.senderKeyStateStructure = senderKeyStateStructure.toBuilder() + .setSenderChainKey(senderChainKeyStructure) + .build(); + } + + public ECPublicKey getSigningKeyPublic() throws InvalidKeyException { + return Curve.decodePoint(senderKeyStateStructure.getSenderSigningKey() + .getPublic() + .toByteArray(), 0); + } + + public ECPrivateKey getSigningKeyPrivate() { + return Curve.decodePrivatePoint(senderKeyStateStructure.getSenderSigningKey() + .getPrivate().toByteArray()); + } + + public boolean hasSenderMessageKey(int iteration) { + for (SenderKeyStateStructure.SenderMessageKey senderMessageKey : senderKeyStateStructure.getSenderMessageKeysList()) { + if (senderMessageKey.getIteration() == iteration) return true; + } + + return false; + } + + public void addSenderMessageKey(SenderMessageKey senderMessageKey) { + SenderKeyStateStructure.SenderMessageKey senderMessageKeyStructure = + SenderKeyStateStructure.SenderMessageKey.newBuilder() + .setIteration(senderMessageKey.getIteration()) + .setSeed(ByteString.copyFrom(senderMessageKey.getSeed())) + .build(); + + this.senderKeyStateStructure = this.senderKeyStateStructure.toBuilder() + .addSenderMessageKeys(senderMessageKeyStructure) + .build(); + } + + public SenderMessageKey removeSenderMessageKey(int iteration) { + List keys = new LinkedList<>(senderKeyStateStructure.getSenderMessageKeysList()); + Iterator iterator = keys.iterator(); + + SenderKeyStateStructure.SenderMessageKey result = null; + + while (iterator.hasNext()) { + SenderKeyStateStructure.SenderMessageKey senderMessageKey = iterator.next(); + + if (senderMessageKey.getIteration() == iteration) { + result = senderMessageKey; + iterator.remove(); + break; + } + } + + this.senderKeyStateStructure = this.senderKeyStateStructure.toBuilder() + .clearSenderMessageKeys() + .addAllSenderMessageKeys(keys) + .build(); + + if (result != null) { + return new SenderMessageKey(result.getIteration(), result.getSeed().toByteArray()); + } else { + return null; + } + } + + public SenderKeyStateStructure getStructure() { + return senderKeyStateStructure; + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyStore.java b/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyStore.java new file mode 100644 index 000000000..da01b1f3d --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyStore.java @@ -0,0 +1,6 @@ +package org.whispersystems.libaxolotl.groups.state; + +public interface SenderKeyStore { + public void storeSenderKey(String senderKeyId, SenderKeyRecord record); + public SenderKeyRecord loadSenderKey(String senderKeyId); +} diff --git a/src/main/java/org/whispersystems/libaxolotl/kdf/DerivedMessageSecrets.java b/src/main/java/org/whispersystems/libaxolotl/kdf/DerivedMessageSecrets.java new file mode 100644 index 000000000..b3f54fb80 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/kdf/DerivedMessageSecrets.java @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2014 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.whispersystems.libaxolotl.kdf; + +import org.whispersystems.libaxolotl.util.ByteUtil; + +import java.text.ParseException; + +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class DerivedMessageSecrets { + + public static final int SIZE = 80; + private static final int CIPHER_KEY_LENGTH = 32; + private static final int MAC_KEY_LENGTH = 32; + private static final int IV_LENGTH = 16; + + private final SecretKeySpec cipherKey; + private final SecretKeySpec macKey; + private final IvParameterSpec iv; + + public DerivedMessageSecrets(byte[] okm) { + try { + byte[][] keys = ByteUtil.split(okm, CIPHER_KEY_LENGTH, MAC_KEY_LENGTH, IV_LENGTH); + + this.cipherKey = new SecretKeySpec(keys[0], "AES"); + this.macKey = new SecretKeySpec(keys[1], "HmacSHA256"); + this.iv = new IvParameterSpec(keys[2]); + } catch (ParseException e) { + throw new AssertionError(e); + } + } + + public SecretKeySpec getCipherKey() { + return cipherKey; + } + + public SecretKeySpec getMacKey() { + return macKey; + } + + public IvParameterSpec getIv() { + return iv; + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/kdf/DerivedRootSecrets.java b/src/main/java/org/whispersystems/libaxolotl/kdf/DerivedRootSecrets.java new file mode 100644 index 000000000..82cc541d8 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/kdf/DerivedRootSecrets.java @@ -0,0 +1,26 @@ +package org.whispersystems.libaxolotl.kdf; + +import org.whispersystems.libaxolotl.util.ByteUtil; + +public class DerivedRootSecrets { + + public static final int SIZE = 64; + + private final byte[] rootKey; + private final byte[] chainKey; + + public DerivedRootSecrets(byte[] okm) { + byte[][] keys = ByteUtil.split(okm, 32, 32); + this.rootKey = keys[0]; + this.chainKey = keys[1]; + } + + public byte[] getRootKey() { + return rootKey; + } + + public byte[] getChainKey() { + return chainKey; + } + +} diff --git a/src/main/java/org/whispersystems/libaxolotl/kdf/HKDF.java b/src/main/java/org/whispersystems/libaxolotl/kdf/HKDF.java new file mode 100644 index 000000000..d190822df --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/kdf/HKDF.java @@ -0,0 +1,93 @@ +/** + * Copyright (C) 2013 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.whispersystems.libaxolotl.kdf; + +import java.io.ByteArrayOutputStream; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +public abstract class HKDF { + + private static final int HASH_OUTPUT_SIZE = 32; + + public static HKDF createFor(int messageVersion) { + switch (messageVersion) { + case 2: return new HKDFv2(); + case 3: return new HKDFv3(); + default: throw new AssertionError("Unknown version: " + messageVersion); + } + } + + public byte[] deriveSecrets(byte[] inputKeyMaterial, byte[] info, int outputLength) { + byte[] salt = new byte[HASH_OUTPUT_SIZE]; + return deriveSecrets(inputKeyMaterial, salt, info, outputLength); + } + + public byte[] deriveSecrets(byte[] inputKeyMaterial, byte[] salt, byte[] info, int outputLength) { + byte[] prk = extract(salt, inputKeyMaterial); + return expand(prk, info, outputLength); + } + + private byte[] extract(byte[] salt, byte[] inputKeyMaterial) { + try { + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(salt, "HmacSHA256")); + return mac.doFinal(inputKeyMaterial); + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + throw new AssertionError(e); + } + } + + private byte[] expand(byte[] prk, byte[] info, int outputSize) { + try { + int iterations = (int) Math.ceil((double) outputSize / (double) HASH_OUTPUT_SIZE); + byte[] mixin = new byte[0]; + ByteArrayOutputStream results = new ByteArrayOutputStream(); + int remainingBytes = outputSize; + + for (int i= getIterationStartOffset();i. + */ +package org.whispersystems.libaxolotl.protocol; + +public interface CiphertextMessage { + + public static final int UNSUPPORTED_VERSION = 1; + public static final int CURRENT_VERSION = 3; + + public static final int WHISPER_TYPE = 2; + public static final int PREKEY_TYPE = 3; + public static final int SENDERKEY_TYPE = 4; + public static final int SENDERKEY_DISTRIBUTION_TYPE = 5; + + // This should be the worst case (worse than V2). So not always accurate, but good enough for padding. + public static final int ENCRYPTED_MESSAGE_OVERHEAD = 53; + + public byte[] serialize(); + public int getType(); + +} \ No newline at end of file diff --git a/src/main/java/org/whispersystems/libaxolotl/protocol/KeyExchangeMessage.java b/src/main/java/org/whispersystems/libaxolotl/protocol/KeyExchangeMessage.java new file mode 100644 index 000000000..bec9208c8 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/protocol/KeyExchangeMessage.java @@ -0,0 +1,153 @@ +package org.whispersystems.libaxolotl.protocol; + + +import com.google.protobuf.ByteString; + +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.InvalidMessageException; +import org.whispersystems.libaxolotl.InvalidVersionException; +import org.whispersystems.libaxolotl.LegacyMessageException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.util.ByteUtil; + +import java.io.IOException; + +import static org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage.Builder; + +public class KeyExchangeMessage { + + public static final int INITIATE_FLAG = 0x01; + public static final int RESPONSE_FLAG = 0X02; + public static final int SIMULTAENOUS_INITIATE_FLAG = 0x04; + + private final int version; + private final int supportedVersion; + private final int sequence; + private final int flags; + + private final ECPublicKey baseKey; + private final byte[] baseKeySignature; + private final ECPublicKey ratchetKey; + private final IdentityKey identityKey; + private final byte[] serialized; + + public KeyExchangeMessage(int messageVersion, int sequence, int flags, + ECPublicKey baseKey, byte[] baseKeySignature, + ECPublicKey ratchetKey, + IdentityKey identityKey) + { + this.supportedVersion = CiphertextMessage.CURRENT_VERSION; + this.version = messageVersion; + this.sequence = sequence; + this.flags = flags; + this.baseKey = baseKey; + this.baseKeySignature = baseKeySignature; + this.ratchetKey = ratchetKey; + this.identityKey = identityKey; + + byte[] version = {ByteUtil.intsToByteHighAndLow(this.version, this.supportedVersion)}; + Builder builder = WhisperProtos.KeyExchangeMessage + .newBuilder() + .setId((sequence << 5) | flags) + .setBaseKey(ByteString.copyFrom(baseKey.serialize())) + .setRatchetKey(ByteString.copyFrom(ratchetKey.serialize())) + .setIdentityKey(ByteString.copyFrom(identityKey.serialize())); + + if (messageVersion >= 3) { + builder.setBaseKeySignature(ByteString.copyFrom(baseKeySignature)); + } + + this.serialized = ByteUtil.combine(version, builder.build().toByteArray()); + } + + public KeyExchangeMessage(byte[] serialized) + throws InvalidMessageException, InvalidVersionException, LegacyMessageException + { + try { + byte[][] parts = ByteUtil.split(serialized, 1, serialized.length - 1); + this.version = ByteUtil.highBitsToInt(parts[0][0]); + this.supportedVersion = ByteUtil.lowBitsToInt(parts[0][0]); + + if (this.version <= CiphertextMessage.UNSUPPORTED_VERSION) { + throw new LegacyMessageException("Unsupported legacy version: " + this.version); + } + + if (this.version > CiphertextMessage.CURRENT_VERSION) { + throw new InvalidVersionException("Unknown version: " + this.version); + } + + WhisperProtos.KeyExchangeMessage message = WhisperProtos.KeyExchangeMessage.parseFrom(parts[1]); + + if (!message.hasId() || !message.hasBaseKey() || + !message.hasRatchetKey() || !message.hasIdentityKey() || + (this.version >=3 && !message.hasBaseKeySignature())) + { + throw new InvalidMessageException("Some required fields missing!"); + } + + this.sequence = message.getId() >> 5; + this.flags = message.getId() & 0x1f; + this.serialized = serialized; + this.baseKey = Curve.decodePoint(message.getBaseKey().toByteArray(), 0); + this.baseKeySignature = message.getBaseKeySignature().toByteArray(); + this.ratchetKey = Curve.decodePoint(message.getRatchetKey().toByteArray(), 0); + this.identityKey = new IdentityKey(message.getIdentityKey().toByteArray(), 0); + } catch (InvalidKeyException | IOException e) { + throw new InvalidMessageException(e); + } + } + + public int getVersion() { + return version; + } + + public ECPublicKey getBaseKey() { + return baseKey; + } + + public byte[] getBaseKeySignature() { + return baseKeySignature; + } + + public ECPublicKey getRatchetKey() { + return ratchetKey; + } + + public IdentityKey getIdentityKey() { + return identityKey; + } + + public boolean hasIdentityKey() { + return true; + } + + public int getMaxVersion() { + return supportedVersion; + } + + public boolean isResponse() { + return ((flags & RESPONSE_FLAG) != 0); + } + + public boolean isInitiate() { + return (flags & INITIATE_FLAG) != 0; + } + + public boolean isResponseForSimultaneousInitiate() { + return (flags & SIMULTAENOUS_INITIATE_FLAG) != 0; + } + + public int getFlags() { + return flags; + } + + public int getSequence() { + return sequence; + } + + public byte[] serialize() { + return serialized; + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/protocol/PreKeyWhisperMessage.java b/src/main/java/org/whispersystems/libaxolotl/protocol/PreKeyWhisperMessage.java new file mode 100644 index 000000000..fff6d02a8 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/protocol/PreKeyWhisperMessage.java @@ -0,0 +1,147 @@ +/** + * Copyright (C) 2014 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.whispersystems.libaxolotl.protocol; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.InvalidMessageException; +import org.whispersystems.libaxolotl.InvalidVersionException; +import org.whispersystems.libaxolotl.LegacyMessageException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.util.ByteUtil; +import org.whispersystems.libaxolotl.util.guava.Optional; + + +public class PreKeyWhisperMessage implements CiphertextMessage { + + private final int version; + private final int registrationId; + private final Optional preKeyId; + private final int signedPreKeyId; + private final ECPublicKey baseKey; + private final IdentityKey identityKey; + private final WhisperMessage message; + private final byte[] serialized; + + public PreKeyWhisperMessage(byte[] serialized) + throws InvalidMessageException, InvalidVersionException + { + try { + this.version = ByteUtil.highBitsToInt(serialized[0]); + + if (this.version > CiphertextMessage.CURRENT_VERSION) { + throw new InvalidVersionException("Unknown version: " + this.version); + } + + WhisperProtos.PreKeyWhisperMessage preKeyWhisperMessage + = WhisperProtos.PreKeyWhisperMessage.parseFrom(ByteString.copyFrom(serialized, 1, + serialized.length-1)); + + if ((version == 2 && !preKeyWhisperMessage.hasPreKeyId()) || + (version == 3 && !preKeyWhisperMessage.hasSignedPreKeyId()) || + !preKeyWhisperMessage.hasBaseKey() || + !preKeyWhisperMessage.hasIdentityKey() || + !preKeyWhisperMessage.hasMessage()) + { + throw new InvalidMessageException("Incomplete message."); + } + + this.serialized = serialized; + this.registrationId = preKeyWhisperMessage.getRegistrationId(); + this.preKeyId = preKeyWhisperMessage.hasPreKeyId() ? Optional.of(preKeyWhisperMessage.getPreKeyId()) : Optional.absent(); + this.signedPreKeyId = preKeyWhisperMessage.hasSignedPreKeyId() ? preKeyWhisperMessage.getSignedPreKeyId() : -1; + this.baseKey = Curve.decodePoint(preKeyWhisperMessage.getBaseKey().toByteArray(), 0); + this.identityKey = new IdentityKey(Curve.decodePoint(preKeyWhisperMessage.getIdentityKey().toByteArray(), 0)); + this.message = new WhisperMessage(preKeyWhisperMessage.getMessage().toByteArray()); + } catch (InvalidProtocolBufferException | InvalidKeyException | LegacyMessageException e) { + throw new InvalidMessageException(e); + } + } + + public PreKeyWhisperMessage(int messageVersion, int registrationId, Optional preKeyId, + int signedPreKeyId, ECPublicKey baseKey, IdentityKey identityKey, + WhisperMessage message) + { + this.version = messageVersion; + this.registrationId = registrationId; + this.preKeyId = preKeyId; + this.signedPreKeyId = signedPreKeyId; + this.baseKey = baseKey; + this.identityKey = identityKey; + this.message = message; + + WhisperProtos.PreKeyWhisperMessage.Builder builder = + WhisperProtos.PreKeyWhisperMessage.newBuilder() + .setSignedPreKeyId(signedPreKeyId) + .setBaseKey(ByteString.copyFrom(baseKey.serialize())) + .setIdentityKey(ByteString.copyFrom(identityKey.serialize())) + .setMessage(ByteString.copyFrom(message.serialize())) + .setRegistrationId(registrationId); + + if (preKeyId.isPresent()) { + builder.setPreKeyId(preKeyId.get()); + } + + byte[] versionBytes = {ByteUtil.intsToByteHighAndLow(this.version, CURRENT_VERSION)}; + byte[] messageBytes = builder.build().toByteArray(); + + this.serialized = ByteUtil.combine(versionBytes, messageBytes); + } + + public int getMessageVersion() { + return version; + } + + public IdentityKey getIdentityKey() { + return identityKey; + } + + public int getRegistrationId() { + return registrationId; + } + + public Optional getPreKeyId() { + return preKeyId; + } + + public int getSignedPreKeyId() { + return signedPreKeyId; + } + + public ECPublicKey getBaseKey() { + return baseKey; + } + + public WhisperMessage getWhisperMessage() { + return message; + } + + @Override + public byte[] serialize() { + return serialized; + } + + @Override + public int getType() { + return CiphertextMessage.PREKEY_TYPE; + } + +} diff --git a/src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyDistributionMessage.java b/src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyDistributionMessage.java new file mode 100644 index 000000000..424dd87cd --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyDistributionMessage.java @@ -0,0 +1,56 @@ +package org.whispersystems.libaxolotl.protocol; + +import com.google.protobuf.ByteString; + +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.util.ByteUtil; + +public class SenderKeyDistributionMessage implements CiphertextMessage { + + private final int id; + private final int iteration; + private final byte[] chainKey; + private final ECPublicKey signatureKey; + private final byte[] serialized; + + public SenderKeyDistributionMessage(int id, int iteration, byte[] chainKey, ECPublicKey signatureKey) { + byte[] version = {ByteUtil.intsToByteHighAndLow(CURRENT_VERSION, CURRENT_VERSION)}; + + this.id = id; + this.iteration = iteration; + this.chainKey = chainKey; + this.signatureKey = signatureKey; + this.serialized = WhisperProtos.SenderKeyDistributionMessage.newBuilder() + .setId(id) + .setIteration(iteration) + .setChainKey(ByteString.copyFrom(chainKey)) + .setSigningKey(ByteString.copyFrom(signatureKey.serialize())) + .build().toByteArray(); + } + + @Override + public byte[] serialize() { + return serialized; + } + + @Override + public int getType() { + return SENDERKEY_DISTRIBUTION_TYPE; + } + + public int getIteration() { + return iteration; + } + + public byte[] getChainKey() { + return chainKey; + } + + public ECPublicKey getSignatureKey() { + return signatureKey; + } + + public int getId() { + return id; + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyMessage.java b/src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyMessage.java new file mode 100644 index 000000000..b3a174567 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyMessage.java @@ -0,0 +1,121 @@ +package org.whispersystems.libaxolotl.protocol; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.InvalidMessageException; +import org.whispersystems.libaxolotl.LegacyMessageException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECPrivateKey; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.util.ByteUtil; + +import java.text.ParseException; + +public class SenderKeyMessage implements CiphertextMessage { + + private static final int SIGNATURE_LENGTH = 64; + + private final int messageVersion; + private final int keyId; + private final int iteration; + private final byte[] ciphertext; + private final byte[] serialized; + + public SenderKeyMessage(byte[] serialized) throws InvalidMessageException, LegacyMessageException { + try { + byte[][] messageParts = ByteUtil.split(serialized, 1, serialized.length - 1 - SIGNATURE_LENGTH, SIGNATURE_LENGTH); + byte version = messageParts[0][0]; + byte[] message = messageParts[1]; + byte[] signature = messageParts[2]; + + if (ByteUtil.highBitsToInt(version) < 3) { + throw new LegacyMessageException("Legacy message: " + ByteUtil.highBitsToInt(version)); + } + + if (ByteUtil.highBitsToInt(version) > CURRENT_VERSION) { + throw new InvalidMessageException("Unknown version: " + ByteUtil.highBitsToInt(version)); + } + + WhisperProtos.SenderKeyMessage senderKeyMessage = WhisperProtos.SenderKeyMessage.parseFrom(message); + + if (!senderKeyMessage.hasId() || + !senderKeyMessage.hasIteration() || + !senderKeyMessage.hasCiphertext()) + { + throw new InvalidMessageException("Incomplete message."); + } + + this.serialized = serialized; + this.messageVersion = ByteUtil.highBitsToInt(version); + this.keyId = senderKeyMessage.getId(); + this.iteration = senderKeyMessage.getIteration(); + this.ciphertext = senderKeyMessage.getCiphertext().toByteArray(); + } catch (InvalidProtocolBufferException | ParseException e) { + throw new InvalidMessageException(e); + } + } + + public SenderKeyMessage(int keyId, int iteration, byte[] ciphertext, ECPrivateKey signatureKey) { + byte[] version = {ByteUtil.intsToByteHighAndLow(CURRENT_VERSION, CURRENT_VERSION)}; + byte[] message = WhisperProtos.SenderKeyMessage.newBuilder() + .setId(keyId) + .setIteration(iteration) + .setCiphertext(ByteString.copyFrom(ciphertext)) + .build().toByteArray(); + + byte[] signature = getSignature(signatureKey, ByteUtil.combine(version, message)); + + this.serialized = ByteUtil.combine(version, message, signature); + this.messageVersion = CURRENT_VERSION; + this.keyId = keyId; + this.iteration = iteration; + this.ciphertext = ciphertext; + } + + public int getKeyId() { + return keyId; + } + + public int getIteration() { + return iteration; + } + + public byte[] getCipherText() { + return ciphertext; + } + + public void verifySignature(ECPublicKey signatureKey) + throws InvalidMessageException + { + try { + byte[][] parts = ByteUtil.split(serialized, serialized.length - SIGNATURE_LENGTH, SIGNATURE_LENGTH); + + if (!Curve.verifySignature(signatureKey, parts[0], parts[1])) { + throw new InvalidMessageException("Invalid signature!"); + } + + } catch (InvalidKeyException e) { + throw new InvalidMessageException(e); + } + } + + private byte[] getSignature(ECPrivateKey signatureKey, byte[] serialized) { + try { + return Curve.calculateSignature(signatureKey, serialized); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + @Override + public byte[] serialize() { + return serialized; + } + + @Override + public int getType() { + return CiphertextMessage.SENDERKEY_TYPE; + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/protocol/WhisperMessage.java b/src/main/java/org/whispersystems/libaxolotl/protocol/WhisperMessage.java new file mode 100644 index 000000000..980bec1fb --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/protocol/WhisperMessage.java @@ -0,0 +1,172 @@ +/** + * Copyright (C) 2014 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.whispersystems.libaxolotl.protocol; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.InvalidMessageException; +import org.whispersystems.libaxolotl.LegacyMessageException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.util.ByteUtil; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.ParseException; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +public class WhisperMessage implements CiphertextMessage { + + private static final int MAC_LENGTH = 8; + + private final int messageVersion; + private final ECPublicKey senderRatchetKey; + private final int counter; + private final int previousCounter; + private final byte[] ciphertext; + private final byte[] serialized; + + public WhisperMessage(byte[] serialized) throws InvalidMessageException, LegacyMessageException { + try { + byte[][] messageParts = ByteUtil.split(serialized, 1, serialized.length - 1 - MAC_LENGTH, MAC_LENGTH); + byte version = messageParts[0][0]; + byte[] message = messageParts[1]; + byte[] mac = messageParts[2]; + + if (ByteUtil.highBitsToInt(version) <= CiphertextMessage.UNSUPPORTED_VERSION) { + throw new LegacyMessageException("Legacy message: " + ByteUtil.highBitsToInt(version)); + } + + if (ByteUtil.highBitsToInt(version) > CURRENT_VERSION) { + throw new InvalidMessageException("Unknown version: " + ByteUtil.highBitsToInt(version)); + } + + WhisperProtos.WhisperMessage whisperMessage = WhisperProtos.WhisperMessage.parseFrom(message); + + if (!whisperMessage.hasCiphertext() || + !whisperMessage.hasCounter() || + !whisperMessage.hasRatchetKey()) + { + throw new InvalidMessageException("Incomplete message."); + } + + this.serialized = serialized; + this.senderRatchetKey = Curve.decodePoint(whisperMessage.getRatchetKey().toByteArray(), 0); + this.messageVersion = ByteUtil.highBitsToInt(version); + this.counter = whisperMessage.getCounter(); + this.previousCounter = whisperMessage.getPreviousCounter(); + this.ciphertext = whisperMessage.getCiphertext().toByteArray(); + } catch (InvalidProtocolBufferException | InvalidKeyException | ParseException e) { + throw new InvalidMessageException(e); + } + } + + public WhisperMessage(int messageVersion, SecretKeySpec macKey, ECPublicKey senderRatchetKey, + int counter, int previousCounter, byte[] ciphertext, + IdentityKey senderIdentityKey, + IdentityKey receiverIdentityKey) + { + byte[] version = {ByteUtil.intsToByteHighAndLow(messageVersion, CURRENT_VERSION)}; + byte[] message = WhisperProtos.WhisperMessage.newBuilder() + .setRatchetKey(ByteString.copyFrom(senderRatchetKey.serialize())) + .setCounter(counter) + .setPreviousCounter(previousCounter) + .setCiphertext(ByteString.copyFrom(ciphertext)) + .build().toByteArray(); + + byte[] mac = getMac(messageVersion, senderIdentityKey, receiverIdentityKey, macKey, + ByteUtil.combine(version, message)); + + this.serialized = ByteUtil.combine(version, message, mac); + this.senderRatchetKey = senderRatchetKey; + this.counter = counter; + this.previousCounter = previousCounter; + this.ciphertext = ciphertext; + this.messageVersion = messageVersion; + } + + public ECPublicKey getSenderRatchetKey() { + return senderRatchetKey; + } + + public int getMessageVersion() { + return messageVersion; + } + + public int getCounter() { + return counter; + } + + public byte[] getBody() { + return ciphertext; + } + + public void verifyMac(int messageVersion, IdentityKey senderIdentityKey, + IdentityKey receiverIdentityKey, SecretKeySpec macKey) + throws InvalidMessageException + { + byte[][] parts = ByteUtil.split(serialized, serialized.length - MAC_LENGTH, MAC_LENGTH); + byte[] ourMac = getMac(messageVersion, senderIdentityKey, receiverIdentityKey, macKey, parts[0]); + byte[] theirMac = parts[1]; + + if (!MessageDigest.isEqual(ourMac, theirMac)) { + throw new InvalidMessageException("Bad Mac!"); + } + } + + private byte[] getMac(int messageVersion, + IdentityKey senderIdentityKey, + IdentityKey receiverIdentityKey, + SecretKeySpec macKey, byte[] serialized) + { + try { + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(macKey); + + if (messageVersion >= 3) { + mac.update(senderIdentityKey.getPublicKey().serialize()); + mac.update(receiverIdentityKey.getPublicKey().serialize()); + } + + byte[] fullMac = mac.doFinal(serialized); + return ByteUtil.trim(fullMac, MAC_LENGTH); + } catch (NoSuchAlgorithmException | java.security.InvalidKeyException e) { + throw new AssertionError(e); + } + } + + @Override + public byte[] serialize() { + return serialized; + } + + @Override + public int getType() { + return CiphertextMessage.WHISPER_TYPE; + } + + public static boolean isLegacy(byte[] message) { + return message != null && message.length >= 1 && + ByteUtil.highBitsToInt(message[0]) <= CiphertextMessage.UNSUPPORTED_VERSION; + } + +} diff --git a/src/main/java/org/whispersystems/libaxolotl/protocol/WhisperProtos.java b/src/main/java/org/whispersystems/libaxolotl/protocol/WhisperProtos.java new file mode 100644 index 000000000..12ab0272d --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/protocol/WhisperProtos.java @@ -0,0 +1,3532 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: WhisperTextProtocol.proto + +package org.whispersystems.libaxolotl.protocol; + +public final class WhisperProtos { + private WhisperProtos() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public interface WhisperMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes ratchetKey = 1; + /** + * optional bytes ratchetKey = 1; + */ + boolean hasRatchetKey(); + /** + * optional bytes ratchetKey = 1; + */ + com.google.protobuf.ByteString getRatchetKey(); + + // optional uint32 counter = 2; + /** + * optional uint32 counter = 2; + */ + boolean hasCounter(); + /** + * optional uint32 counter = 2; + */ + int getCounter(); + + // optional uint32 previousCounter = 3; + /** + * optional uint32 previousCounter = 3; + */ + boolean hasPreviousCounter(); + /** + * optional uint32 previousCounter = 3; + */ + int getPreviousCounter(); + + // optional bytes ciphertext = 4; + /** + * optional bytes ciphertext = 4; + */ + boolean hasCiphertext(); + /** + * optional bytes ciphertext = 4; + */ + com.google.protobuf.ByteString getCiphertext(); + } + /** + * Protobuf type {@code textsecure.WhisperMessage} + */ + public static final class WhisperMessage extends + com.google.protobuf.GeneratedMessage + implements WhisperMessageOrBuilder { + // Use WhisperMessage.newBuilder() to construct. + private WhisperMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private WhisperMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final WhisperMessage defaultInstance; + public static WhisperMessage getDefaultInstance() { + return defaultInstance; + } + + public WhisperMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private WhisperMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + ratchetKey_ = input.readBytes(); + break; + } + case 16: { + bitField0_ |= 0x00000002; + counter_ = input.readUInt32(); + break; + } + case 24: { + bitField0_ |= 0x00000004; + previousCounter_ = input.readUInt32(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + ciphertext_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_WhisperMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_WhisperMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage.class, org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public WhisperMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new WhisperMessage(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional bytes ratchetKey = 1; + public static final int RATCHETKEY_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString ratchetKey_; + /** + * optional bytes ratchetKey = 1; + */ + public boolean hasRatchetKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes ratchetKey = 1; + */ + public com.google.protobuf.ByteString getRatchetKey() { + return ratchetKey_; + } + + // optional uint32 counter = 2; + public static final int COUNTER_FIELD_NUMBER = 2; + private int counter_; + /** + * optional uint32 counter = 2; + */ + public boolean hasCounter() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 counter = 2; + */ + public int getCounter() { + return counter_; + } + + // optional uint32 previousCounter = 3; + public static final int PREVIOUSCOUNTER_FIELD_NUMBER = 3; + private int previousCounter_; + /** + * optional uint32 previousCounter = 3; + */ + public boolean hasPreviousCounter() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 previousCounter = 3; + */ + public int getPreviousCounter() { + return previousCounter_; + } + + // optional bytes ciphertext = 4; + public static final int CIPHERTEXT_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString ciphertext_; + /** + * optional bytes ciphertext = 4; + */ + public boolean hasCiphertext() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes ciphertext = 4; + */ + public com.google.protobuf.ByteString getCiphertext() { + return ciphertext_; + } + + private void initFields() { + ratchetKey_ = com.google.protobuf.ByteString.EMPTY; + counter_ = 0; + previousCounter_ = 0; + ciphertext_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, ratchetKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeUInt32(2, counter_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeUInt32(3, previousCounter_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, ciphertext_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, ratchetKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(2, counter_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(3, previousCounter_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, ciphertext_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.WhisperMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_WhisperMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_WhisperMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage.class, org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage.Builder.class); + } + + // Construct using org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + ratchetKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + counter_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + previousCounter_ = 0; + bitField0_ = (bitField0_ & ~0x00000004); + ciphertext_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_WhisperMessage_descriptor; + } + + public org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage getDefaultInstanceForType() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage.getDefaultInstance(); + } + + public org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage build() { + org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage buildPartial() { + org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage result = new org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.ratchetKey_ = ratchetKey_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.counter_ = counter_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.previousCounter_ = previousCounter_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.ciphertext_ = ciphertext_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage) { + return mergeFrom((org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage other) { + if (other == org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage.getDefaultInstance()) return this; + if (other.hasRatchetKey()) { + setRatchetKey(other.getRatchetKey()); + } + if (other.hasCounter()) { + setCounter(other.getCounter()); + } + if (other.hasPreviousCounter()) { + setPreviousCounter(other.getPreviousCounter()); + } + if (other.hasCiphertext()) { + setCiphertext(other.getCiphertext()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.libaxolotl.protocol.WhisperProtos.WhisperMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes ratchetKey = 1; + private com.google.protobuf.ByteString ratchetKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes ratchetKey = 1; + */ + public boolean hasRatchetKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes ratchetKey = 1; + */ + public com.google.protobuf.ByteString getRatchetKey() { + return ratchetKey_; + } + /** + * optional bytes ratchetKey = 1; + */ + public Builder setRatchetKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + ratchetKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes ratchetKey = 1; + */ + public Builder clearRatchetKey() { + bitField0_ = (bitField0_ & ~0x00000001); + ratchetKey_ = getDefaultInstance().getRatchetKey(); + onChanged(); + return this; + } + + // optional uint32 counter = 2; + private int counter_ ; + /** + * optional uint32 counter = 2; + */ + public boolean hasCounter() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 counter = 2; + */ + public int getCounter() { + return counter_; + } + /** + * optional uint32 counter = 2; + */ + public Builder setCounter(int value) { + bitField0_ |= 0x00000002; + counter_ = value; + onChanged(); + return this; + } + /** + * optional uint32 counter = 2; + */ + public Builder clearCounter() { + bitField0_ = (bitField0_ & ~0x00000002); + counter_ = 0; + onChanged(); + return this; + } + + // optional uint32 previousCounter = 3; + private int previousCounter_ ; + /** + * optional uint32 previousCounter = 3; + */ + public boolean hasPreviousCounter() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 previousCounter = 3; + */ + public int getPreviousCounter() { + return previousCounter_; + } + /** + * optional uint32 previousCounter = 3; + */ + public Builder setPreviousCounter(int value) { + bitField0_ |= 0x00000004; + previousCounter_ = value; + onChanged(); + return this; + } + /** + * optional uint32 previousCounter = 3; + */ + public Builder clearPreviousCounter() { + bitField0_ = (bitField0_ & ~0x00000004); + previousCounter_ = 0; + onChanged(); + return this; + } + + // optional bytes ciphertext = 4; + private com.google.protobuf.ByteString ciphertext_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes ciphertext = 4; + */ + public boolean hasCiphertext() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes ciphertext = 4; + */ + public com.google.protobuf.ByteString getCiphertext() { + return ciphertext_; + } + /** + * optional bytes ciphertext = 4; + */ + public Builder setCiphertext(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + ciphertext_ = value; + onChanged(); + return this; + } + /** + * optional bytes ciphertext = 4; + */ + public Builder clearCiphertext() { + bitField0_ = (bitField0_ & ~0x00000008); + ciphertext_ = getDefaultInstance().getCiphertext(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.WhisperMessage) + } + + static { + defaultInstance = new WhisperMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.WhisperMessage) + } + + public interface PreKeyWhisperMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 registrationId = 5; + /** + * optional uint32 registrationId = 5; + */ + boolean hasRegistrationId(); + /** + * optional uint32 registrationId = 5; + */ + int getRegistrationId(); + + // optional uint32 preKeyId = 1; + /** + * optional uint32 preKeyId = 1; + */ + boolean hasPreKeyId(); + /** + * optional uint32 preKeyId = 1; + */ + int getPreKeyId(); + + // optional uint32 signedPreKeyId = 6; + /** + * optional uint32 signedPreKeyId = 6; + */ + boolean hasSignedPreKeyId(); + /** + * optional uint32 signedPreKeyId = 6; + */ + int getSignedPreKeyId(); + + // optional bytes baseKey = 2; + /** + * optional bytes baseKey = 2; + */ + boolean hasBaseKey(); + /** + * optional bytes baseKey = 2; + */ + com.google.protobuf.ByteString getBaseKey(); + + // optional bytes identityKey = 3; + /** + * optional bytes identityKey = 3; + */ + boolean hasIdentityKey(); + /** + * optional bytes identityKey = 3; + */ + com.google.protobuf.ByteString getIdentityKey(); + + // optional bytes message = 4; + /** + * optional bytes message = 4; + * + *
+     * WhisperMessage
+     * 
+ */ + boolean hasMessage(); + /** + * optional bytes message = 4; + * + *
+     * WhisperMessage
+     * 
+ */ + com.google.protobuf.ByteString getMessage(); + } + /** + * Protobuf type {@code textsecure.PreKeyWhisperMessage} + */ + public static final class PreKeyWhisperMessage extends + com.google.protobuf.GeneratedMessage + implements PreKeyWhisperMessageOrBuilder { + // Use PreKeyWhisperMessage.newBuilder() to construct. + private PreKeyWhisperMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private PreKeyWhisperMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final PreKeyWhisperMessage defaultInstance; + public static PreKeyWhisperMessage getDefaultInstance() { + return defaultInstance; + } + + public PreKeyWhisperMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private PreKeyWhisperMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000002; + preKeyId_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000008; + baseKey_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000010; + identityKey_ = input.readBytes(); + break; + } + case 34: { + bitField0_ |= 0x00000020; + message_ = input.readBytes(); + break; + } + case 40: { + bitField0_ |= 0x00000001; + registrationId_ = input.readUInt32(); + break; + } + case 48: { + bitField0_ |= 0x00000004; + signedPreKeyId_ = input.readUInt32(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_PreKeyWhisperMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_PreKeyWhisperMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage.class, org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public PreKeyWhisperMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new PreKeyWhisperMessage(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 registrationId = 5; + public static final int REGISTRATIONID_FIELD_NUMBER = 5; + private int registrationId_; + /** + * optional uint32 registrationId = 5; + */ + public boolean hasRegistrationId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 registrationId = 5; + */ + public int getRegistrationId() { + return registrationId_; + } + + // optional uint32 preKeyId = 1; + public static final int PREKEYID_FIELD_NUMBER = 1; + private int preKeyId_; + /** + * optional uint32 preKeyId = 1; + */ + public boolean hasPreKeyId() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 preKeyId = 1; + */ + public int getPreKeyId() { + return preKeyId_; + } + + // optional uint32 signedPreKeyId = 6; + public static final int SIGNEDPREKEYID_FIELD_NUMBER = 6; + private int signedPreKeyId_; + /** + * optional uint32 signedPreKeyId = 6; + */ + public boolean hasSignedPreKeyId() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 signedPreKeyId = 6; + */ + public int getSignedPreKeyId() { + return signedPreKeyId_; + } + + // optional bytes baseKey = 2; + public static final int BASEKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString baseKey_; + /** + * optional bytes baseKey = 2; + */ + public boolean hasBaseKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes baseKey = 2; + */ + public com.google.protobuf.ByteString getBaseKey() { + return baseKey_; + } + + // optional bytes identityKey = 3; + public static final int IDENTITYKEY_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString identityKey_; + /** + * optional bytes identityKey = 3; + */ + public boolean hasIdentityKey() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes identityKey = 3; + */ + public com.google.protobuf.ByteString getIdentityKey() { + return identityKey_; + } + + // optional bytes message = 4; + public static final int MESSAGE_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString message_; + /** + * optional bytes message = 4; + * + *
+     * WhisperMessage
+     * 
+ */ + public boolean hasMessage() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes message = 4; + * + *
+     * WhisperMessage
+     * 
+ */ + public com.google.protobuf.ByteString getMessage() { + return message_; + } + + private void initFields() { + registrationId_ = 0; + preKeyId_ = 0; + signedPreKeyId_ = 0; + baseKey_ = com.google.protobuf.ByteString.EMPTY; + identityKey_ = com.google.protobuf.ByteString.EMPTY; + message_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeUInt32(1, preKeyId_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(2, baseKey_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeBytes(3, identityKey_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeBytes(4, message_); + } + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(5, registrationId_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeUInt32(6, signedPreKeyId_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, preKeyId_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, baseKey_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, identityKey_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, message_); + } + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(5, registrationId_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(6, signedPreKeyId_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.PreKeyWhisperMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_PreKeyWhisperMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_PreKeyWhisperMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage.class, org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage.Builder.class); + } + + // Construct using org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + registrationId_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + preKeyId_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + signedPreKeyId_ = 0; + bitField0_ = (bitField0_ & ~0x00000004); + baseKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + identityKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000010); + message_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000020); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_PreKeyWhisperMessage_descriptor; + } + + public org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage getDefaultInstanceForType() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage.getDefaultInstance(); + } + + public org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage build() { + org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage buildPartial() { + org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage result = new org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.registrationId_ = registrationId_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.preKeyId_ = preKeyId_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.signedPreKeyId_ = signedPreKeyId_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.baseKey_ = baseKey_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + result.identityKey_ = identityKey_; + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000020; + } + result.message_ = message_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage) { + return mergeFrom((org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage other) { + if (other == org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage.getDefaultInstance()) return this; + if (other.hasRegistrationId()) { + setRegistrationId(other.getRegistrationId()); + } + if (other.hasPreKeyId()) { + setPreKeyId(other.getPreKeyId()); + } + if (other.hasSignedPreKeyId()) { + setSignedPreKeyId(other.getSignedPreKeyId()); + } + if (other.hasBaseKey()) { + setBaseKey(other.getBaseKey()); + } + if (other.hasIdentityKey()) { + setIdentityKey(other.getIdentityKey()); + } + if (other.hasMessage()) { + setMessage(other.getMessage()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.libaxolotl.protocol.WhisperProtos.PreKeyWhisperMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 registrationId = 5; + private int registrationId_ ; + /** + * optional uint32 registrationId = 5; + */ + public boolean hasRegistrationId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 registrationId = 5; + */ + public int getRegistrationId() { + return registrationId_; + } + /** + * optional uint32 registrationId = 5; + */ + public Builder setRegistrationId(int value) { + bitField0_ |= 0x00000001; + registrationId_ = value; + onChanged(); + return this; + } + /** + * optional uint32 registrationId = 5; + */ + public Builder clearRegistrationId() { + bitField0_ = (bitField0_ & ~0x00000001); + registrationId_ = 0; + onChanged(); + return this; + } + + // optional uint32 preKeyId = 1; + private int preKeyId_ ; + /** + * optional uint32 preKeyId = 1; + */ + public boolean hasPreKeyId() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 preKeyId = 1; + */ + public int getPreKeyId() { + return preKeyId_; + } + /** + * optional uint32 preKeyId = 1; + */ + public Builder setPreKeyId(int value) { + bitField0_ |= 0x00000002; + preKeyId_ = value; + onChanged(); + return this; + } + /** + * optional uint32 preKeyId = 1; + */ + public Builder clearPreKeyId() { + bitField0_ = (bitField0_ & ~0x00000002); + preKeyId_ = 0; + onChanged(); + return this; + } + + // optional uint32 signedPreKeyId = 6; + private int signedPreKeyId_ ; + /** + * optional uint32 signedPreKeyId = 6; + */ + public boolean hasSignedPreKeyId() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 signedPreKeyId = 6; + */ + public int getSignedPreKeyId() { + return signedPreKeyId_; + } + /** + * optional uint32 signedPreKeyId = 6; + */ + public Builder setSignedPreKeyId(int value) { + bitField0_ |= 0x00000004; + signedPreKeyId_ = value; + onChanged(); + return this; + } + /** + * optional uint32 signedPreKeyId = 6; + */ + public Builder clearSignedPreKeyId() { + bitField0_ = (bitField0_ & ~0x00000004); + signedPreKeyId_ = 0; + onChanged(); + return this; + } + + // optional bytes baseKey = 2; + private com.google.protobuf.ByteString baseKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes baseKey = 2; + */ + public boolean hasBaseKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes baseKey = 2; + */ + public com.google.protobuf.ByteString getBaseKey() { + return baseKey_; + } + /** + * optional bytes baseKey = 2; + */ + public Builder setBaseKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + baseKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes baseKey = 2; + */ + public Builder clearBaseKey() { + bitField0_ = (bitField0_ & ~0x00000008); + baseKey_ = getDefaultInstance().getBaseKey(); + onChanged(); + return this; + } + + // optional bytes identityKey = 3; + private com.google.protobuf.ByteString identityKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes identityKey = 3; + */ + public boolean hasIdentityKey() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes identityKey = 3; + */ + public com.google.protobuf.ByteString getIdentityKey() { + return identityKey_; + } + /** + * optional bytes identityKey = 3; + */ + public Builder setIdentityKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000010; + identityKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes identityKey = 3; + */ + public Builder clearIdentityKey() { + bitField0_ = (bitField0_ & ~0x00000010); + identityKey_ = getDefaultInstance().getIdentityKey(); + onChanged(); + return this; + } + + // optional bytes message = 4; + private com.google.protobuf.ByteString message_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes message = 4; + * + *
+       * WhisperMessage
+       * 
+ */ + public boolean hasMessage() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes message = 4; + * + *
+       * WhisperMessage
+       * 
+ */ + public com.google.protobuf.ByteString getMessage() { + return message_; + } + /** + * optional bytes message = 4; + * + *
+       * WhisperMessage
+       * 
+ */ + public Builder setMessage(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000020; + message_ = value; + onChanged(); + return this; + } + /** + * optional bytes message = 4; + * + *
+       * WhisperMessage
+       * 
+ */ + public Builder clearMessage() { + bitField0_ = (bitField0_ & ~0x00000020); + message_ = getDefaultInstance().getMessage(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.PreKeyWhisperMessage) + } + + static { + defaultInstance = new PreKeyWhisperMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.PreKeyWhisperMessage) + } + + public interface KeyExchangeMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 id = 1; + /** + * optional uint32 id = 1; + */ + boolean hasId(); + /** + * optional uint32 id = 1; + */ + int getId(); + + // optional bytes baseKey = 2; + /** + * optional bytes baseKey = 2; + */ + boolean hasBaseKey(); + /** + * optional bytes baseKey = 2; + */ + com.google.protobuf.ByteString getBaseKey(); + + // optional bytes ratchetKey = 3; + /** + * optional bytes ratchetKey = 3; + */ + boolean hasRatchetKey(); + /** + * optional bytes ratchetKey = 3; + */ + com.google.protobuf.ByteString getRatchetKey(); + + // optional bytes identityKey = 4; + /** + * optional bytes identityKey = 4; + */ + boolean hasIdentityKey(); + /** + * optional bytes identityKey = 4; + */ + com.google.protobuf.ByteString getIdentityKey(); + + // optional bytes baseKeySignature = 5; + /** + * optional bytes baseKeySignature = 5; + */ + boolean hasBaseKeySignature(); + /** + * optional bytes baseKeySignature = 5; + */ + com.google.protobuf.ByteString getBaseKeySignature(); + } + /** + * Protobuf type {@code textsecure.KeyExchangeMessage} + */ + public static final class KeyExchangeMessage extends + com.google.protobuf.GeneratedMessage + implements KeyExchangeMessageOrBuilder { + // Use KeyExchangeMessage.newBuilder() to construct. + private KeyExchangeMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private KeyExchangeMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final KeyExchangeMessage defaultInstance; + public static KeyExchangeMessage getDefaultInstance() { + return defaultInstance; + } + + public KeyExchangeMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private KeyExchangeMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + baseKey_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + ratchetKey_ = input.readBytes(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + identityKey_ = input.readBytes(); + break; + } + case 42: { + bitField0_ |= 0x00000010; + baseKeySignature_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_KeyExchangeMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_KeyExchangeMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage.class, org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public KeyExchangeMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new KeyExchangeMessage(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private int id_; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + + // optional bytes baseKey = 2; + public static final int BASEKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString baseKey_; + /** + * optional bytes baseKey = 2; + */ + public boolean hasBaseKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes baseKey = 2; + */ + public com.google.protobuf.ByteString getBaseKey() { + return baseKey_; + } + + // optional bytes ratchetKey = 3; + public static final int RATCHETKEY_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString ratchetKey_; + /** + * optional bytes ratchetKey = 3; + */ + public boolean hasRatchetKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes ratchetKey = 3; + */ + public com.google.protobuf.ByteString getRatchetKey() { + return ratchetKey_; + } + + // optional bytes identityKey = 4; + public static final int IDENTITYKEY_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString identityKey_; + /** + * optional bytes identityKey = 4; + */ + public boolean hasIdentityKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes identityKey = 4; + */ + public com.google.protobuf.ByteString getIdentityKey() { + return identityKey_; + } + + // optional bytes baseKeySignature = 5; + public static final int BASEKEYSIGNATURE_FIELD_NUMBER = 5; + private com.google.protobuf.ByteString baseKeySignature_; + /** + * optional bytes baseKeySignature = 5; + */ + public boolean hasBaseKeySignature() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes baseKeySignature = 5; + */ + public com.google.protobuf.ByteString getBaseKeySignature() { + return baseKeySignature_; + } + + private void initFields() { + id_ = 0; + baseKey_ = com.google.protobuf.ByteString.EMPTY; + ratchetKey_ = com.google.protobuf.ByteString.EMPTY; + identityKey_ = com.google.protobuf.ByteString.EMPTY; + baseKeySignature_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, baseKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, ratchetKey_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, identityKey_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeBytes(5, baseKeySignature_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, baseKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, ratchetKey_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, identityKey_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(5, baseKeySignature_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.KeyExchangeMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_KeyExchangeMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_KeyExchangeMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage.class, org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage.Builder.class); + } + + // Construct using org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + baseKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + ratchetKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + identityKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + baseKeySignature_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000010); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_KeyExchangeMessage_descriptor; + } + + public org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage getDefaultInstanceForType() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage.getDefaultInstance(); + } + + public org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage build() { + org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage buildPartial() { + org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage result = new org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.baseKey_ = baseKey_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.ratchetKey_ = ratchetKey_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.identityKey_ = identityKey_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + result.baseKeySignature_ = baseKeySignature_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage) { + return mergeFrom((org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage other) { + if (other == org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasBaseKey()) { + setBaseKey(other.getBaseKey()); + } + if (other.hasRatchetKey()) { + setRatchetKey(other.getRatchetKey()); + } + if (other.hasIdentityKey()) { + setIdentityKey(other.getIdentityKey()); + } + if (other.hasBaseKeySignature()) { + setBaseKeySignature(other.getBaseKeySignature()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 id = 1; + private int id_ ; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + /** + * optional uint32 id = 1; + */ + public Builder setId(int value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint32 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0; + onChanged(); + return this; + } + + // optional bytes baseKey = 2; + private com.google.protobuf.ByteString baseKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes baseKey = 2; + */ + public boolean hasBaseKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes baseKey = 2; + */ + public com.google.protobuf.ByteString getBaseKey() { + return baseKey_; + } + /** + * optional bytes baseKey = 2; + */ + public Builder setBaseKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + baseKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes baseKey = 2; + */ + public Builder clearBaseKey() { + bitField0_ = (bitField0_ & ~0x00000002); + baseKey_ = getDefaultInstance().getBaseKey(); + onChanged(); + return this; + } + + // optional bytes ratchetKey = 3; + private com.google.protobuf.ByteString ratchetKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes ratchetKey = 3; + */ + public boolean hasRatchetKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes ratchetKey = 3; + */ + public com.google.protobuf.ByteString getRatchetKey() { + return ratchetKey_; + } + /** + * optional bytes ratchetKey = 3; + */ + public Builder setRatchetKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + ratchetKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes ratchetKey = 3; + */ + public Builder clearRatchetKey() { + bitField0_ = (bitField0_ & ~0x00000004); + ratchetKey_ = getDefaultInstance().getRatchetKey(); + onChanged(); + return this; + } + + // optional bytes identityKey = 4; + private com.google.protobuf.ByteString identityKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes identityKey = 4; + */ + public boolean hasIdentityKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes identityKey = 4; + */ + public com.google.protobuf.ByteString getIdentityKey() { + return identityKey_; + } + /** + * optional bytes identityKey = 4; + */ + public Builder setIdentityKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + identityKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes identityKey = 4; + */ + public Builder clearIdentityKey() { + bitField0_ = (bitField0_ & ~0x00000008); + identityKey_ = getDefaultInstance().getIdentityKey(); + onChanged(); + return this; + } + + // optional bytes baseKeySignature = 5; + private com.google.protobuf.ByteString baseKeySignature_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes baseKeySignature = 5; + */ + public boolean hasBaseKeySignature() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes baseKeySignature = 5; + */ + public com.google.protobuf.ByteString getBaseKeySignature() { + return baseKeySignature_; + } + /** + * optional bytes baseKeySignature = 5; + */ + public Builder setBaseKeySignature(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000010; + baseKeySignature_ = value; + onChanged(); + return this; + } + /** + * optional bytes baseKeySignature = 5; + */ + public Builder clearBaseKeySignature() { + bitField0_ = (bitField0_ & ~0x00000010); + baseKeySignature_ = getDefaultInstance().getBaseKeySignature(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.KeyExchangeMessage) + } + + static { + defaultInstance = new KeyExchangeMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.KeyExchangeMessage) + } + + public interface SenderKeyMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 id = 1; + /** + * optional uint32 id = 1; + */ + boolean hasId(); + /** + * optional uint32 id = 1; + */ + int getId(); + + // optional uint32 iteration = 2; + /** + * optional uint32 iteration = 2; + */ + boolean hasIteration(); + /** + * optional uint32 iteration = 2; + */ + int getIteration(); + + // optional bytes ciphertext = 3; + /** + * optional bytes ciphertext = 3; + */ + boolean hasCiphertext(); + /** + * optional bytes ciphertext = 3; + */ + com.google.protobuf.ByteString getCiphertext(); + } + /** + * Protobuf type {@code textsecure.SenderKeyMessage} + */ + public static final class SenderKeyMessage extends + com.google.protobuf.GeneratedMessage + implements SenderKeyMessageOrBuilder { + // Use SenderKeyMessage.newBuilder() to construct. + private SenderKeyMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SenderKeyMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SenderKeyMessage defaultInstance; + public static SenderKeyMessage getDefaultInstance() { + return defaultInstance; + } + + public SenderKeyMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SenderKeyMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt32(); + break; + } + case 16: { + bitField0_ |= 0x00000002; + iteration_ = input.readUInt32(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + ciphertext_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_SenderKeyMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_SenderKeyMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage.class, org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SenderKeyMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SenderKeyMessage(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private int id_; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + + // optional uint32 iteration = 2; + public static final int ITERATION_FIELD_NUMBER = 2; + private int iteration_; + /** + * optional uint32 iteration = 2; + */ + public boolean hasIteration() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 iteration = 2; + */ + public int getIteration() { + return iteration_; + } + + // optional bytes ciphertext = 3; + public static final int CIPHERTEXT_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString ciphertext_; + /** + * optional bytes ciphertext = 3; + */ + public boolean hasCiphertext() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes ciphertext = 3; + */ + public com.google.protobuf.ByteString getCiphertext() { + return ciphertext_; + } + + private void initFields() { + id_ = 0; + iteration_ = 0; + ciphertext_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeUInt32(2, iteration_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, ciphertext_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(2, iteration_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, ciphertext_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SenderKeyMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_SenderKeyMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_SenderKeyMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage.class, org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage.Builder.class); + } + + // Construct using org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + iteration_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + ciphertext_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_SenderKeyMessage_descriptor; + } + + public org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage getDefaultInstanceForType() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage.getDefaultInstance(); + } + + public org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage build() { + org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage buildPartial() { + org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage result = new org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.iteration_ = iteration_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.ciphertext_ = ciphertext_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage) { + return mergeFrom((org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage other) { + if (other == org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasIteration()) { + setIteration(other.getIteration()); + } + if (other.hasCiphertext()) { + setCiphertext(other.getCiphertext()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 id = 1; + private int id_ ; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + /** + * optional uint32 id = 1; + */ + public Builder setId(int value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint32 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0; + onChanged(); + return this; + } + + // optional uint32 iteration = 2; + private int iteration_ ; + /** + * optional uint32 iteration = 2; + */ + public boolean hasIteration() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 iteration = 2; + */ + public int getIteration() { + return iteration_; + } + /** + * optional uint32 iteration = 2; + */ + public Builder setIteration(int value) { + bitField0_ |= 0x00000002; + iteration_ = value; + onChanged(); + return this; + } + /** + * optional uint32 iteration = 2; + */ + public Builder clearIteration() { + bitField0_ = (bitField0_ & ~0x00000002); + iteration_ = 0; + onChanged(); + return this; + } + + // optional bytes ciphertext = 3; + private com.google.protobuf.ByteString ciphertext_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes ciphertext = 3; + */ + public boolean hasCiphertext() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes ciphertext = 3; + */ + public com.google.protobuf.ByteString getCiphertext() { + return ciphertext_; + } + /** + * optional bytes ciphertext = 3; + */ + public Builder setCiphertext(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + ciphertext_ = value; + onChanged(); + return this; + } + /** + * optional bytes ciphertext = 3; + */ + public Builder clearCiphertext() { + bitField0_ = (bitField0_ & ~0x00000004); + ciphertext_ = getDefaultInstance().getCiphertext(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SenderKeyMessage) + } + + static { + defaultInstance = new SenderKeyMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SenderKeyMessage) + } + + public interface SenderKeyDistributionMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 id = 1; + /** + * optional uint32 id = 1; + */ + boolean hasId(); + /** + * optional uint32 id = 1; + */ + int getId(); + + // optional uint32 iteration = 2; + /** + * optional uint32 iteration = 2; + */ + boolean hasIteration(); + /** + * optional uint32 iteration = 2; + */ + int getIteration(); + + // optional bytes chainKey = 3; + /** + * optional bytes chainKey = 3; + */ + boolean hasChainKey(); + /** + * optional bytes chainKey = 3; + */ + com.google.protobuf.ByteString getChainKey(); + + // optional bytes signingKey = 4; + /** + * optional bytes signingKey = 4; + */ + boolean hasSigningKey(); + /** + * optional bytes signingKey = 4; + */ + com.google.protobuf.ByteString getSigningKey(); + } + /** + * Protobuf type {@code textsecure.SenderKeyDistributionMessage} + */ + public static final class SenderKeyDistributionMessage extends + com.google.protobuf.GeneratedMessage + implements SenderKeyDistributionMessageOrBuilder { + // Use SenderKeyDistributionMessage.newBuilder() to construct. + private SenderKeyDistributionMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SenderKeyDistributionMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SenderKeyDistributionMessage defaultInstance; + public static SenderKeyDistributionMessage getDefaultInstance() { + return defaultInstance; + } + + public SenderKeyDistributionMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SenderKeyDistributionMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt32(); + break; + } + case 16: { + bitField0_ |= 0x00000002; + iteration_ = input.readUInt32(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + chainKey_ = input.readBytes(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + signingKey_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_SenderKeyDistributionMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_SenderKeyDistributionMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage.class, org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SenderKeyDistributionMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SenderKeyDistributionMessage(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private int id_; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + + // optional uint32 iteration = 2; + public static final int ITERATION_FIELD_NUMBER = 2; + private int iteration_; + /** + * optional uint32 iteration = 2; + */ + public boolean hasIteration() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 iteration = 2; + */ + public int getIteration() { + return iteration_; + } + + // optional bytes chainKey = 3; + public static final int CHAINKEY_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString chainKey_; + /** + * optional bytes chainKey = 3; + */ + public boolean hasChainKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes chainKey = 3; + */ + public com.google.protobuf.ByteString getChainKey() { + return chainKey_; + } + + // optional bytes signingKey = 4; + public static final int SIGNINGKEY_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString signingKey_; + /** + * optional bytes signingKey = 4; + */ + public boolean hasSigningKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes signingKey = 4; + */ + public com.google.protobuf.ByteString getSigningKey() { + return signingKey_; + } + + private void initFields() { + id_ = 0; + iteration_ = 0; + chainKey_ = com.google.protobuf.ByteString.EMPTY; + signingKey_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeUInt32(2, iteration_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, chainKey_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, signingKey_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(2, iteration_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, chainKey_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, signingKey_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SenderKeyDistributionMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_SenderKeyDistributionMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_SenderKeyDistributionMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage.class, org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage.Builder.class); + } + + // Construct using org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + iteration_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + chainKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + signingKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.internal_static_textsecure_SenderKeyDistributionMessage_descriptor; + } + + public org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage getDefaultInstanceForType() { + return org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage.getDefaultInstance(); + } + + public org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage build() { + org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage buildPartial() { + org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage result = new org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.iteration_ = iteration_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.chainKey_ = chainKey_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.signingKey_ = signingKey_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage) { + return mergeFrom((org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage other) { + if (other == org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasIteration()) { + setIteration(other.getIteration()); + } + if (other.hasChainKey()) { + setChainKey(other.getChainKey()); + } + if (other.hasSigningKey()) { + setSigningKey(other.getSigningKey()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.libaxolotl.protocol.WhisperProtos.SenderKeyDistributionMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 id = 1; + private int id_ ; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + /** + * optional uint32 id = 1; + */ + public Builder setId(int value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint32 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0; + onChanged(); + return this; + } + + // optional uint32 iteration = 2; + private int iteration_ ; + /** + * optional uint32 iteration = 2; + */ + public boolean hasIteration() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 iteration = 2; + */ + public int getIteration() { + return iteration_; + } + /** + * optional uint32 iteration = 2; + */ + public Builder setIteration(int value) { + bitField0_ |= 0x00000002; + iteration_ = value; + onChanged(); + return this; + } + /** + * optional uint32 iteration = 2; + */ + public Builder clearIteration() { + bitField0_ = (bitField0_ & ~0x00000002); + iteration_ = 0; + onChanged(); + return this; + } + + // optional bytes chainKey = 3; + private com.google.protobuf.ByteString chainKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes chainKey = 3; + */ + public boolean hasChainKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes chainKey = 3; + */ + public com.google.protobuf.ByteString getChainKey() { + return chainKey_; + } + /** + * optional bytes chainKey = 3; + */ + public Builder setChainKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + chainKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes chainKey = 3; + */ + public Builder clearChainKey() { + bitField0_ = (bitField0_ & ~0x00000004); + chainKey_ = getDefaultInstance().getChainKey(); + onChanged(); + return this; + } + + // optional bytes signingKey = 4; + private com.google.protobuf.ByteString signingKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes signingKey = 4; + */ + public boolean hasSigningKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes signingKey = 4; + */ + public com.google.protobuf.ByteString getSigningKey() { + return signingKey_; + } + /** + * optional bytes signingKey = 4; + */ + public Builder setSigningKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + signingKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes signingKey = 4; + */ + public Builder clearSigningKey() { + bitField0_ = (bitField0_ & ~0x00000008); + signingKey_ = getDefaultInstance().getSigningKey(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SenderKeyDistributionMessage) + } + + static { + defaultInstance = new SenderKeyDistributionMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SenderKeyDistributionMessage) + } + + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_WhisperMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_WhisperMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_PreKeyWhisperMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_PreKeyWhisperMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_KeyExchangeMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_KeyExchangeMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SenderKeyMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SenderKeyMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SenderKeyDistributionMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SenderKeyDistributionMessage_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\031WhisperTextProtocol.proto\022\ntextsecure\"" + + "b\n\016WhisperMessage\022\022\n\nratchetKey\030\001 \001(\014\022\017\n" + + "\007counter\030\002 \001(\r\022\027\n\017previousCounter\030\003 \001(\r\022" + + "\022\n\nciphertext\030\004 \001(\014\"\217\001\n\024PreKeyWhisperMes" + + "sage\022\026\n\016registrationId\030\005 \001(\r\022\020\n\010preKeyId" + + "\030\001 \001(\r\022\026\n\016signedPreKeyId\030\006 \001(\r\022\017\n\007baseKe" + + "y\030\002 \001(\014\022\023\n\013identityKey\030\003 \001(\014\022\017\n\007message\030" + + "\004 \001(\014\"t\n\022KeyExchangeMessage\022\n\n\002id\030\001 \001(\r\022" + + "\017\n\007baseKey\030\002 \001(\014\022\022\n\nratchetKey\030\003 \001(\014\022\023\n\013" + + "identityKey\030\004 \001(\014\022\030\n\020baseKeySignature\030\005 ", + "\001(\014\"E\n\020SenderKeyMessage\022\n\n\002id\030\001 \001(\r\022\021\n\ti" + + "teration\030\002 \001(\r\022\022\n\nciphertext\030\003 \001(\014\"c\n\034Se" + + "nderKeyDistributionMessage\022\n\n\002id\030\001 \001(\r\022\021" + + "\n\titeration\030\002 \001(\r\022\020\n\010chainKey\030\003 \001(\014\022\022\n\ns" + + "igningKey\030\004 \001(\014B7\n&org.whispersystems.li" + + "baxolotl.protocolB\rWhisperProtos" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + internal_static_textsecure_WhisperMessage_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_textsecure_WhisperMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_WhisperMessage_descriptor, + new java.lang.String[] { "RatchetKey", "Counter", "PreviousCounter", "Ciphertext", }); + internal_static_textsecure_PreKeyWhisperMessage_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_textsecure_PreKeyWhisperMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_PreKeyWhisperMessage_descriptor, + new java.lang.String[] { "RegistrationId", "PreKeyId", "SignedPreKeyId", "BaseKey", "IdentityKey", "Message", }); + internal_static_textsecure_KeyExchangeMessage_descriptor = + getDescriptor().getMessageTypes().get(2); + internal_static_textsecure_KeyExchangeMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_KeyExchangeMessage_descriptor, + new java.lang.String[] { "Id", "BaseKey", "RatchetKey", "IdentityKey", "BaseKeySignature", }); + internal_static_textsecure_SenderKeyMessage_descriptor = + getDescriptor().getMessageTypes().get(3); + internal_static_textsecure_SenderKeyMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SenderKeyMessage_descriptor, + new java.lang.String[] { "Id", "Iteration", "Ciphertext", }); + internal_static_textsecure_SenderKeyDistributionMessage_descriptor = + getDescriptor().getMessageTypes().get(4); + internal_static_textsecure_SenderKeyDistributionMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SenderKeyDistributionMessage_descriptor, + new java.lang.String[] { "Id", "Iteration", "ChainKey", "SigningKey", }); + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/src/main/java/org/whispersystems/libaxolotl/ratchet/AliceAxolotlParameters.java b/src/main/java/org/whispersystems/libaxolotl/ratchet/AliceAxolotlParameters.java new file mode 100644 index 000000000..13c995e97 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/ratchet/AliceAxolotlParameters.java @@ -0,0 +1,109 @@ +package org.whispersystems.libaxolotl.ratchet; + +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.IdentityKeyPair; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.util.guava.Optional; + +public class AliceAxolotlParameters { + + private final IdentityKeyPair ourIdentityKey; + private final ECKeyPair ourBaseKey; + + private final IdentityKey theirIdentityKey; + private final ECPublicKey theirSignedPreKey; + private final Optional theirOneTimePreKey; + private final ECPublicKey theirRatchetKey; + + private AliceAxolotlParameters(IdentityKeyPair ourIdentityKey, ECKeyPair ourBaseKey, + IdentityKey theirIdentityKey, ECPublicKey theirSignedPreKey, + ECPublicKey theirRatchetKey, Optional theirOneTimePreKey) + { + this.ourIdentityKey = ourIdentityKey; + this.ourBaseKey = ourBaseKey; + this.theirIdentityKey = theirIdentityKey; + this.theirSignedPreKey = theirSignedPreKey; + this.theirRatchetKey = theirRatchetKey; + this.theirOneTimePreKey = theirOneTimePreKey; + + if (ourIdentityKey == null || ourBaseKey == null || theirIdentityKey == null || + theirSignedPreKey == null || theirRatchetKey == null || theirOneTimePreKey == null) + { + throw new IllegalArgumentException("Null values!"); + } + } + + public IdentityKeyPair getOurIdentityKey() { + return ourIdentityKey; + } + + public ECKeyPair getOurBaseKey() { + return ourBaseKey; + } + + public IdentityKey getTheirIdentityKey() { + return theirIdentityKey; + } + + public ECPublicKey getTheirSignedPreKey() { + return theirSignedPreKey; + } + + public Optional getTheirOneTimePreKey() { + return theirOneTimePreKey; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public ECPublicKey getTheirRatchetKey() { + return theirRatchetKey; + } + + public static class Builder { + private IdentityKeyPair ourIdentityKey; + private ECKeyPair ourBaseKey; + + private IdentityKey theirIdentityKey; + private ECPublicKey theirSignedPreKey; + private ECPublicKey theirRatchetKey; + private Optional theirOneTimePreKey; + + public Builder setOurIdentityKey(IdentityKeyPair ourIdentityKey) { + this.ourIdentityKey = ourIdentityKey; + return this; + } + + public Builder setOurBaseKey(ECKeyPair ourBaseKey) { + this.ourBaseKey = ourBaseKey; + return this; + } + + public Builder setTheirRatchetKey(ECPublicKey theirRatchetKey) { + this.theirRatchetKey = theirRatchetKey; + return this; + } + + public Builder setTheirIdentityKey(IdentityKey theirIdentityKey) { + this.theirIdentityKey = theirIdentityKey; + return this; + } + + public Builder setTheirSignedPreKey(ECPublicKey theirSignedPreKey) { + this.theirSignedPreKey = theirSignedPreKey; + return this; + } + + public Builder setTheirOneTimePreKey(Optional theirOneTimePreKey) { + this.theirOneTimePreKey = theirOneTimePreKey; + return this; + } + + public AliceAxolotlParameters create() { + return new AliceAxolotlParameters(ourIdentityKey, ourBaseKey, theirIdentityKey, + theirSignedPreKey, theirRatchetKey, theirOneTimePreKey); + } + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/ratchet/BobAxolotlParameters.java b/src/main/java/org/whispersystems/libaxolotl/ratchet/BobAxolotlParameters.java new file mode 100644 index 000000000..27116a8b4 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/ratchet/BobAxolotlParameters.java @@ -0,0 +1,109 @@ +package org.whispersystems.libaxolotl.ratchet; + +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.IdentityKeyPair; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.util.guava.Optional; + +public class BobAxolotlParameters { + + private final IdentityKeyPair ourIdentityKey; + private final ECKeyPair ourSignedPreKey; + private final Optional ourOneTimePreKey; + private final ECKeyPair ourRatchetKey; + + private final IdentityKey theirIdentityKey; + private final ECPublicKey theirBaseKey; + + BobAxolotlParameters(IdentityKeyPair ourIdentityKey, ECKeyPair ourSignedPreKey, + ECKeyPair ourRatchetKey, Optional ourOneTimePreKey, + IdentityKey theirIdentityKey, ECPublicKey theirBaseKey) + { + this.ourIdentityKey = ourIdentityKey; + this.ourSignedPreKey = ourSignedPreKey; + this.ourRatchetKey = ourRatchetKey; + this.ourOneTimePreKey = ourOneTimePreKey; + this.theirIdentityKey = theirIdentityKey; + this.theirBaseKey = theirBaseKey; + + if (ourIdentityKey == null || ourSignedPreKey == null || ourRatchetKey == null || + ourOneTimePreKey == null || theirIdentityKey == null || theirBaseKey == null) + { + throw new IllegalArgumentException("Null value!"); + } + } + + public IdentityKeyPair getOurIdentityKey() { + return ourIdentityKey; + } + + public ECKeyPair getOurSignedPreKey() { + return ourSignedPreKey; + } + + public Optional getOurOneTimePreKey() { + return ourOneTimePreKey; + } + + public IdentityKey getTheirIdentityKey() { + return theirIdentityKey; + } + + public ECPublicKey getTheirBaseKey() { + return theirBaseKey; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public ECKeyPair getOurRatchetKey() { + return ourRatchetKey; + } + + public static class Builder { + private IdentityKeyPair ourIdentityKey; + private ECKeyPair ourSignedPreKey; + private Optional ourOneTimePreKey; + private ECKeyPair ourRatchetKey; + + private IdentityKey theirIdentityKey; + private ECPublicKey theirBaseKey; + + public Builder setOurIdentityKey(IdentityKeyPair ourIdentityKey) { + this.ourIdentityKey = ourIdentityKey; + return this; + } + + public Builder setOurSignedPreKey(ECKeyPair ourSignedPreKey) { + this.ourSignedPreKey = ourSignedPreKey; + return this; + } + + public Builder setOurOneTimePreKey(Optional ourOneTimePreKey) { + this.ourOneTimePreKey = ourOneTimePreKey; + return this; + } + + public Builder setTheirIdentityKey(IdentityKey theirIdentityKey) { + this.theirIdentityKey = theirIdentityKey; + return this; + } + + public Builder setTheirBaseKey(ECPublicKey theirBaseKey) { + this.theirBaseKey = theirBaseKey; + return this; + } + + public Builder setOurRatchetKey(ECKeyPair ourRatchetKey) { + this.ourRatchetKey = ourRatchetKey; + return this; + } + + public BobAxolotlParameters create() { + return new BobAxolotlParameters(ourIdentityKey, ourSignedPreKey, ourRatchetKey, + ourOneTimePreKey, theirIdentityKey, theirBaseKey); + } + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/ratchet/ChainKey.java b/src/main/java/org/whispersystems/libaxolotl/ratchet/ChainKey.java new file mode 100644 index 000000000..9dd1dbee6 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/ratchet/ChainKey.java @@ -0,0 +1,75 @@ +/** + * Copyright (C) 2014 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.whispersystems.libaxolotl.ratchet; + + +import org.whispersystems.libaxolotl.kdf.DerivedMessageSecrets; +import org.whispersystems.libaxolotl.kdf.HKDF; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +public class ChainKey { + + private static final byte[] MESSAGE_KEY_SEED = {0x01}; + private static final byte[] CHAIN_KEY_SEED = {0x02}; + + private final HKDF kdf; + private final byte[] key; + private final int index; + + public ChainKey(HKDF kdf, byte[] key, int index) { + this.kdf = kdf; + this.key = key; + this.index = index; + } + + public byte[] getKey() { + return key; + } + + public int getIndex() { + return index; + } + + public ChainKey getNextChainKey() { + byte[] nextKey = getBaseMaterial(CHAIN_KEY_SEED); + return new ChainKey(kdf, nextKey, index + 1); + } + + public MessageKeys getMessageKeys() { + byte[] inputKeyMaterial = getBaseMaterial(MESSAGE_KEY_SEED); + byte[] keyMaterialBytes = kdf.deriveSecrets(inputKeyMaterial, "WhisperMessageKeys".getBytes(), DerivedMessageSecrets.SIZE); + DerivedMessageSecrets keyMaterial = new DerivedMessageSecrets(keyMaterialBytes); + + return new MessageKeys(keyMaterial.getCipherKey(), keyMaterial.getMacKey(), keyMaterial.getIv(), index); + } + + private byte[] getBaseMaterial(byte[] seed) { + try { + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(key, "HmacSHA256")); + + return mac.doFinal(seed); + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + throw new AssertionError(e); + } + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/ratchet/MessageKeys.java b/src/main/java/org/whispersystems/libaxolotl/ratchet/MessageKeys.java new file mode 100644 index 000000000..95a8c7dc0 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/ratchet/MessageKeys.java @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2014 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.whispersystems.libaxolotl.ratchet; + +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class MessageKeys { + + private final SecretKeySpec cipherKey; + private final SecretKeySpec macKey; + private final IvParameterSpec iv; + private final int counter; + + public MessageKeys(SecretKeySpec cipherKey, SecretKeySpec macKey, IvParameterSpec iv, int counter) { + this.cipherKey = cipherKey; + this.macKey = macKey; + this.iv = iv; + this.counter = counter; + } + + public SecretKeySpec getCipherKey() { + return cipherKey; + } + + public SecretKeySpec getMacKey() { + return macKey; + } + + public IvParameterSpec getIv() { + return iv; + } + + public int getCounter() { + return counter; + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/ratchet/RatchetingSession.java b/src/main/java/org/whispersystems/libaxolotl/ratchet/RatchetingSession.java new file mode 100644 index 000000000..8c094ec0d --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/ratchet/RatchetingSession.java @@ -0,0 +1,179 @@ +/** + * Copyright (C) 2014 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.whispersystems.libaxolotl.ratchet; + +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.kdf.HKDF; +import org.whispersystems.libaxolotl.state.SessionState; +import org.whispersystems.libaxolotl.util.ByteUtil; +import org.whispersystems.libaxolotl.util.Pair; +import org.whispersystems.libaxolotl.util.guava.Optional; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; + +public class RatchetingSession { + + public static void initializeSession(SessionState sessionState, + int sessionVersion, + SymmetricAxolotlParameters parameters) + throws InvalidKeyException + { + if (isAlice(parameters.getOurBaseKey().getPublicKey(), parameters.getTheirBaseKey())) { + AliceAxolotlParameters.Builder aliceParameters = AliceAxolotlParameters.newBuilder(); + + aliceParameters.setOurBaseKey(parameters.getOurBaseKey()) + .setOurIdentityKey(parameters.getOurIdentityKey()) + .setTheirRatchetKey(parameters.getTheirRatchetKey()) + .setTheirIdentityKey(parameters.getTheirIdentityKey()) + .setTheirSignedPreKey(parameters.getTheirBaseKey()) + .setTheirOneTimePreKey(Optional.absent()); + + RatchetingSession.initializeSession(sessionState, sessionVersion, aliceParameters.create()); + } else { + BobAxolotlParameters.Builder bobParameters = BobAxolotlParameters.newBuilder(); + + bobParameters.setOurIdentityKey(parameters.getOurIdentityKey()) + .setOurRatchetKey(parameters.getOurRatchetKey()) + .setOurSignedPreKey(parameters.getOurBaseKey()) + .setOurOneTimePreKey(Optional.absent()) + .setTheirBaseKey(parameters.getTheirBaseKey()) + .setTheirIdentityKey(parameters.getTheirIdentityKey()); + + RatchetingSession.initializeSession(sessionState, sessionVersion, bobParameters.create()); + } + } + + public static void initializeSession(SessionState sessionState, + int sessionVersion, + AliceAxolotlParameters parameters) + throws InvalidKeyException + { + try { + sessionState.setSessionVersion(sessionVersion); + sessionState.setRemoteIdentityKey(parameters.getTheirIdentityKey()); + sessionState.setLocalIdentityKey(parameters.getOurIdentityKey().getPublicKey()); + + ECKeyPair sendingRatchetKey = Curve.generateKeyPair(); + ByteArrayOutputStream secrets = new ByteArrayOutputStream(); + + if (sessionVersion >= 3) { + secrets.write(getDiscontinuityBytes()); + } + + secrets.write(Curve.calculateAgreement(parameters.getTheirSignedPreKey(), + parameters.getOurIdentityKey().getPrivateKey())); + secrets.write(Curve.calculateAgreement(parameters.getTheirIdentityKey().getPublicKey(), + parameters.getOurBaseKey().getPrivateKey())); + secrets.write(Curve.calculateAgreement(parameters.getTheirSignedPreKey(), + parameters.getOurBaseKey().getPrivateKey())); + + if (sessionVersion >= 3 & parameters.getTheirOneTimePreKey().isPresent()) { + secrets.write(Curve.calculateAgreement(parameters.getTheirOneTimePreKey().get(), + parameters.getOurBaseKey().getPrivateKey())); + } + + DerivedKeys derivedKeys = calculateDerivedKeys(sessionVersion, secrets.toByteArray()); + Pair sendingChain = derivedKeys.getRootKey().createChain(parameters.getTheirRatchetKey(), sendingRatchetKey); + + sessionState.addReceiverChain(parameters.getTheirRatchetKey(), derivedKeys.getChainKey()); + sessionState.setSenderChain(sendingRatchetKey, sendingChain.second()); + sessionState.setRootKey(sendingChain.first()); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + public static void initializeSession(SessionState sessionState, + int sessionVersion, + BobAxolotlParameters parameters) + throws InvalidKeyException + { + + try { + sessionState.setSessionVersion(sessionVersion); + sessionState.setRemoteIdentityKey(parameters.getTheirIdentityKey()); + sessionState.setLocalIdentityKey(parameters.getOurIdentityKey().getPublicKey()); + + ByteArrayOutputStream secrets = new ByteArrayOutputStream(); + + if (sessionVersion >= 3) { + secrets.write(getDiscontinuityBytes()); + } + + secrets.write(Curve.calculateAgreement(parameters.getTheirIdentityKey().getPublicKey(), + parameters.getOurSignedPreKey().getPrivateKey())); + secrets.write(Curve.calculateAgreement(parameters.getTheirBaseKey(), + parameters.getOurIdentityKey().getPrivateKey())); + secrets.write(Curve.calculateAgreement(parameters.getTheirBaseKey(), + parameters.getOurSignedPreKey().getPrivateKey())); + + if (sessionVersion >= 3 && parameters.getOurOneTimePreKey().isPresent()) { + secrets.write(Curve.calculateAgreement(parameters.getTheirBaseKey(), + parameters.getOurOneTimePreKey().get().getPrivateKey())); + } + + DerivedKeys derivedKeys = calculateDerivedKeys(sessionVersion, secrets.toByteArray()); + + sessionState.setSenderChain(parameters.getOurRatchetKey(), derivedKeys.getChainKey()); + sessionState.setRootKey(derivedKeys.getRootKey()); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + private static byte[] getDiscontinuityBytes() { + byte[] discontinuity = new byte[32]; + Arrays.fill(discontinuity, (byte) 0xFF); + return discontinuity; + } + + private static DerivedKeys calculateDerivedKeys(int sessionVersion, byte[] masterSecret) { + HKDF kdf = HKDF.createFor(sessionVersion); + byte[] derivedSecretBytes = kdf.deriveSecrets(masterSecret, "WhisperText".getBytes(), 64); + byte[][] derivedSecrets = ByteUtil.split(derivedSecretBytes, 32, 32); + + return new DerivedKeys(new RootKey(kdf, derivedSecrets[0]), + new ChainKey(kdf, derivedSecrets[1], 0)); + } + + private static boolean isAlice(ECPublicKey ourKey, ECPublicKey theirKey) { + return ourKey.compareTo(theirKey) < 0; + } + + private static class DerivedKeys { + private final RootKey rootKey; + private final ChainKey chainKey; + + private DerivedKeys(RootKey rootKey, ChainKey chainKey) { + this.rootKey = rootKey; + this.chainKey = chainKey; + } + + public RootKey getRootKey() { + return rootKey; + } + + public ChainKey getChainKey() { + return chainKey; + } + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/ratchet/RootKey.java b/src/main/java/org/whispersystems/libaxolotl/ratchet/RootKey.java new file mode 100644 index 000000000..39f3a831c --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/ratchet/RootKey.java @@ -0,0 +1,54 @@ +/** + * Copyright (C) 2014 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.whispersystems.libaxolotl.ratchet; + +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.kdf.DerivedRootSecrets; +import org.whispersystems.libaxolotl.kdf.HKDF; +import org.whispersystems.libaxolotl.util.ByteUtil; +import org.whispersystems.libaxolotl.util.Pair; + +public class RootKey { + + private final HKDF kdf; + private final byte[] key; + + public RootKey(HKDF kdf, byte[] key) { + this.kdf = kdf; + this.key = key; + } + + public byte[] getKeyBytes() { + return key; + } + + public Pair createChain(ECPublicKey theirRatchetKey, ECKeyPair ourRatchetKey) + throws InvalidKeyException + { + byte[] sharedSecret = Curve.calculateAgreement(theirRatchetKey, ourRatchetKey.getPrivateKey()); + byte[] derivedSecretBytes = kdf.deriveSecrets(sharedSecret, key, "WhisperRatchet".getBytes(), DerivedRootSecrets.SIZE); + DerivedRootSecrets derivedSecrets = new DerivedRootSecrets(derivedSecretBytes); + + RootKey newRootKey = new RootKey(kdf, derivedSecrets.getRootKey()); + ChainKey newChainKey = new ChainKey(kdf, derivedSecrets.getChainKey(), 0); + + return new Pair<>(newRootKey, newChainKey); + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/ratchet/SymmetricAxolotlParameters.java b/src/main/java/org/whispersystems/libaxolotl/ratchet/SymmetricAxolotlParameters.java new file mode 100644 index 000000000..7a63c45ff --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/ratchet/SymmetricAxolotlParameters.java @@ -0,0 +1,108 @@ +package org.whispersystems.libaxolotl.ratchet; + +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.IdentityKeyPair; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; + +public class SymmetricAxolotlParameters { + + private final ECKeyPair ourBaseKey; + private final ECKeyPair ourRatchetKey; + private final IdentityKeyPair ourIdentityKey; + + private final ECPublicKey theirBaseKey; + private final ECPublicKey theirRatchetKey; + private final IdentityKey theirIdentityKey; + + SymmetricAxolotlParameters(ECKeyPair ourBaseKey, ECKeyPair ourRatchetKey, + IdentityKeyPair ourIdentityKey, ECPublicKey theirBaseKey, + ECPublicKey theirRatchetKey, IdentityKey theirIdentityKey) + { + this.ourBaseKey = ourBaseKey; + this.ourRatchetKey = ourRatchetKey; + this.ourIdentityKey = ourIdentityKey; + this.theirBaseKey = theirBaseKey; + this.theirRatchetKey = theirRatchetKey; + this.theirIdentityKey = theirIdentityKey; + + if (ourBaseKey == null || ourRatchetKey == null || ourIdentityKey == null || + theirBaseKey == null || theirRatchetKey == null || theirIdentityKey == null) + { + throw new IllegalArgumentException("Null values!"); + } + } + + public ECKeyPair getOurBaseKey() { + return ourBaseKey; + } + + public ECKeyPair getOurRatchetKey() { + return ourRatchetKey; + } + + public IdentityKeyPair getOurIdentityKey() { + return ourIdentityKey; + } + + public ECPublicKey getTheirBaseKey() { + return theirBaseKey; + } + + public ECPublicKey getTheirRatchetKey() { + return theirRatchetKey; + } + + public IdentityKey getTheirIdentityKey() { + return theirIdentityKey; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public static class Builder { + private ECKeyPair ourBaseKey; + private ECKeyPair ourRatchetKey; + private IdentityKeyPair ourIdentityKey; + + private ECPublicKey theirBaseKey; + private ECPublicKey theirRatchetKey; + private IdentityKey theirIdentityKey; + + public Builder setOurBaseKey(ECKeyPair ourBaseKey) { + this.ourBaseKey = ourBaseKey; + return this; + } + + public Builder setOurRatchetKey(ECKeyPair ourRatchetKey) { + this.ourRatchetKey = ourRatchetKey; + return this; + } + + public Builder setOurIdentityKey(IdentityKeyPair ourIdentityKey) { + this.ourIdentityKey = ourIdentityKey; + return this; + } + + public Builder setTheirBaseKey(ECPublicKey theirBaseKey) { + this.theirBaseKey = theirBaseKey; + return this; + } + + public Builder setTheirRatchetKey(ECPublicKey theirRatchetKey) { + this.theirRatchetKey = theirRatchetKey; + return this; + } + + public Builder setTheirIdentityKey(IdentityKey theirIdentityKey) { + this.theirIdentityKey = theirIdentityKey; + return this; + } + + public SymmetricAxolotlParameters create() { + return new SymmetricAxolotlParameters(ourBaseKey, ourRatchetKey, ourIdentityKey, + theirBaseKey, theirRatchetKey, theirIdentityKey); + } + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/state/AxolotlStore.java b/src/main/java/org/whispersystems/libaxolotl/state/AxolotlStore.java new file mode 100644 index 000000000..2fda9a215 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/state/AxolotlStore.java @@ -0,0 +1,6 @@ +package org.whispersystems.libaxolotl.state; + +public interface AxolotlStore + extends IdentityKeyStore, PreKeyStore, SessionStore, SignedPreKeyStore +{ +} diff --git a/src/main/java/org/whispersystems/libaxolotl/state/IdentityKeyStore.java b/src/main/java/org/whispersystems/libaxolotl/state/IdentityKeyStore.java new file mode 100644 index 000000000..d2024f783 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/state/IdentityKeyStore.java @@ -0,0 +1,57 @@ +package org.whispersystems.libaxolotl.state; + +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.IdentityKeyPair; + +/** + * Provides an interface to identity information. + * + * @author Moxie Marlinspike + */ +public interface IdentityKeyStore { + + /** + * Get the local client's identity key pair. + * + * @return The local client's persistent identity key pair. + */ + public IdentityKeyPair getIdentityKeyPair(); + + /** + * Return the local client's registration ID. + *

+ * Clients should maintain a registration ID, a random number + * between 1 and 16380 that's generated once at install time. + * + * @return the local client's registration ID. + */ + public int getLocalRegistrationId(); + + /** + * Save a remote client's identity key + *

+ * Store a remote client's identity key as trusted. + * + * @param recipientId The recipient ID of the remote client. + * @param identityKey The remote client's identity key. + */ + public void saveIdentity(long recipientId, IdentityKey identityKey); + + + /** + * Verify a remote client's identity key. + *

+ * Determine whether a remote client's identity is trusted. Convention is + * that the TextSecure protocol is 'trust on first use.' This means that + * an identity key is considered 'trusted' if there is no entry for the recipient + * in the local store, or if it matches the saved key for a recipient in the local + * store. Only if it mismatches an entry in the local store is it considered + * 'untrusted.' + * + * @param recipientId The recipient ID of the remote client. + * @param identityKey The identity key to verify. + * @return true if trusted, false if untrusted. + */ + public boolean isTrustedIdentity(long recipientId, IdentityKey identityKey); + +} diff --git a/src/main/java/org/whispersystems/libaxolotl/state/PreKeyBundle.java b/src/main/java/org/whispersystems/libaxolotl/state/PreKeyBundle.java new file mode 100644 index 000000000..772bcc14c --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/state/PreKeyBundle.java @@ -0,0 +1,96 @@ +package org.whispersystems.libaxolotl.state; + +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; + +/** + * A class that contains a remote PreKey and collection + * of associated items. + * + * @author Moxie Marlinspike + */ +public class PreKeyBundle { + + private int registrationId; + + private int deviceId; + + private int preKeyId; + private ECPublicKey preKeyPublic; + + private int signedPreKeyId; + private ECPublicKey signedPreKeyPublic; + private byte[] signedPreKeySignature; + + private IdentityKey identityKey; + + public PreKeyBundle(int registrationId, int deviceId, int preKeyId, ECPublicKey preKeyPublic, + int signedPreKeyId, ECPublicKey signedPreKeyPublic, byte[] signedPreKeySignature, + IdentityKey identityKey) + { + this.registrationId = registrationId; + this.deviceId = deviceId; + this.preKeyId = preKeyId; + this.preKeyPublic = preKeyPublic; + this.signedPreKeyId = signedPreKeyId; + this.signedPreKeyPublic = signedPreKeyPublic; + this.signedPreKeySignature = signedPreKeySignature; + this.identityKey = identityKey; + } + + /** + * @return the device ID this PreKey belongs to. + */ + public int getDeviceId() { + return deviceId; + } + + /** + * @return the unique key ID for this PreKey. + */ + public int getPreKeyId() { + return preKeyId; + } + + /** + * @return the public key for this PreKey. + */ + public ECPublicKey getPreKey() { + return preKeyPublic; + } + + /** + * @return the unique key ID for this signed prekey. + */ + public int getSignedPreKeyId() { + return signedPreKeyId; + } + + /** + * @return the signed prekey for this PreKeyBundle. + */ + public ECPublicKey getSignedPreKey() { + return signedPreKeyPublic; + } + + /** + * @return the signature over the signed prekey. + */ + public byte[] getSignedPreKeySignature() { + return signedPreKeySignature; + } + + /** + * @return the {@link org.whispersystems.libaxolotl.IdentityKey} of this PreKeys owner. + */ + public IdentityKey getIdentityKey() { + return identityKey; + } + + /** + * @return the registration ID associated with this PreKey. + */ + public int getRegistrationId() { + return registrationId; + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/state/PreKeyRecord.java b/src/main/java/org/whispersystems/libaxolotl/state/PreKeyRecord.java new file mode 100644 index 000000000..0ea905f44 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/state/PreKeyRecord.java @@ -0,0 +1,51 @@ +package org.whispersystems.libaxolotl.state; + +import com.google.protobuf.ByteString; + +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.ecc.ECPrivateKey; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; + +import java.io.IOException; + +import static org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure; + +public class PreKeyRecord { + + private PreKeyRecordStructure structure; + + public PreKeyRecord(int id, ECKeyPair keyPair) { + this.structure = PreKeyRecordStructure.newBuilder() + .setId(id) + .setPublicKey(ByteString.copyFrom(keyPair.getPublicKey() + .serialize())) + .setPrivateKey(ByteString.copyFrom(keyPair.getPrivateKey() + .serialize())) + .build(); + } + + public PreKeyRecord(byte[] serialized) throws IOException { + this.structure = PreKeyRecordStructure.parseFrom(serialized); + } + + public int getId() { + return this.structure.getId(); + } + + public ECKeyPair getKeyPair() { + try { + ECPublicKey publicKey = Curve.decodePoint(this.structure.getPublicKey().toByteArray(), 0); + ECPrivateKey privateKey = Curve.decodePrivatePoint(this.structure.getPrivateKey().toByteArray()); + + return new ECKeyPair(publicKey, privateKey); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + public byte[] serialize() { + return this.structure.toByteArray(); + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/state/PreKeyStore.java b/src/main/java/org/whispersystems/libaxolotl/state/PreKeyStore.java new file mode 100644 index 000000000..7dc5e626e --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/state/PreKeyStore.java @@ -0,0 +1,42 @@ +package org.whispersystems.libaxolotl.state; + +import org.whispersystems.libaxolotl.InvalidKeyIdException; + +/** + * An interface describing the local storage of {@link PreKeyRecord}s. + * + * @author Moxie Marlinspike + */ +public interface PreKeyStore { + + /** + * Load a local PreKeyRecord. + * + * @param preKeyId the ID of the local PreKeyRecord. + * @return the corresponding PreKeyRecord. + * @throws InvalidKeyIdException when there is no corresponding PreKeyRecord. + */ + public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException; + + /** + * Store a local PreKeyRecord. + * + * @param preKeyId the ID of the PreKeyRecord to store. + * @param record the PreKeyRecord. + */ + public void storePreKey(int preKeyId, PreKeyRecord record); + + /** + * @param preKeyId A PreKeyRecord ID. + * @return true if the store has a record for the preKeyId, otherwise false. + */ + public boolean containsPreKey(int preKeyId); + + /** + * Delete a PreKeyRecord from local storage. + * + * @param preKeyId The ID of the PreKeyRecord to remove. + */ + public void removePreKey(int preKeyId); + +} diff --git a/src/main/java/org/whispersystems/libaxolotl/state/SessionRecord.java b/src/main/java/org/whispersystems/libaxolotl/state/SessionRecord.java new file mode 100644 index 000000000..76c64922c --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/state/SessionRecord.java @@ -0,0 +1,117 @@ +package org.whispersystems.libaxolotl.state; + +import java.io.IOException; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import static org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure; +import static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure; + +/** + * A SessionRecord encapsulates the state of an ongoing session. + * + * @author Moxie Marlinspike + */ +public class SessionRecord { + + private static final int ARCHIVED_STATES_MAX_LENGTH = 40; + + private SessionState sessionState = new SessionState(); + private LinkedList previousStates = new LinkedList<>(); + private boolean fresh = false; + + public SessionRecord() { + this.fresh = true; + } + + public SessionRecord(SessionState sessionState) { + this.sessionState = sessionState; + this.fresh = false; + } + + public SessionRecord(byte[] serialized) throws IOException { + RecordStructure record = RecordStructure.parseFrom(serialized); + this.sessionState = new SessionState(record.getCurrentSession()); + this.fresh = false; + + for (SessionStructure previousStructure : record.getPreviousSessionsList()) { + previousStates.add(new SessionState(previousStructure)); + } + } + + public boolean hasSessionState(int version, byte[] aliceBaseKey) { + if (sessionState.getSessionVersion() == version && + Arrays.equals(aliceBaseKey, sessionState.getAliceBaseKey())) + { + return true; + } + + for (SessionState state : previousStates) { + if (state.getSessionVersion() == version && + Arrays.equals(aliceBaseKey, state.getAliceBaseKey())) + { + return true; + } + } + + return false; + } + + public SessionState getSessionState() { + return sessionState; + } + + /** + * @return the list of all currently maintained "previous" session states. + */ + public List getPreviousSessionStates() { + return previousStates; + } + + + public boolean isFresh() { + return fresh; + } + + /** + * Move the current {@link SessionState} into the list of "previous" session states, + * and replace the current {@link org.whispersystems.libaxolotl.state.SessionState} + * with a fresh reset instance. + */ + public void archiveCurrentState() { + promoteState(new SessionState()); + } + + public void promoteState(SessionState promotedState) { + this.previousStates.addFirst(sessionState); + this.sessionState = promotedState; + + if (previousStates.size() > ARCHIVED_STATES_MAX_LENGTH) { + previousStates.removeLast(); + } + } + + public void setState(SessionState sessionState) { + this.sessionState = sessionState; + } + + /** + * @return a serialized version of the current SessionRecord. + */ + public byte[] serialize() { + List previousStructures = new LinkedList<>(); + + for (SessionState previousState : previousStates) { + previousStructures.add(previousState.getStructure()); + } + + RecordStructure record = RecordStructure.newBuilder() + .setCurrentSession(sessionState.getStructure()) + .addAllPreviousSessions(previousStructures) + .build(); + + return record.toByteArray(); + } + +} diff --git a/src/main/java/org/whispersystems/libaxolotl/state/SessionState.java b/src/main/java/org/whispersystems/libaxolotl/state/SessionState.java new file mode 100644 index 000000000..9b2b1e2ae --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/state/SessionState.java @@ -0,0 +1,509 @@ +/** + * Copyright (C) 2014 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.whispersystems.libaxolotl.state; + +import android.util.Log; + +import com.google.protobuf.ByteString; + +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.IdentityKeyPair; +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.ecc.ECPrivateKey; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.kdf.HKDF; +import org.whispersystems.libaxolotl.ratchet.ChainKey; +import org.whispersystems.libaxolotl.ratchet.MessageKeys; +import org.whispersystems.libaxolotl.ratchet.RootKey; +import org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain; +import org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange; +import org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey; +import org.whispersystems.libaxolotl.util.Pair; +import org.whispersystems.libaxolotl.util.guava.Optional; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure; + +public class SessionState { + + private SessionStructure sessionStructure; + + public SessionState() { + this.sessionStructure = SessionStructure.newBuilder().build(); + } + + public SessionState(SessionStructure sessionStructure) { + this.sessionStructure = sessionStructure; + } + + public SessionState(SessionState copy) { + this.sessionStructure = copy.sessionStructure.toBuilder().build(); + } + + public SessionStructure getStructure() { + return sessionStructure; + } + + public byte[] getAliceBaseKey() { + return this.sessionStructure.getAliceBaseKey().toByteArray(); + } + + public void setAliceBaseKey(byte[] aliceBaseKey) { + this.sessionStructure = this.sessionStructure.toBuilder() + .setAliceBaseKey(ByteString.copyFrom(aliceBaseKey)) + .build(); + } + + public void setSessionVersion(int version) { + this.sessionStructure = this.sessionStructure.toBuilder() + .setSessionVersion(version) + .build(); + } + + public int getSessionVersion() { + int sessionVersion = this.sessionStructure.getSessionVersion(); + + if (sessionVersion == 0) return 2; + else return sessionVersion; + } + + public void setRemoteIdentityKey(IdentityKey identityKey) { + this.sessionStructure = this.sessionStructure.toBuilder() + .setRemoteIdentityPublic(ByteString.copyFrom(identityKey.serialize())) + .build(); + } + + public void setLocalIdentityKey(IdentityKey identityKey) { + this.sessionStructure = this.sessionStructure.toBuilder() + .setLocalIdentityPublic(ByteString.copyFrom(identityKey.serialize())) + .build(); + } + + public IdentityKey getRemoteIdentityKey() { + try { + if (!this.sessionStructure.hasRemoteIdentityPublic()) { + return null; + } + + return new IdentityKey(this.sessionStructure.getRemoteIdentityPublic().toByteArray(), 0); + } catch (InvalidKeyException e) { + Log.w("SessionRecordV2", e); + return null; + } + } + + public IdentityKey getLocalIdentityKey() { + try { + return new IdentityKey(this.sessionStructure.getLocalIdentityPublic().toByteArray(), 0); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + public int getPreviousCounter() { + return sessionStructure.getPreviousCounter(); + } + + public void setPreviousCounter(int previousCounter) { + this.sessionStructure = this.sessionStructure.toBuilder() + .setPreviousCounter(previousCounter) + .build(); + } + + public RootKey getRootKey() { + return new RootKey(HKDF.createFor(getSessionVersion()), + this.sessionStructure.getRootKey().toByteArray()); + } + + public void setRootKey(RootKey rootKey) { + this.sessionStructure = this.sessionStructure.toBuilder() + .setRootKey(ByteString.copyFrom(rootKey.getKeyBytes())) + .build(); + } + + public ECPublicKey getSenderRatchetKey() { + try { + return Curve.decodePoint(sessionStructure.getSenderChain().getSenderRatchetKey().toByteArray(), 0); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + public ECKeyPair getSenderRatchetKeyPair() { + ECPublicKey publicKey = getSenderRatchetKey(); + ECPrivateKey privateKey = Curve.decodePrivatePoint(sessionStructure.getSenderChain() + .getSenderRatchetKeyPrivate() + .toByteArray()); + + return new ECKeyPair(publicKey, privateKey); + } + + public boolean hasReceiverChain(ECPublicKey senderEphemeral) { + return getReceiverChain(senderEphemeral) != null; + } + + public boolean hasSenderChain() { + return sessionStructure.hasSenderChain(); + } + + private Pair getReceiverChain(ECPublicKey senderEphemeral) { + List receiverChains = sessionStructure.getReceiverChainsList(); + int index = 0; + + for (Chain receiverChain : receiverChains) { + try { + ECPublicKey chainSenderRatchetKey = Curve.decodePoint(receiverChain.getSenderRatchetKey().toByteArray(), 0); + + if (chainSenderRatchetKey.equals(senderEphemeral)) { + return new Pair<>(receiverChain,index); + } + } catch (InvalidKeyException e) { + Log.w("SessionRecordV2", e); + } + + index++; + } + + return null; + } + + public ChainKey getReceiverChainKey(ECPublicKey senderEphemeral) { + Pair receiverChainAndIndex = getReceiverChain(senderEphemeral); + Chain receiverChain = receiverChainAndIndex.first(); + + if (receiverChain == null) { + return null; + } else { + return new ChainKey(HKDF.createFor(getSessionVersion()), + receiverChain.getChainKey().getKey().toByteArray(), + receiverChain.getChainKey().getIndex()); + } + } + + public void addReceiverChain(ECPublicKey senderRatchetKey, ChainKey chainKey) { + Chain.ChainKey chainKeyStructure = Chain.ChainKey.newBuilder() + .setKey(ByteString.copyFrom(chainKey.getKey())) + .setIndex(chainKey.getIndex()) + .build(); + + Chain chain = Chain.newBuilder() + .setChainKey(chainKeyStructure) + .setSenderRatchetKey(ByteString.copyFrom(senderRatchetKey.serialize())) + .build(); + + this.sessionStructure = this.sessionStructure.toBuilder().addReceiverChains(chain).build(); + + if (this.sessionStructure.getReceiverChainsList().size() > 5) { + this.sessionStructure = this.sessionStructure.toBuilder() + .removeReceiverChains(0) + .build(); + } + } + + public void setSenderChain(ECKeyPair senderRatchetKeyPair, ChainKey chainKey) { + Chain.ChainKey chainKeyStructure = Chain.ChainKey.newBuilder() + .setKey(ByteString.copyFrom(chainKey.getKey())) + .setIndex(chainKey.getIndex()) + .build(); + + Chain senderChain = Chain.newBuilder() + .setSenderRatchetKey(ByteString.copyFrom(senderRatchetKeyPair.getPublicKey().serialize())) + .setSenderRatchetKeyPrivate(ByteString.copyFrom(senderRatchetKeyPair.getPrivateKey().serialize())) + .setChainKey(chainKeyStructure) + .build(); + + this.sessionStructure = this.sessionStructure.toBuilder().setSenderChain(senderChain).build(); + } + + public ChainKey getSenderChainKey() { + Chain.ChainKey chainKeyStructure = sessionStructure.getSenderChain().getChainKey(); + return new ChainKey(HKDF.createFor(getSessionVersion()), + chainKeyStructure.getKey().toByteArray(), chainKeyStructure.getIndex()); + } + + + public void setSenderChainKey(ChainKey nextChainKey) { + Chain.ChainKey chainKey = Chain.ChainKey.newBuilder() + .setKey(ByteString.copyFrom(nextChainKey.getKey())) + .setIndex(nextChainKey.getIndex()) + .build(); + + Chain chain = sessionStructure.getSenderChain().toBuilder() + .setChainKey(chainKey).build(); + + this.sessionStructure = this.sessionStructure.toBuilder().setSenderChain(chain).build(); + } + + public boolean hasMessageKeys(ECPublicKey senderEphemeral, int counter) { + Pair chainAndIndex = getReceiverChain(senderEphemeral); + Chain chain = chainAndIndex.first(); + + if (chain == null) { + return false; + } + + List messageKeyList = chain.getMessageKeysList(); + + for (Chain.MessageKey messageKey : messageKeyList) { + if (messageKey.getIndex() == counter) { + return true; + } + } + + return false; + } + + public MessageKeys removeMessageKeys(ECPublicKey senderEphemeral, int counter) { + Pair chainAndIndex = getReceiverChain(senderEphemeral); + Chain chain = chainAndIndex.first(); + + if (chain == null) { + return null; + } + + List messageKeyList = new LinkedList<>(chain.getMessageKeysList()); + Iterator messageKeyIterator = messageKeyList.iterator(); + MessageKeys result = null; + + while (messageKeyIterator.hasNext()) { + Chain.MessageKey messageKey = messageKeyIterator.next(); + + if (messageKey.getIndex() == counter) { + result = new MessageKeys(new SecretKeySpec(messageKey.getCipherKey().toByteArray(), "AES"), + new SecretKeySpec(messageKey.getMacKey().toByteArray(), "HmacSHA256"), + new IvParameterSpec(messageKey.getIv().toByteArray()), + messageKey.getIndex()); + + messageKeyIterator.remove(); + break; + } + } + + Chain updatedChain = chain.toBuilder().clearMessageKeys() + .addAllMessageKeys(messageKeyList) + .build(); + + this.sessionStructure = this.sessionStructure.toBuilder() + .setReceiverChains(chainAndIndex.second(), updatedChain) + .build(); + + return result; + } + + public void setMessageKeys(ECPublicKey senderEphemeral, MessageKeys messageKeys) { + Pair chainAndIndex = getReceiverChain(senderEphemeral); + Chain chain = chainAndIndex.first(); + Chain.MessageKey messageKeyStructure = Chain.MessageKey.newBuilder() + .setCipherKey(ByteString.copyFrom(messageKeys.getCipherKey().getEncoded())) + .setMacKey(ByteString.copyFrom(messageKeys.getMacKey().getEncoded())) + .setIndex(messageKeys.getCounter()) + .setIv(ByteString.copyFrom(messageKeys.getIv().getIV())) + .build(); + + Chain updatedChain = chain.toBuilder() + .addMessageKeys(messageKeyStructure) + .build(); + + this.sessionStructure = this.sessionStructure.toBuilder() + .setReceiverChains(chainAndIndex.second(), updatedChain) + .build(); + } + + public void setReceiverChainKey(ECPublicKey senderEphemeral, ChainKey chainKey) { + Pair chainAndIndex = getReceiverChain(senderEphemeral); + Chain chain = chainAndIndex.first(); + + Chain.ChainKey chainKeyStructure = Chain.ChainKey.newBuilder() + .setKey(ByteString.copyFrom(chainKey.getKey())) + .setIndex(chainKey.getIndex()) + .build(); + + Chain updatedChain = chain.toBuilder().setChainKey(chainKeyStructure).build(); + + this.sessionStructure = this.sessionStructure.toBuilder() + .setReceiverChains(chainAndIndex.second(), updatedChain) + .build(); + } + + public void setPendingKeyExchange(int sequence, + ECKeyPair ourBaseKey, + ECKeyPair ourRatchetKey, + IdentityKeyPair ourIdentityKey) + { + PendingKeyExchange structure = + PendingKeyExchange.newBuilder() + .setSequence(sequence) + .setLocalBaseKey(ByteString.copyFrom(ourBaseKey.getPublicKey().serialize())) + .setLocalBaseKeyPrivate(ByteString.copyFrom(ourBaseKey.getPrivateKey().serialize())) + .setLocalRatchetKey(ByteString.copyFrom(ourRatchetKey.getPublicKey().serialize())) + .setLocalRatchetKeyPrivate(ByteString.copyFrom(ourRatchetKey.getPrivateKey().serialize())) + .setLocalIdentityKey(ByteString.copyFrom(ourIdentityKey.getPublicKey().serialize())) + .setLocalIdentityKeyPrivate(ByteString.copyFrom(ourIdentityKey.getPrivateKey().serialize())) + .build(); + + this.sessionStructure = this.sessionStructure.toBuilder() + .setPendingKeyExchange(structure) + .build(); + } + + public int getPendingKeyExchangeSequence() { + return sessionStructure.getPendingKeyExchange().getSequence(); + } + + public ECKeyPair getPendingKeyExchangeBaseKey() throws InvalidKeyException { + ECPublicKey publicKey = Curve.decodePoint(sessionStructure.getPendingKeyExchange() + .getLocalBaseKey().toByteArray(), 0); + + ECPrivateKey privateKey = Curve.decodePrivatePoint(sessionStructure.getPendingKeyExchange() + .getLocalBaseKeyPrivate() + .toByteArray()); + + return new ECKeyPair(publicKey, privateKey); + } + + public ECKeyPair getPendingKeyExchangeRatchetKey() throws InvalidKeyException { + ECPublicKey publicKey = Curve.decodePoint(sessionStructure.getPendingKeyExchange() + .getLocalRatchetKey().toByteArray(), 0); + + ECPrivateKey privateKey = Curve.decodePrivatePoint(sessionStructure.getPendingKeyExchange() + .getLocalRatchetKeyPrivate() + .toByteArray()); + + return new ECKeyPair(publicKey, privateKey); + } + + public IdentityKeyPair getPendingKeyExchangeIdentityKey() throws InvalidKeyException { + IdentityKey publicKey = new IdentityKey(sessionStructure.getPendingKeyExchange() + .getLocalIdentityKey().toByteArray(), 0); + + ECPrivateKey privateKey = Curve.decodePrivatePoint(sessionStructure.getPendingKeyExchange() + .getLocalIdentityKeyPrivate() + .toByteArray()); + + return new IdentityKeyPair(publicKey, privateKey); + } + + public boolean hasPendingKeyExchange() { + return sessionStructure.hasPendingKeyExchange(); + } + + public void setUnacknowledgedPreKeyMessage(Optional preKeyId, int signedPreKeyId, ECPublicKey baseKey) { + PendingPreKey.Builder pending = PendingPreKey.newBuilder() + .setSignedPreKeyId(signedPreKeyId) + .setBaseKey(ByteString.copyFrom(baseKey.serialize())); + + if (preKeyId.isPresent()) { + pending.setPreKeyId(preKeyId.get()); + } + + this.sessionStructure = this.sessionStructure.toBuilder() + .setPendingPreKey(pending.build()) + .build(); + } + + public boolean hasUnacknowledgedPreKeyMessage() { + return this.sessionStructure.hasPendingPreKey(); + } + + public UnacknowledgedPreKeyMessageItems getUnacknowledgedPreKeyMessageItems() { + try { + Optional preKeyId; + + if (sessionStructure.getPendingPreKey().hasPreKeyId()) { + preKeyId = Optional.of(sessionStructure.getPendingPreKey().getPreKeyId()); + } else { + preKeyId = Optional.absent(); + } + + return + new UnacknowledgedPreKeyMessageItems(preKeyId, + sessionStructure.getPendingPreKey().getSignedPreKeyId(), + Curve.decodePoint(sessionStructure.getPendingPreKey() + .getBaseKey() + .toByteArray(), 0)); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + public void clearUnacknowledgedPreKeyMessage() { + this.sessionStructure = this.sessionStructure.toBuilder() + .clearPendingPreKey() + .build(); + } + + public void setRemoteRegistrationId(int registrationId) { + this.sessionStructure = this.sessionStructure.toBuilder() + .setRemoteRegistrationId(registrationId) + .build(); + } + + public int getRemoteRegistrationId() { + return this.sessionStructure.getRemoteRegistrationId(); + } + + public void setLocalRegistrationId(int registrationId) { + this.sessionStructure = this.sessionStructure.toBuilder() + .setLocalRegistrationId(registrationId) + .build(); + } + + public int getLocalRegistrationId() { + return this.sessionStructure.getLocalRegistrationId(); + } + + public byte[] serialize() { + return sessionStructure.toByteArray(); + } + + public static class UnacknowledgedPreKeyMessageItems { + private final Optional preKeyId; + private final int signedPreKeyId; + private final ECPublicKey baseKey; + + public UnacknowledgedPreKeyMessageItems(Optional preKeyId, + int signedPreKeyId, + ECPublicKey baseKey) + { + this.preKeyId = preKeyId; + this.signedPreKeyId = signedPreKeyId; + this.baseKey = baseKey; + } + + + public Optional getPreKeyId() { + return preKeyId; + } + + public int getSignedPreKeyId() { + return signedPreKeyId; + } + + public ECPublicKey getBaseKey() { + return baseKey; + } + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/state/SessionStore.java b/src/main/java/org/whispersystems/libaxolotl/state/SessionStore.java new file mode 100644 index 000000000..c5ad00b0b --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/state/SessionStore.java @@ -0,0 +1,68 @@ +package org.whispersystems.libaxolotl.state; + +import java.util.List; + +/** + * The interface to the durable store of session state information + * for remote clients. + * + * @author Moxie Marlinspike + */ +public interface SessionStore { + + /** + * Returns a copy of the {@link SessionRecord} corresponding to the recipientId + deviceId tuple, + * or a new SessionRecord if one does not currently exist. + *

+ * It is important that implementations return a copy of the current durable information. The + * returned SessionRecord may be modified, but those changes should not have an effect on the + * durable session state (what is returned by subsequent calls to this method) without the + * store method being called here first. + * + * @param recipientId The recipientID of the remote client. + * @param deviceId The deviceID of the remote client. + * @return a copy of the SessionRecord corresponding to the recipientId + deviceId tuple, or + * a new SessionRecord if one does not currently exist. + */ + public SessionRecord loadSession(long recipientId, int deviceId); + + /** + * Returns all known devices with active sessions for a recipient + * + * @param recipientId the recipient ID. + * @return all known sub-devices with active sessions. + */ + public List getSubDeviceSessions(long recipientId); + + /** + * Commit to storage the {@link SessionRecord} for a given recipientId + deviceId tuple. + * @param recipientId the recipient ID of the remote client. + * @param deviceId the device ID of the remote client. + * @param record the current SessionRecord for the remote client. + */ + public void storeSession(long recipientId, int deviceId, SessionRecord record); + + /** + * Determine whether there is a committed {@link SessionRecord} for a recipientId + deviceId tuple. + * @param recipientId the recipient ID of the remote client. + * @param deviceId the device ID of the remote client. + * @return true if a {@link SessionRecord} exists, false otherwise. + */ + public boolean containsSession(long recipientId, int deviceId); + + /** + * Remove a {@link SessionRecord} for a recipientId + deviceId tuple. + * + * @param recipientId the recipient ID of the remote client. + * @param deviceId the device ID of the remote client. + */ + public void deleteSession(long recipientId, int deviceId); + + /** + * Remove the {@link SessionRecord}s corresponding to all devices of a recipientId. + * + * @param recipientId the recipient ID of the remote client. + */ + public void deleteAllSessions(long recipientId); + +} diff --git a/src/main/java/org/whispersystems/libaxolotl/state/SignedPreKeyRecord.java b/src/main/java/org/whispersystems/libaxolotl/state/SignedPreKeyRecord.java new file mode 100644 index 000000000..f11f5cf19 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/state/SignedPreKeyRecord.java @@ -0,0 +1,61 @@ +package org.whispersystems.libaxolotl.state; + +import com.google.protobuf.ByteString; + +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.ecc.ECPrivateKey; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; + +import java.io.IOException; + +import static org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure; + +public class SignedPreKeyRecord { + + private SignedPreKeyRecordStructure structure; + + public SignedPreKeyRecord(int id, long timestamp, ECKeyPair keyPair, byte[] signature) { + this.structure = SignedPreKeyRecordStructure.newBuilder() + .setId(id) + .setPublicKey(ByteString.copyFrom(keyPair.getPublicKey() + .serialize())) + .setPrivateKey(ByteString.copyFrom(keyPair.getPrivateKey() + .serialize())) + .setSignature(ByteString.copyFrom(signature)) + .setTimestamp(timestamp) + .build(); + } + + public SignedPreKeyRecord(byte[] serialized) throws IOException { + this.structure = SignedPreKeyRecordStructure.parseFrom(serialized); + } + + public int getId() { + return this.structure.getId(); + } + + public long getTimestamp() { + return this.structure.getTimestamp(); + } + + public ECKeyPair getKeyPair() { + try { + ECPublicKey publicKey = Curve.decodePoint(this.structure.getPublicKey().toByteArray(), 0); + ECPrivateKey privateKey = Curve.decodePrivatePoint(this.structure.getPrivateKey().toByteArray()); + + return new ECKeyPair(publicKey, privateKey); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + public byte[] getSignature() { + return this.structure.getSignature().toByteArray(); + } + + public byte[] serialize() { + return this.structure.toByteArray(); + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/state/SignedPreKeyStore.java b/src/main/java/org/whispersystems/libaxolotl/state/SignedPreKeyStore.java new file mode 100644 index 000000000..c828bf237 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/state/SignedPreKeyStore.java @@ -0,0 +1,47 @@ +package org.whispersystems.libaxolotl.state; + +import org.whispersystems.libaxolotl.InvalidKeyIdException; + +import java.util.List; + +public interface SignedPreKeyStore { + + + /** + * Load a local SignedPreKeyRecord. + * + * @param signedPreKeyId the ID of the local SignedPreKeyRecord. + * @return the corresponding SignedPreKeyRecord. + * @throws InvalidKeyIdException when there is no corresponding SignedPreKeyRecord. + */ + public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException; + + /** + * Load all local SignedPreKeyRecords. + * + * @return All stored SignedPreKeyRecords. + */ + public List loadSignedPreKeys(); + + /** + * Store a local SignedPreKeyRecord. + * + * @param signedPreKeyId the ID of the SignedPreKeyRecord to store. + * @param record the SignedPreKeyRecord. + */ + public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record); + + /** + * @param signedPreKeyId A SignedPreKeyRecord ID. + * @return true if the store has a record for the signedPreKeyId, otherwise false. + */ + public boolean containsSignedPreKey(int signedPreKeyId); + + /** + * Delete a SignedPreKeyRecord from local storage. + * + * @param signedPreKeyId The ID of the SignedPreKeyRecord to remove. + */ + public void removeSignedPreKey(int signedPreKeyId); + +} diff --git a/src/main/java/org/whispersystems/libaxolotl/state/StorageProtos.java b/src/main/java/org/whispersystems/libaxolotl/state/StorageProtos.java new file mode 100644 index 000000000..b3e3e0e66 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/state/StorageProtos.java @@ -0,0 +1,11779 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: LocalStorageProtocol.proto + +package org.whispersystems.libaxolotl.state; + +public final class StorageProtos { + private StorageProtos() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public interface SessionStructureOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 sessionVersion = 1; + /** + * optional uint32 sessionVersion = 1; + */ + boolean hasSessionVersion(); + /** + * optional uint32 sessionVersion = 1; + */ + int getSessionVersion(); + + // optional bytes localIdentityPublic = 2; + /** + * optional bytes localIdentityPublic = 2; + */ + boolean hasLocalIdentityPublic(); + /** + * optional bytes localIdentityPublic = 2; + */ + com.google.protobuf.ByteString getLocalIdentityPublic(); + + // optional bytes remoteIdentityPublic = 3; + /** + * optional bytes remoteIdentityPublic = 3; + */ + boolean hasRemoteIdentityPublic(); + /** + * optional bytes remoteIdentityPublic = 3; + */ + com.google.protobuf.ByteString getRemoteIdentityPublic(); + + // optional bytes rootKey = 4; + /** + * optional bytes rootKey = 4; + */ + boolean hasRootKey(); + /** + * optional bytes rootKey = 4; + */ + com.google.protobuf.ByteString getRootKey(); + + // optional uint32 previousCounter = 5; + /** + * optional uint32 previousCounter = 5; + */ + boolean hasPreviousCounter(); + /** + * optional uint32 previousCounter = 5; + */ + int getPreviousCounter(); + + // optional .textsecure.SessionStructure.Chain senderChain = 6; + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + boolean hasSenderChain(); + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain getSenderChain(); + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.ChainOrBuilder getSenderChainOrBuilder(); + + // repeated .textsecure.SessionStructure.Chain receiverChains = 7; + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + java.util.List + getReceiverChainsList(); + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain getReceiverChains(int index); + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + int getReceiverChainsCount(); + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + java.util.List + getReceiverChainsOrBuilderList(); + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.ChainOrBuilder getReceiverChainsOrBuilder( + int index); + + // optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + boolean hasPendingKeyExchange(); + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange getPendingKeyExchange(); + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchangeOrBuilder getPendingKeyExchangeOrBuilder(); + + // optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + boolean hasPendingPreKey(); + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey getPendingPreKey(); + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKeyOrBuilder getPendingPreKeyOrBuilder(); + + // optional uint32 remoteRegistrationId = 10; + /** + * optional uint32 remoteRegistrationId = 10; + */ + boolean hasRemoteRegistrationId(); + /** + * optional uint32 remoteRegistrationId = 10; + */ + int getRemoteRegistrationId(); + + // optional uint32 localRegistrationId = 11; + /** + * optional uint32 localRegistrationId = 11; + */ + boolean hasLocalRegistrationId(); + /** + * optional uint32 localRegistrationId = 11; + */ + int getLocalRegistrationId(); + + // optional bool needsRefresh = 12; + /** + * optional bool needsRefresh = 12; + */ + boolean hasNeedsRefresh(); + /** + * optional bool needsRefresh = 12; + */ + boolean getNeedsRefresh(); + + // optional bytes aliceBaseKey = 13; + /** + * optional bytes aliceBaseKey = 13; + */ + boolean hasAliceBaseKey(); + /** + * optional bytes aliceBaseKey = 13; + */ + com.google.protobuf.ByteString getAliceBaseKey(); + } + /** + * Protobuf type {@code textsecure.SessionStructure} + */ + public static final class SessionStructure extends + com.google.protobuf.GeneratedMessage + implements SessionStructureOrBuilder { + // Use SessionStructure.newBuilder() to construct. + private SessionStructure(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SessionStructure(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SessionStructure defaultInstance; + public static SessionStructure getDefaultInstance() { + return defaultInstance; + } + + public SessionStructure getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SessionStructure( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + sessionVersion_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + localIdentityPublic_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + remoteIdentityPublic_ = input.readBytes(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + rootKey_ = input.readBytes(); + break; + } + case 40: { + bitField0_ |= 0x00000010; + previousCounter_ = input.readUInt32(); + break; + } + case 50: { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.Builder subBuilder = null; + if (((bitField0_ & 0x00000020) == 0x00000020)) { + subBuilder = senderChain_.toBuilder(); + } + senderChain_ = input.readMessage(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(senderChain_); + senderChain_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000020; + break; + } + case 58: { + if (!((mutable_bitField0_ & 0x00000040) == 0x00000040)) { + receiverChains_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000040; + } + receiverChains_.add(input.readMessage(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.PARSER, extensionRegistry)); + break; + } + case 66: { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder subBuilder = null; + if (((bitField0_ & 0x00000040) == 0x00000040)) { + subBuilder = pendingKeyExchange_.toBuilder(); + } + pendingKeyExchange_ = input.readMessage(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(pendingKeyExchange_); + pendingKeyExchange_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000040; + break; + } + case 74: { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey.Builder subBuilder = null; + if (((bitField0_ & 0x00000080) == 0x00000080)) { + subBuilder = pendingPreKey_.toBuilder(); + } + pendingPreKey_ = input.readMessage(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(pendingPreKey_); + pendingPreKey_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000080; + break; + } + case 80: { + bitField0_ |= 0x00000100; + remoteRegistrationId_ = input.readUInt32(); + break; + } + case 88: { + bitField0_ |= 0x00000200; + localRegistrationId_ = input.readUInt32(); + break; + } + case 96: { + bitField0_ |= 0x00000400; + needsRefresh_ = input.readBool(); + break; + } + case 106: { + bitField0_ |= 0x00000800; + aliceBaseKey_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000040) == 0x00000040)) { + receiverChains_ = java.util.Collections.unmodifiableList(receiverChains_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.class, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SessionStructure parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SessionStructure(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public interface ChainOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes senderRatchetKey = 1; + /** + * optional bytes senderRatchetKey = 1; + */ + boolean hasSenderRatchetKey(); + /** + * optional bytes senderRatchetKey = 1; + */ + com.google.protobuf.ByteString getSenderRatchetKey(); + + // optional bytes senderRatchetKeyPrivate = 2; + /** + * optional bytes senderRatchetKeyPrivate = 2; + */ + boolean hasSenderRatchetKeyPrivate(); + /** + * optional bytes senderRatchetKeyPrivate = 2; + */ + com.google.protobuf.ByteString getSenderRatchetKeyPrivate(); + + // optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + boolean hasChainKey(); + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey getChainKey(); + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKeyOrBuilder getChainKeyOrBuilder(); + + // repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + java.util.List + getMessageKeysList(); + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey getMessageKeys(int index); + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + int getMessageKeysCount(); + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + java.util.List + getMessageKeysOrBuilderList(); + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKeyOrBuilder getMessageKeysOrBuilder( + int index); + } + /** + * Protobuf type {@code textsecure.SessionStructure.Chain} + */ + public static final class Chain extends + com.google.protobuf.GeneratedMessage + implements ChainOrBuilder { + // Use Chain.newBuilder() to construct. + private Chain(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Chain(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Chain defaultInstance; + public static Chain getDefaultInstance() { + return defaultInstance; + } + + public Chain getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Chain( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + senderRatchetKey_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + senderRatchetKeyPrivate_ = input.readBytes(); + break; + } + case 26: { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder subBuilder = null; + if (((bitField0_ & 0x00000004) == 0x00000004)) { + subBuilder = chainKey_.toBuilder(); + } + chainKey_ = input.readMessage(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(chainKey_); + chainKey_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000004; + break; + } + case 34: { + if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + messageKeys_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000008; + } + messageKeys_.add(input.readMessage(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey.PARSER, extensionRegistry)); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + messageKeys_ = java.util.Collections.unmodifiableList(messageKeys_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.class, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Chain parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Chain(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public interface ChainKeyOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 index = 1; + /** + * optional uint32 index = 1; + */ + boolean hasIndex(); + /** + * optional uint32 index = 1; + */ + int getIndex(); + + // optional bytes key = 2; + /** + * optional bytes key = 2; + */ + boolean hasKey(); + /** + * optional bytes key = 2; + */ + com.google.protobuf.ByteString getKey(); + } + /** + * Protobuf type {@code textsecure.SessionStructure.Chain.ChainKey} + */ + public static final class ChainKey extends + com.google.protobuf.GeneratedMessage + implements ChainKeyOrBuilder { + // Use ChainKey.newBuilder() to construct. + private ChainKey(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private ChainKey(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final ChainKey defaultInstance; + public static ChainKey getDefaultInstance() { + return defaultInstance; + } + + public ChainKey getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ChainKey( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + index_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + key_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_ChainKey_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_ChainKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey.class, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public ChainKey parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ChainKey(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 index = 1; + public static final int INDEX_FIELD_NUMBER = 1; + private int index_; + /** + * optional uint32 index = 1; + */ + public boolean hasIndex() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 index = 1; + */ + public int getIndex() { + return index_; + } + + // optional bytes key = 2; + public static final int KEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString key_; + /** + * optional bytes key = 2; + */ + public boolean hasKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes key = 2; + */ + public com.google.protobuf.ByteString getKey() { + return key_; + } + + private void initFields() { + index_ = 0; + key_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, index_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, key_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, index_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, key_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SessionStructure.Chain.ChainKey} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKeyOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_ChainKey_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_ChainKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey.class, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder.class); + } + + // Construct using org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + index_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + key_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_ChainKey_descriptor; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey getDefaultInstanceForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey.getDefaultInstance(); + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey build() { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey buildPartial() { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey result = new org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.index_ = index_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.key_ = key_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey) { + return mergeFrom((org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey other) { + if (other == org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey.getDefaultInstance()) return this; + if (other.hasIndex()) { + setIndex(other.getIndex()); + } + if (other.hasKey()) { + setKey(other.getKey()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 index = 1; + private int index_ ; + /** + * optional uint32 index = 1; + */ + public boolean hasIndex() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 index = 1; + */ + public int getIndex() { + return index_; + } + /** + * optional uint32 index = 1; + */ + public Builder setIndex(int value) { + bitField0_ |= 0x00000001; + index_ = value; + onChanged(); + return this; + } + /** + * optional uint32 index = 1; + */ + public Builder clearIndex() { + bitField0_ = (bitField0_ & ~0x00000001); + index_ = 0; + onChanged(); + return this; + } + + // optional bytes key = 2; + private com.google.protobuf.ByteString key_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes key = 2; + */ + public boolean hasKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes key = 2; + */ + public com.google.protobuf.ByteString getKey() { + return key_; + } + /** + * optional bytes key = 2; + */ + public Builder setKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + key_ = value; + onChanged(); + return this; + } + /** + * optional bytes key = 2; + */ + public Builder clearKey() { + bitField0_ = (bitField0_ & ~0x00000002); + key_ = getDefaultInstance().getKey(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SessionStructure.Chain.ChainKey) + } + + static { + defaultInstance = new ChainKey(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SessionStructure.Chain.ChainKey) + } + + public interface MessageKeyOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 index = 1; + /** + * optional uint32 index = 1; + */ + boolean hasIndex(); + /** + * optional uint32 index = 1; + */ + int getIndex(); + + // optional bytes cipherKey = 2; + /** + * optional bytes cipherKey = 2; + */ + boolean hasCipherKey(); + /** + * optional bytes cipherKey = 2; + */ + com.google.protobuf.ByteString getCipherKey(); + + // optional bytes macKey = 3; + /** + * optional bytes macKey = 3; + */ + boolean hasMacKey(); + /** + * optional bytes macKey = 3; + */ + com.google.protobuf.ByteString getMacKey(); + + // optional bytes iv = 4; + /** + * optional bytes iv = 4; + */ + boolean hasIv(); + /** + * optional bytes iv = 4; + */ + com.google.protobuf.ByteString getIv(); + } + /** + * Protobuf type {@code textsecure.SessionStructure.Chain.MessageKey} + */ + public static final class MessageKey extends + com.google.protobuf.GeneratedMessage + implements MessageKeyOrBuilder { + // Use MessageKey.newBuilder() to construct. + private MessageKey(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private MessageKey(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final MessageKey defaultInstance; + public static MessageKey getDefaultInstance() { + return defaultInstance; + } + + public MessageKey getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private MessageKey( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + index_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + cipherKey_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + macKey_ = input.readBytes(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + iv_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_MessageKey_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_MessageKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey.class, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public MessageKey parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new MessageKey(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 index = 1; + public static final int INDEX_FIELD_NUMBER = 1; + private int index_; + /** + * optional uint32 index = 1; + */ + public boolean hasIndex() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 index = 1; + */ + public int getIndex() { + return index_; + } + + // optional bytes cipherKey = 2; + public static final int CIPHERKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString cipherKey_; + /** + * optional bytes cipherKey = 2; + */ + public boolean hasCipherKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes cipherKey = 2; + */ + public com.google.protobuf.ByteString getCipherKey() { + return cipherKey_; + } + + // optional bytes macKey = 3; + public static final int MACKEY_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString macKey_; + /** + * optional bytes macKey = 3; + */ + public boolean hasMacKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes macKey = 3; + */ + public com.google.protobuf.ByteString getMacKey() { + return macKey_; + } + + // optional bytes iv = 4; + public static final int IV_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString iv_; + /** + * optional bytes iv = 4; + */ + public boolean hasIv() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes iv = 4; + */ + public com.google.protobuf.ByteString getIv() { + return iv_; + } + + private void initFields() { + index_ = 0; + cipherKey_ = com.google.protobuf.ByteString.EMPTY; + macKey_ = com.google.protobuf.ByteString.EMPTY; + iv_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, index_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, cipherKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, macKey_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, iv_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, index_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, cipherKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, macKey_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, iv_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SessionStructure.Chain.MessageKey} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKeyOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_MessageKey_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_MessageKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey.class, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder.class); + } + + // Construct using org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + index_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + cipherKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + macKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + iv_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_MessageKey_descriptor; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey getDefaultInstanceForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey.getDefaultInstance(); + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey build() { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey buildPartial() { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey result = new org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.index_ = index_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.cipherKey_ = cipherKey_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.macKey_ = macKey_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.iv_ = iv_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey) { + return mergeFrom((org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey other) { + if (other == org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey.getDefaultInstance()) return this; + if (other.hasIndex()) { + setIndex(other.getIndex()); + } + if (other.hasCipherKey()) { + setCipherKey(other.getCipherKey()); + } + if (other.hasMacKey()) { + setMacKey(other.getMacKey()); + } + if (other.hasIv()) { + setIv(other.getIv()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 index = 1; + private int index_ ; + /** + * optional uint32 index = 1; + */ + public boolean hasIndex() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 index = 1; + */ + public int getIndex() { + return index_; + } + /** + * optional uint32 index = 1; + */ + public Builder setIndex(int value) { + bitField0_ |= 0x00000001; + index_ = value; + onChanged(); + return this; + } + /** + * optional uint32 index = 1; + */ + public Builder clearIndex() { + bitField0_ = (bitField0_ & ~0x00000001); + index_ = 0; + onChanged(); + return this; + } + + // optional bytes cipherKey = 2; + private com.google.protobuf.ByteString cipherKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes cipherKey = 2; + */ + public boolean hasCipherKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes cipherKey = 2; + */ + public com.google.protobuf.ByteString getCipherKey() { + return cipherKey_; + } + /** + * optional bytes cipherKey = 2; + */ + public Builder setCipherKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + cipherKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes cipherKey = 2; + */ + public Builder clearCipherKey() { + bitField0_ = (bitField0_ & ~0x00000002); + cipherKey_ = getDefaultInstance().getCipherKey(); + onChanged(); + return this; + } + + // optional bytes macKey = 3; + private com.google.protobuf.ByteString macKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes macKey = 3; + */ + public boolean hasMacKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes macKey = 3; + */ + public com.google.protobuf.ByteString getMacKey() { + return macKey_; + } + /** + * optional bytes macKey = 3; + */ + public Builder setMacKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + macKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes macKey = 3; + */ + public Builder clearMacKey() { + bitField0_ = (bitField0_ & ~0x00000004); + macKey_ = getDefaultInstance().getMacKey(); + onChanged(); + return this; + } + + // optional bytes iv = 4; + private com.google.protobuf.ByteString iv_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes iv = 4; + */ + public boolean hasIv() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes iv = 4; + */ + public com.google.protobuf.ByteString getIv() { + return iv_; + } + /** + * optional bytes iv = 4; + */ + public Builder setIv(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + iv_ = value; + onChanged(); + return this; + } + /** + * optional bytes iv = 4; + */ + public Builder clearIv() { + bitField0_ = (bitField0_ & ~0x00000008); + iv_ = getDefaultInstance().getIv(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SessionStructure.Chain.MessageKey) + } + + static { + defaultInstance = new MessageKey(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SessionStructure.Chain.MessageKey) + } + + private int bitField0_; + // optional bytes senderRatchetKey = 1; + public static final int SENDERRATCHETKEY_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString senderRatchetKey_; + /** + * optional bytes senderRatchetKey = 1; + */ + public boolean hasSenderRatchetKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes senderRatchetKey = 1; + */ + public com.google.protobuf.ByteString getSenderRatchetKey() { + return senderRatchetKey_; + } + + // optional bytes senderRatchetKeyPrivate = 2; + public static final int SENDERRATCHETKEYPRIVATE_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString senderRatchetKeyPrivate_; + /** + * optional bytes senderRatchetKeyPrivate = 2; + */ + public boolean hasSenderRatchetKeyPrivate() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes senderRatchetKeyPrivate = 2; + */ + public com.google.protobuf.ByteString getSenderRatchetKeyPrivate() { + return senderRatchetKeyPrivate_; + } + + // optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + public static final int CHAINKEY_FIELD_NUMBER = 3; + private org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey chainKey_; + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public boolean hasChainKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey getChainKey() { + return chainKey_; + } + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKeyOrBuilder getChainKeyOrBuilder() { + return chainKey_; + } + + // repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + public static final int MESSAGEKEYS_FIELD_NUMBER = 4; + private java.util.List messageKeys_; + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public java.util.List getMessageKeysList() { + return messageKeys_; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public java.util.List + getMessageKeysOrBuilderList() { + return messageKeys_; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public int getMessageKeysCount() { + return messageKeys_.size(); + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey getMessageKeys(int index) { + return messageKeys_.get(index); + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKeyOrBuilder getMessageKeysOrBuilder( + int index) { + return messageKeys_.get(index); + } + + private void initFields() { + senderRatchetKey_ = com.google.protobuf.ByteString.EMPTY; + senderRatchetKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + chainKey_ = org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey.getDefaultInstance(); + messageKeys_ = java.util.Collections.emptyList(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, senderRatchetKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, senderRatchetKeyPrivate_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeMessage(3, chainKey_); + } + for (int i = 0; i < messageKeys_.size(); i++) { + output.writeMessage(4, messageKeys_.get(i)); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, senderRatchetKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, senderRatchetKeyPrivate_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, chainKey_); + } + for (int i = 0; i < messageKeys_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, messageKeys_.get(i)); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SessionStructure.Chain} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.ChainOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.class, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.Builder.class); + } + + // Construct using org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getChainKeyFieldBuilder(); + getMessageKeysFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + senderRatchetKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + senderRatchetKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + if (chainKeyBuilder_ == null) { + chainKey_ = org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey.getDefaultInstance(); + } else { + chainKeyBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + if (messageKeysBuilder_ == null) { + messageKeys_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + } else { + messageKeysBuilder_.clear(); + } + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_Chain_descriptor; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain getDefaultInstanceForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.getDefaultInstance(); + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain build() { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain buildPartial() { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain result = new org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.senderRatchetKey_ = senderRatchetKey_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.senderRatchetKeyPrivate_ = senderRatchetKeyPrivate_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + if (chainKeyBuilder_ == null) { + result.chainKey_ = chainKey_; + } else { + result.chainKey_ = chainKeyBuilder_.build(); + } + if (messageKeysBuilder_ == null) { + if (((bitField0_ & 0x00000008) == 0x00000008)) { + messageKeys_ = java.util.Collections.unmodifiableList(messageKeys_); + bitField0_ = (bitField0_ & ~0x00000008); + } + result.messageKeys_ = messageKeys_; + } else { + result.messageKeys_ = messageKeysBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain) { + return mergeFrom((org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain other) { + if (other == org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.getDefaultInstance()) return this; + if (other.hasSenderRatchetKey()) { + setSenderRatchetKey(other.getSenderRatchetKey()); + } + if (other.hasSenderRatchetKeyPrivate()) { + setSenderRatchetKeyPrivate(other.getSenderRatchetKeyPrivate()); + } + if (other.hasChainKey()) { + mergeChainKey(other.getChainKey()); + } + if (messageKeysBuilder_ == null) { + if (!other.messageKeys_.isEmpty()) { + if (messageKeys_.isEmpty()) { + messageKeys_ = other.messageKeys_; + bitField0_ = (bitField0_ & ~0x00000008); + } else { + ensureMessageKeysIsMutable(); + messageKeys_.addAll(other.messageKeys_); + } + onChanged(); + } + } else { + if (!other.messageKeys_.isEmpty()) { + if (messageKeysBuilder_.isEmpty()) { + messageKeysBuilder_.dispose(); + messageKeysBuilder_ = null; + messageKeys_ = other.messageKeys_; + bitField0_ = (bitField0_ & ~0x00000008); + messageKeysBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getMessageKeysFieldBuilder() : null; + } else { + messageKeysBuilder_.addAllMessages(other.messageKeys_); + } + } + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes senderRatchetKey = 1; + private com.google.protobuf.ByteString senderRatchetKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes senderRatchetKey = 1; + */ + public boolean hasSenderRatchetKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes senderRatchetKey = 1; + */ + public com.google.protobuf.ByteString getSenderRatchetKey() { + return senderRatchetKey_; + } + /** + * optional bytes senderRatchetKey = 1; + */ + public Builder setSenderRatchetKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + senderRatchetKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes senderRatchetKey = 1; + */ + public Builder clearSenderRatchetKey() { + bitField0_ = (bitField0_ & ~0x00000001); + senderRatchetKey_ = getDefaultInstance().getSenderRatchetKey(); + onChanged(); + return this; + } + + // optional bytes senderRatchetKeyPrivate = 2; + private com.google.protobuf.ByteString senderRatchetKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes senderRatchetKeyPrivate = 2; + */ + public boolean hasSenderRatchetKeyPrivate() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes senderRatchetKeyPrivate = 2; + */ + public com.google.protobuf.ByteString getSenderRatchetKeyPrivate() { + return senderRatchetKeyPrivate_; + } + /** + * optional bytes senderRatchetKeyPrivate = 2; + */ + public Builder setSenderRatchetKeyPrivate(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + senderRatchetKeyPrivate_ = value; + onChanged(); + return this; + } + /** + * optional bytes senderRatchetKeyPrivate = 2; + */ + public Builder clearSenderRatchetKeyPrivate() { + bitField0_ = (bitField0_ & ~0x00000002); + senderRatchetKeyPrivate_ = getDefaultInstance().getSenderRatchetKeyPrivate(); + onChanged(); + return this; + } + + // optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + private org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey chainKey_ = org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKeyOrBuilder> chainKeyBuilder_; + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public boolean hasChainKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey getChainKey() { + if (chainKeyBuilder_ == null) { + return chainKey_; + } else { + return chainKeyBuilder_.getMessage(); + } + } + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public Builder setChainKey(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey value) { + if (chainKeyBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + chainKey_ = value; + onChanged(); + } else { + chainKeyBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public Builder setChainKey( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder builderForValue) { + if (chainKeyBuilder_ == null) { + chainKey_ = builderForValue.build(); + onChanged(); + } else { + chainKeyBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public Builder mergeChainKey(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey value) { + if (chainKeyBuilder_ == null) { + if (((bitField0_ & 0x00000004) == 0x00000004) && + chainKey_ != org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey.getDefaultInstance()) { + chainKey_ = + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey.newBuilder(chainKey_).mergeFrom(value).buildPartial(); + } else { + chainKey_ = value; + } + onChanged(); + } else { + chainKeyBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public Builder clearChainKey() { + if (chainKeyBuilder_ == null) { + chainKey_ = org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey.getDefaultInstance(); + onChanged(); + } else { + chainKeyBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder getChainKeyBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return getChainKeyFieldBuilder().getBuilder(); + } + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKeyOrBuilder getChainKeyOrBuilder() { + if (chainKeyBuilder_ != null) { + return chainKeyBuilder_.getMessageOrBuilder(); + } else { + return chainKey_; + } + } + /** + * optional .textsecure.SessionStructure.Chain.ChainKey chainKey = 3; + */ + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKeyOrBuilder> + getChainKeyFieldBuilder() { + if (chainKeyBuilder_ == null) { + chainKeyBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKey.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.ChainKeyOrBuilder>( + chainKey_, + getParentForChildren(), + isClean()); + chainKey_ = null; + } + return chainKeyBuilder_; + } + + // repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + private java.util.List messageKeys_ = + java.util.Collections.emptyList(); + private void ensureMessageKeysIsMutable() { + if (!((bitField0_ & 0x00000008) == 0x00000008)) { + messageKeys_ = new java.util.ArrayList(messageKeys_); + bitField0_ |= 0x00000008; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKeyOrBuilder> messageKeysBuilder_; + + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public java.util.List getMessageKeysList() { + if (messageKeysBuilder_ == null) { + return java.util.Collections.unmodifiableList(messageKeys_); + } else { + return messageKeysBuilder_.getMessageList(); + } + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public int getMessageKeysCount() { + if (messageKeysBuilder_ == null) { + return messageKeys_.size(); + } else { + return messageKeysBuilder_.getCount(); + } + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey getMessageKeys(int index) { + if (messageKeysBuilder_ == null) { + return messageKeys_.get(index); + } else { + return messageKeysBuilder_.getMessage(index); + } + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public Builder setMessageKeys( + int index, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey value) { + if (messageKeysBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureMessageKeysIsMutable(); + messageKeys_.set(index, value); + onChanged(); + } else { + messageKeysBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public Builder setMessageKeys( + int index, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder builderForValue) { + if (messageKeysBuilder_ == null) { + ensureMessageKeysIsMutable(); + messageKeys_.set(index, builderForValue.build()); + onChanged(); + } else { + messageKeysBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public Builder addMessageKeys(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey value) { + if (messageKeysBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureMessageKeysIsMutable(); + messageKeys_.add(value); + onChanged(); + } else { + messageKeysBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public Builder addMessageKeys( + int index, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey value) { + if (messageKeysBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureMessageKeysIsMutable(); + messageKeys_.add(index, value); + onChanged(); + } else { + messageKeysBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public Builder addMessageKeys( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder builderForValue) { + if (messageKeysBuilder_ == null) { + ensureMessageKeysIsMutable(); + messageKeys_.add(builderForValue.build()); + onChanged(); + } else { + messageKeysBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public Builder addMessageKeys( + int index, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder builderForValue) { + if (messageKeysBuilder_ == null) { + ensureMessageKeysIsMutable(); + messageKeys_.add(index, builderForValue.build()); + onChanged(); + } else { + messageKeysBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public Builder addAllMessageKeys( + java.lang.Iterable values) { + if (messageKeysBuilder_ == null) { + ensureMessageKeysIsMutable(); + super.addAll(values, messageKeys_); + onChanged(); + } else { + messageKeysBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public Builder clearMessageKeys() { + if (messageKeysBuilder_ == null) { + messageKeys_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); + } else { + messageKeysBuilder_.clear(); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public Builder removeMessageKeys(int index) { + if (messageKeysBuilder_ == null) { + ensureMessageKeysIsMutable(); + messageKeys_.remove(index); + onChanged(); + } else { + messageKeysBuilder_.remove(index); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder getMessageKeysBuilder( + int index) { + return getMessageKeysFieldBuilder().getBuilder(index); + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKeyOrBuilder getMessageKeysOrBuilder( + int index) { + if (messageKeysBuilder_ == null) { + return messageKeys_.get(index); } else { + return messageKeysBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public java.util.List + getMessageKeysOrBuilderList() { + if (messageKeysBuilder_ != null) { + return messageKeysBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(messageKeys_); + } + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder addMessageKeysBuilder() { + return getMessageKeysFieldBuilder().addBuilder( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey.getDefaultInstance()); + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder addMessageKeysBuilder( + int index) { + return getMessageKeysFieldBuilder().addBuilder( + index, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey.getDefaultInstance()); + } + /** + * repeated .textsecure.SessionStructure.Chain.MessageKey messageKeys = 4; + */ + public java.util.List + getMessageKeysBuilderList() { + return getMessageKeysFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKeyOrBuilder> + getMessageKeysFieldBuilder() { + if (messageKeysBuilder_ == null) { + messageKeysBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKey.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.MessageKeyOrBuilder>( + messageKeys_, + ((bitField0_ & 0x00000008) == 0x00000008), + getParentForChildren(), + isClean()); + messageKeys_ = null; + } + return messageKeysBuilder_; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SessionStructure.Chain) + } + + static { + defaultInstance = new Chain(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SessionStructure.Chain) + } + + public interface PendingKeyExchangeOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 sequence = 1; + /** + * optional uint32 sequence = 1; + */ + boolean hasSequence(); + /** + * optional uint32 sequence = 1; + */ + int getSequence(); + + // optional bytes localBaseKey = 2; + /** + * optional bytes localBaseKey = 2; + */ + boolean hasLocalBaseKey(); + /** + * optional bytes localBaseKey = 2; + */ + com.google.protobuf.ByteString getLocalBaseKey(); + + // optional bytes localBaseKeyPrivate = 3; + /** + * optional bytes localBaseKeyPrivate = 3; + */ + boolean hasLocalBaseKeyPrivate(); + /** + * optional bytes localBaseKeyPrivate = 3; + */ + com.google.protobuf.ByteString getLocalBaseKeyPrivate(); + + // optional bytes localRatchetKey = 4; + /** + * optional bytes localRatchetKey = 4; + */ + boolean hasLocalRatchetKey(); + /** + * optional bytes localRatchetKey = 4; + */ + com.google.protobuf.ByteString getLocalRatchetKey(); + + // optional bytes localRatchetKeyPrivate = 5; + /** + * optional bytes localRatchetKeyPrivate = 5; + */ + boolean hasLocalRatchetKeyPrivate(); + /** + * optional bytes localRatchetKeyPrivate = 5; + */ + com.google.protobuf.ByteString getLocalRatchetKeyPrivate(); + + // optional bytes localIdentityKey = 7; + /** + * optional bytes localIdentityKey = 7; + */ + boolean hasLocalIdentityKey(); + /** + * optional bytes localIdentityKey = 7; + */ + com.google.protobuf.ByteString getLocalIdentityKey(); + + // optional bytes localIdentityKeyPrivate = 8; + /** + * optional bytes localIdentityKeyPrivate = 8; + */ + boolean hasLocalIdentityKeyPrivate(); + /** + * optional bytes localIdentityKeyPrivate = 8; + */ + com.google.protobuf.ByteString getLocalIdentityKeyPrivate(); + } + /** + * Protobuf type {@code textsecure.SessionStructure.PendingKeyExchange} + */ + public static final class PendingKeyExchange extends + com.google.protobuf.GeneratedMessage + implements PendingKeyExchangeOrBuilder { + // Use PendingKeyExchange.newBuilder() to construct. + private PendingKeyExchange(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private PendingKeyExchange(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final PendingKeyExchange defaultInstance; + public static PendingKeyExchange getDefaultInstance() { + return defaultInstance; + } + + public PendingKeyExchange getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private PendingKeyExchange( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + sequence_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + localBaseKey_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + localBaseKeyPrivate_ = input.readBytes(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + localRatchetKey_ = input.readBytes(); + break; + } + case 42: { + bitField0_ |= 0x00000010; + localRatchetKeyPrivate_ = input.readBytes(); + break; + } + case 58: { + bitField0_ |= 0x00000020; + localIdentityKey_ = input.readBytes(); + break; + } + case 66: { + bitField0_ |= 0x00000040; + localIdentityKeyPrivate_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingKeyExchange_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingKeyExchange_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange.class, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public PendingKeyExchange parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new PendingKeyExchange(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 sequence = 1; + public static final int SEQUENCE_FIELD_NUMBER = 1; + private int sequence_; + /** + * optional uint32 sequence = 1; + */ + public boolean hasSequence() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 sequence = 1; + */ + public int getSequence() { + return sequence_; + } + + // optional bytes localBaseKey = 2; + public static final int LOCALBASEKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString localBaseKey_; + /** + * optional bytes localBaseKey = 2; + */ + public boolean hasLocalBaseKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes localBaseKey = 2; + */ + public com.google.protobuf.ByteString getLocalBaseKey() { + return localBaseKey_; + } + + // optional bytes localBaseKeyPrivate = 3; + public static final int LOCALBASEKEYPRIVATE_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString localBaseKeyPrivate_; + /** + * optional bytes localBaseKeyPrivate = 3; + */ + public boolean hasLocalBaseKeyPrivate() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes localBaseKeyPrivate = 3; + */ + public com.google.protobuf.ByteString getLocalBaseKeyPrivate() { + return localBaseKeyPrivate_; + } + + // optional bytes localRatchetKey = 4; + public static final int LOCALRATCHETKEY_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString localRatchetKey_; + /** + * optional bytes localRatchetKey = 4; + */ + public boolean hasLocalRatchetKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes localRatchetKey = 4; + */ + public com.google.protobuf.ByteString getLocalRatchetKey() { + return localRatchetKey_; + } + + // optional bytes localRatchetKeyPrivate = 5; + public static final int LOCALRATCHETKEYPRIVATE_FIELD_NUMBER = 5; + private com.google.protobuf.ByteString localRatchetKeyPrivate_; + /** + * optional bytes localRatchetKeyPrivate = 5; + */ + public boolean hasLocalRatchetKeyPrivate() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes localRatchetKeyPrivate = 5; + */ + public com.google.protobuf.ByteString getLocalRatchetKeyPrivate() { + return localRatchetKeyPrivate_; + } + + // optional bytes localIdentityKey = 7; + public static final int LOCALIDENTITYKEY_FIELD_NUMBER = 7; + private com.google.protobuf.ByteString localIdentityKey_; + /** + * optional bytes localIdentityKey = 7; + */ + public boolean hasLocalIdentityKey() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes localIdentityKey = 7; + */ + public com.google.protobuf.ByteString getLocalIdentityKey() { + return localIdentityKey_; + } + + // optional bytes localIdentityKeyPrivate = 8; + public static final int LOCALIDENTITYKEYPRIVATE_FIELD_NUMBER = 8; + private com.google.protobuf.ByteString localIdentityKeyPrivate_; + /** + * optional bytes localIdentityKeyPrivate = 8; + */ + public boolean hasLocalIdentityKeyPrivate() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional bytes localIdentityKeyPrivate = 8; + */ + public com.google.protobuf.ByteString getLocalIdentityKeyPrivate() { + return localIdentityKeyPrivate_; + } + + private void initFields() { + sequence_ = 0; + localBaseKey_ = com.google.protobuf.ByteString.EMPTY; + localBaseKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + localRatchetKey_ = com.google.protobuf.ByteString.EMPTY; + localRatchetKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + localIdentityKey_ = com.google.protobuf.ByteString.EMPTY; + localIdentityKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, sequence_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, localBaseKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, localBaseKeyPrivate_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, localRatchetKey_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeBytes(5, localRatchetKeyPrivate_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeBytes(7, localIdentityKey_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + output.writeBytes(8, localIdentityKeyPrivate_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, sequence_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, localBaseKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, localBaseKeyPrivate_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, localRatchetKey_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(5, localRatchetKeyPrivate_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(7, localIdentityKey_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(8, localIdentityKeyPrivate_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SessionStructure.PendingKeyExchange} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchangeOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingKeyExchange_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingKeyExchange_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange.class, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder.class); + } + + // Construct using org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + sequence_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + localBaseKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + localBaseKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + localRatchetKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + localRatchetKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000010); + localIdentityKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000020); + localIdentityKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000040); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingKeyExchange_descriptor; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange getDefaultInstanceForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange.getDefaultInstance(); + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange build() { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange buildPartial() { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange result = new org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.sequence_ = sequence_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.localBaseKey_ = localBaseKey_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.localBaseKeyPrivate_ = localBaseKeyPrivate_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.localRatchetKey_ = localRatchetKey_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + result.localRatchetKeyPrivate_ = localRatchetKeyPrivate_; + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000020; + } + result.localIdentityKey_ = localIdentityKey_; + if (((from_bitField0_ & 0x00000040) == 0x00000040)) { + to_bitField0_ |= 0x00000040; + } + result.localIdentityKeyPrivate_ = localIdentityKeyPrivate_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange) { + return mergeFrom((org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange other) { + if (other == org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange.getDefaultInstance()) return this; + if (other.hasSequence()) { + setSequence(other.getSequence()); + } + if (other.hasLocalBaseKey()) { + setLocalBaseKey(other.getLocalBaseKey()); + } + if (other.hasLocalBaseKeyPrivate()) { + setLocalBaseKeyPrivate(other.getLocalBaseKeyPrivate()); + } + if (other.hasLocalRatchetKey()) { + setLocalRatchetKey(other.getLocalRatchetKey()); + } + if (other.hasLocalRatchetKeyPrivate()) { + setLocalRatchetKeyPrivate(other.getLocalRatchetKeyPrivate()); + } + if (other.hasLocalIdentityKey()) { + setLocalIdentityKey(other.getLocalIdentityKey()); + } + if (other.hasLocalIdentityKeyPrivate()) { + setLocalIdentityKeyPrivate(other.getLocalIdentityKeyPrivate()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 sequence = 1; + private int sequence_ ; + /** + * optional uint32 sequence = 1; + */ + public boolean hasSequence() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 sequence = 1; + */ + public int getSequence() { + return sequence_; + } + /** + * optional uint32 sequence = 1; + */ + public Builder setSequence(int value) { + bitField0_ |= 0x00000001; + sequence_ = value; + onChanged(); + return this; + } + /** + * optional uint32 sequence = 1; + */ + public Builder clearSequence() { + bitField0_ = (bitField0_ & ~0x00000001); + sequence_ = 0; + onChanged(); + return this; + } + + // optional bytes localBaseKey = 2; + private com.google.protobuf.ByteString localBaseKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes localBaseKey = 2; + */ + public boolean hasLocalBaseKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes localBaseKey = 2; + */ + public com.google.protobuf.ByteString getLocalBaseKey() { + return localBaseKey_; + } + /** + * optional bytes localBaseKey = 2; + */ + public Builder setLocalBaseKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + localBaseKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes localBaseKey = 2; + */ + public Builder clearLocalBaseKey() { + bitField0_ = (bitField0_ & ~0x00000002); + localBaseKey_ = getDefaultInstance().getLocalBaseKey(); + onChanged(); + return this; + } + + // optional bytes localBaseKeyPrivate = 3; + private com.google.protobuf.ByteString localBaseKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes localBaseKeyPrivate = 3; + */ + public boolean hasLocalBaseKeyPrivate() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes localBaseKeyPrivate = 3; + */ + public com.google.protobuf.ByteString getLocalBaseKeyPrivate() { + return localBaseKeyPrivate_; + } + /** + * optional bytes localBaseKeyPrivate = 3; + */ + public Builder setLocalBaseKeyPrivate(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + localBaseKeyPrivate_ = value; + onChanged(); + return this; + } + /** + * optional bytes localBaseKeyPrivate = 3; + */ + public Builder clearLocalBaseKeyPrivate() { + bitField0_ = (bitField0_ & ~0x00000004); + localBaseKeyPrivate_ = getDefaultInstance().getLocalBaseKeyPrivate(); + onChanged(); + return this; + } + + // optional bytes localRatchetKey = 4; + private com.google.protobuf.ByteString localRatchetKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes localRatchetKey = 4; + */ + public boolean hasLocalRatchetKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes localRatchetKey = 4; + */ + public com.google.protobuf.ByteString getLocalRatchetKey() { + return localRatchetKey_; + } + /** + * optional bytes localRatchetKey = 4; + */ + public Builder setLocalRatchetKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + localRatchetKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes localRatchetKey = 4; + */ + public Builder clearLocalRatchetKey() { + bitField0_ = (bitField0_ & ~0x00000008); + localRatchetKey_ = getDefaultInstance().getLocalRatchetKey(); + onChanged(); + return this; + } + + // optional bytes localRatchetKeyPrivate = 5; + private com.google.protobuf.ByteString localRatchetKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes localRatchetKeyPrivate = 5; + */ + public boolean hasLocalRatchetKeyPrivate() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes localRatchetKeyPrivate = 5; + */ + public com.google.protobuf.ByteString getLocalRatchetKeyPrivate() { + return localRatchetKeyPrivate_; + } + /** + * optional bytes localRatchetKeyPrivate = 5; + */ + public Builder setLocalRatchetKeyPrivate(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000010; + localRatchetKeyPrivate_ = value; + onChanged(); + return this; + } + /** + * optional bytes localRatchetKeyPrivate = 5; + */ + public Builder clearLocalRatchetKeyPrivate() { + bitField0_ = (bitField0_ & ~0x00000010); + localRatchetKeyPrivate_ = getDefaultInstance().getLocalRatchetKeyPrivate(); + onChanged(); + return this; + } + + // optional bytes localIdentityKey = 7; + private com.google.protobuf.ByteString localIdentityKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes localIdentityKey = 7; + */ + public boolean hasLocalIdentityKey() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes localIdentityKey = 7; + */ + public com.google.protobuf.ByteString getLocalIdentityKey() { + return localIdentityKey_; + } + /** + * optional bytes localIdentityKey = 7; + */ + public Builder setLocalIdentityKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000020; + localIdentityKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes localIdentityKey = 7; + */ + public Builder clearLocalIdentityKey() { + bitField0_ = (bitField0_ & ~0x00000020); + localIdentityKey_ = getDefaultInstance().getLocalIdentityKey(); + onChanged(); + return this; + } + + // optional bytes localIdentityKeyPrivate = 8; + private com.google.protobuf.ByteString localIdentityKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes localIdentityKeyPrivate = 8; + */ + public boolean hasLocalIdentityKeyPrivate() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional bytes localIdentityKeyPrivate = 8; + */ + public com.google.protobuf.ByteString getLocalIdentityKeyPrivate() { + return localIdentityKeyPrivate_; + } + /** + * optional bytes localIdentityKeyPrivate = 8; + */ + public Builder setLocalIdentityKeyPrivate(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000040; + localIdentityKeyPrivate_ = value; + onChanged(); + return this; + } + /** + * optional bytes localIdentityKeyPrivate = 8; + */ + public Builder clearLocalIdentityKeyPrivate() { + bitField0_ = (bitField0_ & ~0x00000040); + localIdentityKeyPrivate_ = getDefaultInstance().getLocalIdentityKeyPrivate(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SessionStructure.PendingKeyExchange) + } + + static { + defaultInstance = new PendingKeyExchange(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SessionStructure.PendingKeyExchange) + } + + public interface PendingPreKeyOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 preKeyId = 1; + /** + * optional uint32 preKeyId = 1; + */ + boolean hasPreKeyId(); + /** + * optional uint32 preKeyId = 1; + */ + int getPreKeyId(); + + // optional int32 signedPreKeyId = 3; + /** + * optional int32 signedPreKeyId = 3; + */ + boolean hasSignedPreKeyId(); + /** + * optional int32 signedPreKeyId = 3; + */ + int getSignedPreKeyId(); + + // optional bytes baseKey = 2; + /** + * optional bytes baseKey = 2; + */ + boolean hasBaseKey(); + /** + * optional bytes baseKey = 2; + */ + com.google.protobuf.ByteString getBaseKey(); + } + /** + * Protobuf type {@code textsecure.SessionStructure.PendingPreKey} + */ + public static final class PendingPreKey extends + com.google.protobuf.GeneratedMessage + implements PendingPreKeyOrBuilder { + // Use PendingPreKey.newBuilder() to construct. + private PendingPreKey(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private PendingPreKey(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final PendingPreKey defaultInstance; + public static PendingPreKey getDefaultInstance() { + return defaultInstance; + } + + public PendingPreKey getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private PendingPreKey( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + preKeyId_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000004; + baseKey_ = input.readBytes(); + break; + } + case 24: { + bitField0_ |= 0x00000002; + signedPreKeyId_ = input.readInt32(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingPreKey_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingPreKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey.class, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public PendingPreKey parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new PendingPreKey(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 preKeyId = 1; + public static final int PREKEYID_FIELD_NUMBER = 1; + private int preKeyId_; + /** + * optional uint32 preKeyId = 1; + */ + public boolean hasPreKeyId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 preKeyId = 1; + */ + public int getPreKeyId() { + return preKeyId_; + } + + // optional int32 signedPreKeyId = 3; + public static final int SIGNEDPREKEYID_FIELD_NUMBER = 3; + private int signedPreKeyId_; + /** + * optional int32 signedPreKeyId = 3; + */ + public boolean hasSignedPreKeyId() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional int32 signedPreKeyId = 3; + */ + public int getSignedPreKeyId() { + return signedPreKeyId_; + } + + // optional bytes baseKey = 2; + public static final int BASEKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString baseKey_; + /** + * optional bytes baseKey = 2; + */ + public boolean hasBaseKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes baseKey = 2; + */ + public com.google.protobuf.ByteString getBaseKey() { + return baseKey_; + } + + private void initFields() { + preKeyId_ = 0; + signedPreKeyId_ = 0; + baseKey_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, preKeyId_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(2, baseKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeInt32(3, signedPreKeyId_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, preKeyId_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, baseKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(3, signedPreKeyId_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SessionStructure.PendingPreKey} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKeyOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingPreKey_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingPreKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey.class, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey.Builder.class); + } + + // Construct using org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + preKeyId_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + signedPreKeyId_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + baseKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_PendingPreKey_descriptor; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey getDefaultInstanceForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance(); + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey build() { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey buildPartial() { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey result = new org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.preKeyId_ = preKeyId_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.signedPreKeyId_ = signedPreKeyId_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.baseKey_ = baseKey_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey) { + return mergeFrom((org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey other) { + if (other == org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance()) return this; + if (other.hasPreKeyId()) { + setPreKeyId(other.getPreKeyId()); + } + if (other.hasSignedPreKeyId()) { + setSignedPreKeyId(other.getSignedPreKeyId()); + } + if (other.hasBaseKey()) { + setBaseKey(other.getBaseKey()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 preKeyId = 1; + private int preKeyId_ ; + /** + * optional uint32 preKeyId = 1; + */ + public boolean hasPreKeyId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 preKeyId = 1; + */ + public int getPreKeyId() { + return preKeyId_; + } + /** + * optional uint32 preKeyId = 1; + */ + public Builder setPreKeyId(int value) { + bitField0_ |= 0x00000001; + preKeyId_ = value; + onChanged(); + return this; + } + /** + * optional uint32 preKeyId = 1; + */ + public Builder clearPreKeyId() { + bitField0_ = (bitField0_ & ~0x00000001); + preKeyId_ = 0; + onChanged(); + return this; + } + + // optional int32 signedPreKeyId = 3; + private int signedPreKeyId_ ; + /** + * optional int32 signedPreKeyId = 3; + */ + public boolean hasSignedPreKeyId() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional int32 signedPreKeyId = 3; + */ + public int getSignedPreKeyId() { + return signedPreKeyId_; + } + /** + * optional int32 signedPreKeyId = 3; + */ + public Builder setSignedPreKeyId(int value) { + bitField0_ |= 0x00000002; + signedPreKeyId_ = value; + onChanged(); + return this; + } + /** + * optional int32 signedPreKeyId = 3; + */ + public Builder clearSignedPreKeyId() { + bitField0_ = (bitField0_ & ~0x00000002); + signedPreKeyId_ = 0; + onChanged(); + return this; + } + + // optional bytes baseKey = 2; + private com.google.protobuf.ByteString baseKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes baseKey = 2; + */ + public boolean hasBaseKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes baseKey = 2; + */ + public com.google.protobuf.ByteString getBaseKey() { + return baseKey_; + } + /** + * optional bytes baseKey = 2; + */ + public Builder setBaseKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + baseKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes baseKey = 2; + */ + public Builder clearBaseKey() { + bitField0_ = (bitField0_ & ~0x00000004); + baseKey_ = getDefaultInstance().getBaseKey(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SessionStructure.PendingPreKey) + } + + static { + defaultInstance = new PendingPreKey(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SessionStructure.PendingPreKey) + } + + private int bitField0_; + // optional uint32 sessionVersion = 1; + public static final int SESSIONVERSION_FIELD_NUMBER = 1; + private int sessionVersion_; + /** + * optional uint32 sessionVersion = 1; + */ + public boolean hasSessionVersion() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 sessionVersion = 1; + */ + public int getSessionVersion() { + return sessionVersion_; + } + + // optional bytes localIdentityPublic = 2; + public static final int LOCALIDENTITYPUBLIC_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString localIdentityPublic_; + /** + * optional bytes localIdentityPublic = 2; + */ + public boolean hasLocalIdentityPublic() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes localIdentityPublic = 2; + */ + public com.google.protobuf.ByteString getLocalIdentityPublic() { + return localIdentityPublic_; + } + + // optional bytes remoteIdentityPublic = 3; + public static final int REMOTEIDENTITYPUBLIC_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString remoteIdentityPublic_; + /** + * optional bytes remoteIdentityPublic = 3; + */ + public boolean hasRemoteIdentityPublic() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes remoteIdentityPublic = 3; + */ + public com.google.protobuf.ByteString getRemoteIdentityPublic() { + return remoteIdentityPublic_; + } + + // optional bytes rootKey = 4; + public static final int ROOTKEY_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString rootKey_; + /** + * optional bytes rootKey = 4; + */ + public boolean hasRootKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes rootKey = 4; + */ + public com.google.protobuf.ByteString getRootKey() { + return rootKey_; + } + + // optional uint32 previousCounter = 5; + public static final int PREVIOUSCOUNTER_FIELD_NUMBER = 5; + private int previousCounter_; + /** + * optional uint32 previousCounter = 5; + */ + public boolean hasPreviousCounter() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional uint32 previousCounter = 5; + */ + public int getPreviousCounter() { + return previousCounter_; + } + + // optional .textsecure.SessionStructure.Chain senderChain = 6; + public static final int SENDERCHAIN_FIELD_NUMBER = 6; + private org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain senderChain_; + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public boolean hasSenderChain() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain getSenderChain() { + return senderChain_; + } + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.ChainOrBuilder getSenderChainOrBuilder() { + return senderChain_; + } + + // repeated .textsecure.SessionStructure.Chain receiverChains = 7; + public static final int RECEIVERCHAINS_FIELD_NUMBER = 7; + private java.util.List receiverChains_; + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public java.util.List getReceiverChainsList() { + return receiverChains_; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public java.util.List + getReceiverChainsOrBuilderList() { + return receiverChains_; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public int getReceiverChainsCount() { + return receiverChains_.size(); + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain getReceiverChains(int index) { + return receiverChains_.get(index); + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.ChainOrBuilder getReceiverChainsOrBuilder( + int index) { + return receiverChains_.get(index); + } + + // optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + public static final int PENDINGKEYEXCHANGE_FIELD_NUMBER = 8; + private org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange pendingKeyExchange_; + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public boolean hasPendingKeyExchange() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange getPendingKeyExchange() { + return pendingKeyExchange_; + } + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchangeOrBuilder getPendingKeyExchangeOrBuilder() { + return pendingKeyExchange_; + } + + // optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + public static final int PENDINGPREKEY_FIELD_NUMBER = 9; + private org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey pendingPreKey_; + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public boolean hasPendingPreKey() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey getPendingPreKey() { + return pendingPreKey_; + } + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKeyOrBuilder getPendingPreKeyOrBuilder() { + return pendingPreKey_; + } + + // optional uint32 remoteRegistrationId = 10; + public static final int REMOTEREGISTRATIONID_FIELD_NUMBER = 10; + private int remoteRegistrationId_; + /** + * optional uint32 remoteRegistrationId = 10; + */ + public boolean hasRemoteRegistrationId() { + return ((bitField0_ & 0x00000100) == 0x00000100); + } + /** + * optional uint32 remoteRegistrationId = 10; + */ + public int getRemoteRegistrationId() { + return remoteRegistrationId_; + } + + // optional uint32 localRegistrationId = 11; + public static final int LOCALREGISTRATIONID_FIELD_NUMBER = 11; + private int localRegistrationId_; + /** + * optional uint32 localRegistrationId = 11; + */ + public boolean hasLocalRegistrationId() { + return ((bitField0_ & 0x00000200) == 0x00000200); + } + /** + * optional uint32 localRegistrationId = 11; + */ + public int getLocalRegistrationId() { + return localRegistrationId_; + } + + // optional bool needsRefresh = 12; + public static final int NEEDSREFRESH_FIELD_NUMBER = 12; + private boolean needsRefresh_; + /** + * optional bool needsRefresh = 12; + */ + public boolean hasNeedsRefresh() { + return ((bitField0_ & 0x00000400) == 0x00000400); + } + /** + * optional bool needsRefresh = 12; + */ + public boolean getNeedsRefresh() { + return needsRefresh_; + } + + // optional bytes aliceBaseKey = 13; + public static final int ALICEBASEKEY_FIELD_NUMBER = 13; + private com.google.protobuf.ByteString aliceBaseKey_; + /** + * optional bytes aliceBaseKey = 13; + */ + public boolean hasAliceBaseKey() { + return ((bitField0_ & 0x00000800) == 0x00000800); + } + /** + * optional bytes aliceBaseKey = 13; + */ + public com.google.protobuf.ByteString getAliceBaseKey() { + return aliceBaseKey_; + } + + private void initFields() { + sessionVersion_ = 0; + localIdentityPublic_ = com.google.protobuf.ByteString.EMPTY; + remoteIdentityPublic_ = com.google.protobuf.ByteString.EMPTY; + rootKey_ = com.google.protobuf.ByteString.EMPTY; + previousCounter_ = 0; + senderChain_ = org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.getDefaultInstance(); + receiverChains_ = java.util.Collections.emptyList(); + pendingKeyExchange_ = org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange.getDefaultInstance(); + pendingPreKey_ = org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance(); + remoteRegistrationId_ = 0; + localRegistrationId_ = 0; + needsRefresh_ = false; + aliceBaseKey_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, sessionVersion_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, localIdentityPublic_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, remoteIdentityPublic_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, rootKey_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeUInt32(5, previousCounter_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeMessage(6, senderChain_); + } + for (int i = 0; i < receiverChains_.size(); i++) { + output.writeMessage(7, receiverChains_.get(i)); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + output.writeMessage(8, pendingKeyExchange_); + } + if (((bitField0_ & 0x00000080) == 0x00000080)) { + output.writeMessage(9, pendingPreKey_); + } + if (((bitField0_ & 0x00000100) == 0x00000100)) { + output.writeUInt32(10, remoteRegistrationId_); + } + if (((bitField0_ & 0x00000200) == 0x00000200)) { + output.writeUInt32(11, localRegistrationId_); + } + if (((bitField0_ & 0x00000400) == 0x00000400)) { + output.writeBool(12, needsRefresh_); + } + if (((bitField0_ & 0x00000800) == 0x00000800)) { + output.writeBytes(13, aliceBaseKey_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, sessionVersion_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, localIdentityPublic_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, remoteIdentityPublic_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, rootKey_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(5, previousCounter_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(6, senderChain_); + } + for (int i = 0; i < receiverChains_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(7, receiverChains_.get(i)); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(8, pendingKeyExchange_); + } + if (((bitField0_ & 0x00000080) == 0x00000080)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(9, pendingPreKey_); + } + if (((bitField0_ & 0x00000100) == 0x00000100)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(10, remoteRegistrationId_); + } + if (((bitField0_ & 0x00000200) == 0x00000200)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(11, localRegistrationId_); + } + if (((bitField0_ & 0x00000400) == 0x00000400)) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(12, needsRefresh_); + } + if (((bitField0_ & 0x00000800) == 0x00000800)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(13, aliceBaseKey_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SessionStructure} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.libaxolotl.state.StorageProtos.SessionStructureOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.class, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Builder.class); + } + + // Construct using org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getSenderChainFieldBuilder(); + getReceiverChainsFieldBuilder(); + getPendingKeyExchangeFieldBuilder(); + getPendingPreKeyFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + sessionVersion_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + localIdentityPublic_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + remoteIdentityPublic_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + rootKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + previousCounter_ = 0; + bitField0_ = (bitField0_ & ~0x00000010); + if (senderChainBuilder_ == null) { + senderChain_ = org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.getDefaultInstance(); + } else { + senderChainBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000020); + if (receiverChainsBuilder_ == null) { + receiverChains_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000040); + } else { + receiverChainsBuilder_.clear(); + } + if (pendingKeyExchangeBuilder_ == null) { + pendingKeyExchange_ = org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange.getDefaultInstance(); + } else { + pendingKeyExchangeBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000080); + if (pendingPreKeyBuilder_ == null) { + pendingPreKey_ = org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance(); + } else { + pendingPreKeyBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000100); + remoteRegistrationId_ = 0; + bitField0_ = (bitField0_ & ~0x00000200); + localRegistrationId_ = 0; + bitField0_ = (bitField0_ & ~0x00000400); + needsRefresh_ = false; + bitField0_ = (bitField0_ & ~0x00000800); + aliceBaseKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00001000); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SessionStructure_descriptor; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure getDefaultInstanceForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.getDefaultInstance(); + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure build() { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure buildPartial() { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure result = new org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.sessionVersion_ = sessionVersion_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.localIdentityPublic_ = localIdentityPublic_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.remoteIdentityPublic_ = remoteIdentityPublic_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.rootKey_ = rootKey_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + result.previousCounter_ = previousCounter_; + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000020; + } + if (senderChainBuilder_ == null) { + result.senderChain_ = senderChain_; + } else { + result.senderChain_ = senderChainBuilder_.build(); + } + if (receiverChainsBuilder_ == null) { + if (((bitField0_ & 0x00000040) == 0x00000040)) { + receiverChains_ = java.util.Collections.unmodifiableList(receiverChains_); + bitField0_ = (bitField0_ & ~0x00000040); + } + result.receiverChains_ = receiverChains_; + } else { + result.receiverChains_ = receiverChainsBuilder_.build(); + } + if (((from_bitField0_ & 0x00000080) == 0x00000080)) { + to_bitField0_ |= 0x00000040; + } + if (pendingKeyExchangeBuilder_ == null) { + result.pendingKeyExchange_ = pendingKeyExchange_; + } else { + result.pendingKeyExchange_ = pendingKeyExchangeBuilder_.build(); + } + if (((from_bitField0_ & 0x00000100) == 0x00000100)) { + to_bitField0_ |= 0x00000080; + } + if (pendingPreKeyBuilder_ == null) { + result.pendingPreKey_ = pendingPreKey_; + } else { + result.pendingPreKey_ = pendingPreKeyBuilder_.build(); + } + if (((from_bitField0_ & 0x00000200) == 0x00000200)) { + to_bitField0_ |= 0x00000100; + } + result.remoteRegistrationId_ = remoteRegistrationId_; + if (((from_bitField0_ & 0x00000400) == 0x00000400)) { + to_bitField0_ |= 0x00000200; + } + result.localRegistrationId_ = localRegistrationId_; + if (((from_bitField0_ & 0x00000800) == 0x00000800)) { + to_bitField0_ |= 0x00000400; + } + result.needsRefresh_ = needsRefresh_; + if (((from_bitField0_ & 0x00001000) == 0x00001000)) { + to_bitField0_ |= 0x00000800; + } + result.aliceBaseKey_ = aliceBaseKey_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure) { + return mergeFrom((org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure other) { + if (other == org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.getDefaultInstance()) return this; + if (other.hasSessionVersion()) { + setSessionVersion(other.getSessionVersion()); + } + if (other.hasLocalIdentityPublic()) { + setLocalIdentityPublic(other.getLocalIdentityPublic()); + } + if (other.hasRemoteIdentityPublic()) { + setRemoteIdentityPublic(other.getRemoteIdentityPublic()); + } + if (other.hasRootKey()) { + setRootKey(other.getRootKey()); + } + if (other.hasPreviousCounter()) { + setPreviousCounter(other.getPreviousCounter()); + } + if (other.hasSenderChain()) { + mergeSenderChain(other.getSenderChain()); + } + if (receiverChainsBuilder_ == null) { + if (!other.receiverChains_.isEmpty()) { + if (receiverChains_.isEmpty()) { + receiverChains_ = other.receiverChains_; + bitField0_ = (bitField0_ & ~0x00000040); + } else { + ensureReceiverChainsIsMutable(); + receiverChains_.addAll(other.receiverChains_); + } + onChanged(); + } + } else { + if (!other.receiverChains_.isEmpty()) { + if (receiverChainsBuilder_.isEmpty()) { + receiverChainsBuilder_.dispose(); + receiverChainsBuilder_ = null; + receiverChains_ = other.receiverChains_; + bitField0_ = (bitField0_ & ~0x00000040); + receiverChainsBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getReceiverChainsFieldBuilder() : null; + } else { + receiverChainsBuilder_.addAllMessages(other.receiverChains_); + } + } + } + if (other.hasPendingKeyExchange()) { + mergePendingKeyExchange(other.getPendingKeyExchange()); + } + if (other.hasPendingPreKey()) { + mergePendingPreKey(other.getPendingPreKey()); + } + if (other.hasRemoteRegistrationId()) { + setRemoteRegistrationId(other.getRemoteRegistrationId()); + } + if (other.hasLocalRegistrationId()) { + setLocalRegistrationId(other.getLocalRegistrationId()); + } + if (other.hasNeedsRefresh()) { + setNeedsRefresh(other.getNeedsRefresh()); + } + if (other.hasAliceBaseKey()) { + setAliceBaseKey(other.getAliceBaseKey()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 sessionVersion = 1; + private int sessionVersion_ ; + /** + * optional uint32 sessionVersion = 1; + */ + public boolean hasSessionVersion() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 sessionVersion = 1; + */ + public int getSessionVersion() { + return sessionVersion_; + } + /** + * optional uint32 sessionVersion = 1; + */ + public Builder setSessionVersion(int value) { + bitField0_ |= 0x00000001; + sessionVersion_ = value; + onChanged(); + return this; + } + /** + * optional uint32 sessionVersion = 1; + */ + public Builder clearSessionVersion() { + bitField0_ = (bitField0_ & ~0x00000001); + sessionVersion_ = 0; + onChanged(); + return this; + } + + // optional bytes localIdentityPublic = 2; + private com.google.protobuf.ByteString localIdentityPublic_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes localIdentityPublic = 2; + */ + public boolean hasLocalIdentityPublic() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes localIdentityPublic = 2; + */ + public com.google.protobuf.ByteString getLocalIdentityPublic() { + return localIdentityPublic_; + } + /** + * optional bytes localIdentityPublic = 2; + */ + public Builder setLocalIdentityPublic(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + localIdentityPublic_ = value; + onChanged(); + return this; + } + /** + * optional bytes localIdentityPublic = 2; + */ + public Builder clearLocalIdentityPublic() { + bitField0_ = (bitField0_ & ~0x00000002); + localIdentityPublic_ = getDefaultInstance().getLocalIdentityPublic(); + onChanged(); + return this; + } + + // optional bytes remoteIdentityPublic = 3; + private com.google.protobuf.ByteString remoteIdentityPublic_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes remoteIdentityPublic = 3; + */ + public boolean hasRemoteIdentityPublic() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes remoteIdentityPublic = 3; + */ + public com.google.protobuf.ByteString getRemoteIdentityPublic() { + return remoteIdentityPublic_; + } + /** + * optional bytes remoteIdentityPublic = 3; + */ + public Builder setRemoteIdentityPublic(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + remoteIdentityPublic_ = value; + onChanged(); + return this; + } + /** + * optional bytes remoteIdentityPublic = 3; + */ + public Builder clearRemoteIdentityPublic() { + bitField0_ = (bitField0_ & ~0x00000004); + remoteIdentityPublic_ = getDefaultInstance().getRemoteIdentityPublic(); + onChanged(); + return this; + } + + // optional bytes rootKey = 4; + private com.google.protobuf.ByteString rootKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes rootKey = 4; + */ + public boolean hasRootKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes rootKey = 4; + */ + public com.google.protobuf.ByteString getRootKey() { + return rootKey_; + } + /** + * optional bytes rootKey = 4; + */ + public Builder setRootKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + rootKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes rootKey = 4; + */ + public Builder clearRootKey() { + bitField0_ = (bitField0_ & ~0x00000008); + rootKey_ = getDefaultInstance().getRootKey(); + onChanged(); + return this; + } + + // optional uint32 previousCounter = 5; + private int previousCounter_ ; + /** + * optional uint32 previousCounter = 5; + */ + public boolean hasPreviousCounter() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional uint32 previousCounter = 5; + */ + public int getPreviousCounter() { + return previousCounter_; + } + /** + * optional uint32 previousCounter = 5; + */ + public Builder setPreviousCounter(int value) { + bitField0_ |= 0x00000010; + previousCounter_ = value; + onChanged(); + return this; + } + /** + * optional uint32 previousCounter = 5; + */ + public Builder clearPreviousCounter() { + bitField0_ = (bitField0_ & ~0x00000010); + previousCounter_ = 0; + onChanged(); + return this; + } + + // optional .textsecure.SessionStructure.Chain senderChain = 6; + private org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain senderChain_ = org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.ChainOrBuilder> senderChainBuilder_; + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public boolean hasSenderChain() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain getSenderChain() { + if (senderChainBuilder_ == null) { + return senderChain_; + } else { + return senderChainBuilder_.getMessage(); + } + } + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public Builder setSenderChain(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain value) { + if (senderChainBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + senderChain_ = value; + onChanged(); + } else { + senderChainBuilder_.setMessage(value); + } + bitField0_ |= 0x00000020; + return this; + } + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public Builder setSenderChain( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.Builder builderForValue) { + if (senderChainBuilder_ == null) { + senderChain_ = builderForValue.build(); + onChanged(); + } else { + senderChainBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000020; + return this; + } + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public Builder mergeSenderChain(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain value) { + if (senderChainBuilder_ == null) { + if (((bitField0_ & 0x00000020) == 0x00000020) && + senderChain_ != org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.getDefaultInstance()) { + senderChain_ = + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.newBuilder(senderChain_).mergeFrom(value).buildPartial(); + } else { + senderChain_ = value; + } + onChanged(); + } else { + senderChainBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000020; + return this; + } + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public Builder clearSenderChain() { + if (senderChainBuilder_ == null) { + senderChain_ = org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.getDefaultInstance(); + onChanged(); + } else { + senderChainBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000020); + return this; + } + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.Builder getSenderChainBuilder() { + bitField0_ |= 0x00000020; + onChanged(); + return getSenderChainFieldBuilder().getBuilder(); + } + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.ChainOrBuilder getSenderChainOrBuilder() { + if (senderChainBuilder_ != null) { + return senderChainBuilder_.getMessageOrBuilder(); + } else { + return senderChain_; + } + } + /** + * optional .textsecure.SessionStructure.Chain senderChain = 6; + */ + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.ChainOrBuilder> + getSenderChainFieldBuilder() { + if (senderChainBuilder_ == null) { + senderChainBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.ChainOrBuilder>( + senderChain_, + getParentForChildren(), + isClean()); + senderChain_ = null; + } + return senderChainBuilder_; + } + + // repeated .textsecure.SessionStructure.Chain receiverChains = 7; + private java.util.List receiverChains_ = + java.util.Collections.emptyList(); + private void ensureReceiverChainsIsMutable() { + if (!((bitField0_ & 0x00000040) == 0x00000040)) { + receiverChains_ = new java.util.ArrayList(receiverChains_); + bitField0_ |= 0x00000040; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.ChainOrBuilder> receiverChainsBuilder_; + + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public java.util.List getReceiverChainsList() { + if (receiverChainsBuilder_ == null) { + return java.util.Collections.unmodifiableList(receiverChains_); + } else { + return receiverChainsBuilder_.getMessageList(); + } + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public int getReceiverChainsCount() { + if (receiverChainsBuilder_ == null) { + return receiverChains_.size(); + } else { + return receiverChainsBuilder_.getCount(); + } + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain getReceiverChains(int index) { + if (receiverChainsBuilder_ == null) { + return receiverChains_.get(index); + } else { + return receiverChainsBuilder_.getMessage(index); + } + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public Builder setReceiverChains( + int index, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain value) { + if (receiverChainsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureReceiverChainsIsMutable(); + receiverChains_.set(index, value); + onChanged(); + } else { + receiverChainsBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public Builder setReceiverChains( + int index, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.Builder builderForValue) { + if (receiverChainsBuilder_ == null) { + ensureReceiverChainsIsMutable(); + receiverChains_.set(index, builderForValue.build()); + onChanged(); + } else { + receiverChainsBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public Builder addReceiverChains(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain value) { + if (receiverChainsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureReceiverChainsIsMutable(); + receiverChains_.add(value); + onChanged(); + } else { + receiverChainsBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public Builder addReceiverChains( + int index, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain value) { + if (receiverChainsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureReceiverChainsIsMutable(); + receiverChains_.add(index, value); + onChanged(); + } else { + receiverChainsBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public Builder addReceiverChains( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.Builder builderForValue) { + if (receiverChainsBuilder_ == null) { + ensureReceiverChainsIsMutable(); + receiverChains_.add(builderForValue.build()); + onChanged(); + } else { + receiverChainsBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public Builder addReceiverChains( + int index, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.Builder builderForValue) { + if (receiverChainsBuilder_ == null) { + ensureReceiverChainsIsMutable(); + receiverChains_.add(index, builderForValue.build()); + onChanged(); + } else { + receiverChainsBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public Builder addAllReceiverChains( + java.lang.Iterable values) { + if (receiverChainsBuilder_ == null) { + ensureReceiverChainsIsMutable(); + super.addAll(values, receiverChains_); + onChanged(); + } else { + receiverChainsBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public Builder clearReceiverChains() { + if (receiverChainsBuilder_ == null) { + receiverChains_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000040); + onChanged(); + } else { + receiverChainsBuilder_.clear(); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public Builder removeReceiverChains(int index) { + if (receiverChainsBuilder_ == null) { + ensureReceiverChainsIsMutable(); + receiverChains_.remove(index); + onChanged(); + } else { + receiverChainsBuilder_.remove(index); + } + return this; + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.Builder getReceiverChainsBuilder( + int index) { + return getReceiverChainsFieldBuilder().getBuilder(index); + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.ChainOrBuilder getReceiverChainsOrBuilder( + int index) { + if (receiverChainsBuilder_ == null) { + return receiverChains_.get(index); } else { + return receiverChainsBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public java.util.List + getReceiverChainsOrBuilderList() { + if (receiverChainsBuilder_ != null) { + return receiverChainsBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(receiverChains_); + } + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.Builder addReceiverChainsBuilder() { + return getReceiverChainsFieldBuilder().addBuilder( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.getDefaultInstance()); + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.Builder addReceiverChainsBuilder( + int index) { + return getReceiverChainsFieldBuilder().addBuilder( + index, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.getDefaultInstance()); + } + /** + * repeated .textsecure.SessionStructure.Chain receiverChains = 7; + */ + public java.util.List + getReceiverChainsBuilderList() { + return getReceiverChainsFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.ChainOrBuilder> + getReceiverChainsFieldBuilder() { + if (receiverChainsBuilder_ == null) { + receiverChainsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Chain.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.ChainOrBuilder>( + receiverChains_, + ((bitField0_ & 0x00000040) == 0x00000040), + getParentForChildren(), + isClean()); + receiverChains_ = null; + } + return receiverChainsBuilder_; + } + + // optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + private org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange pendingKeyExchange_ = org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchangeOrBuilder> pendingKeyExchangeBuilder_; + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public boolean hasPendingKeyExchange() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange getPendingKeyExchange() { + if (pendingKeyExchangeBuilder_ == null) { + return pendingKeyExchange_; + } else { + return pendingKeyExchangeBuilder_.getMessage(); + } + } + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public Builder setPendingKeyExchange(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange value) { + if (pendingKeyExchangeBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + pendingKeyExchange_ = value; + onChanged(); + } else { + pendingKeyExchangeBuilder_.setMessage(value); + } + bitField0_ |= 0x00000080; + return this; + } + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public Builder setPendingKeyExchange( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder builderForValue) { + if (pendingKeyExchangeBuilder_ == null) { + pendingKeyExchange_ = builderForValue.build(); + onChanged(); + } else { + pendingKeyExchangeBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000080; + return this; + } + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public Builder mergePendingKeyExchange(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange value) { + if (pendingKeyExchangeBuilder_ == null) { + if (((bitField0_ & 0x00000080) == 0x00000080) && + pendingKeyExchange_ != org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange.getDefaultInstance()) { + pendingKeyExchange_ = + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange.newBuilder(pendingKeyExchange_).mergeFrom(value).buildPartial(); + } else { + pendingKeyExchange_ = value; + } + onChanged(); + } else { + pendingKeyExchangeBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000080; + return this; + } + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public Builder clearPendingKeyExchange() { + if (pendingKeyExchangeBuilder_ == null) { + pendingKeyExchange_ = org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange.getDefaultInstance(); + onChanged(); + } else { + pendingKeyExchangeBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000080); + return this; + } + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder getPendingKeyExchangeBuilder() { + bitField0_ |= 0x00000080; + onChanged(); + return getPendingKeyExchangeFieldBuilder().getBuilder(); + } + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchangeOrBuilder getPendingKeyExchangeOrBuilder() { + if (pendingKeyExchangeBuilder_ != null) { + return pendingKeyExchangeBuilder_.getMessageOrBuilder(); + } else { + return pendingKeyExchange_; + } + } + /** + * optional .textsecure.SessionStructure.PendingKeyExchange pendingKeyExchange = 8; + */ + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchangeOrBuilder> + getPendingKeyExchangeFieldBuilder() { + if (pendingKeyExchangeBuilder_ == null) { + pendingKeyExchangeBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchange.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingKeyExchangeOrBuilder>( + pendingKeyExchange_, + getParentForChildren(), + isClean()); + pendingKeyExchange_ = null; + } + return pendingKeyExchangeBuilder_; + } + + // optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + private org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey pendingPreKey_ = org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKeyOrBuilder> pendingPreKeyBuilder_; + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public boolean hasPendingPreKey() { + return ((bitField0_ & 0x00000100) == 0x00000100); + } + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey getPendingPreKey() { + if (pendingPreKeyBuilder_ == null) { + return pendingPreKey_; + } else { + return pendingPreKeyBuilder_.getMessage(); + } + } + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public Builder setPendingPreKey(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey value) { + if (pendingPreKeyBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + pendingPreKey_ = value; + onChanged(); + } else { + pendingPreKeyBuilder_.setMessage(value); + } + bitField0_ |= 0x00000100; + return this; + } + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public Builder setPendingPreKey( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey.Builder builderForValue) { + if (pendingPreKeyBuilder_ == null) { + pendingPreKey_ = builderForValue.build(); + onChanged(); + } else { + pendingPreKeyBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000100; + return this; + } + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public Builder mergePendingPreKey(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey value) { + if (pendingPreKeyBuilder_ == null) { + if (((bitField0_ & 0x00000100) == 0x00000100) && + pendingPreKey_ != org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance()) { + pendingPreKey_ = + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey.newBuilder(pendingPreKey_).mergeFrom(value).buildPartial(); + } else { + pendingPreKey_ = value; + } + onChanged(); + } else { + pendingPreKeyBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000100; + return this; + } + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public Builder clearPendingPreKey() { + if (pendingPreKeyBuilder_ == null) { + pendingPreKey_ = org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance(); + onChanged(); + } else { + pendingPreKeyBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000100); + return this; + } + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey.Builder getPendingPreKeyBuilder() { + bitField0_ |= 0x00000100; + onChanged(); + return getPendingPreKeyFieldBuilder().getBuilder(); + } + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKeyOrBuilder getPendingPreKeyOrBuilder() { + if (pendingPreKeyBuilder_ != null) { + return pendingPreKeyBuilder_.getMessageOrBuilder(); + } else { + return pendingPreKey_; + } + } + /** + * optional .textsecure.SessionStructure.PendingPreKey pendingPreKey = 9; + */ + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKeyOrBuilder> + getPendingPreKeyFieldBuilder() { + if (pendingPreKeyBuilder_ == null) { + pendingPreKeyBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKey.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PendingPreKeyOrBuilder>( + pendingPreKey_, + getParentForChildren(), + isClean()); + pendingPreKey_ = null; + } + return pendingPreKeyBuilder_; + } + + // optional uint32 remoteRegistrationId = 10; + private int remoteRegistrationId_ ; + /** + * optional uint32 remoteRegistrationId = 10; + */ + public boolean hasRemoteRegistrationId() { + return ((bitField0_ & 0x00000200) == 0x00000200); + } + /** + * optional uint32 remoteRegistrationId = 10; + */ + public int getRemoteRegistrationId() { + return remoteRegistrationId_; + } + /** + * optional uint32 remoteRegistrationId = 10; + */ + public Builder setRemoteRegistrationId(int value) { + bitField0_ |= 0x00000200; + remoteRegistrationId_ = value; + onChanged(); + return this; + } + /** + * optional uint32 remoteRegistrationId = 10; + */ + public Builder clearRemoteRegistrationId() { + bitField0_ = (bitField0_ & ~0x00000200); + remoteRegistrationId_ = 0; + onChanged(); + return this; + } + + // optional uint32 localRegistrationId = 11; + private int localRegistrationId_ ; + /** + * optional uint32 localRegistrationId = 11; + */ + public boolean hasLocalRegistrationId() { + return ((bitField0_ & 0x00000400) == 0x00000400); + } + /** + * optional uint32 localRegistrationId = 11; + */ + public int getLocalRegistrationId() { + return localRegistrationId_; + } + /** + * optional uint32 localRegistrationId = 11; + */ + public Builder setLocalRegistrationId(int value) { + bitField0_ |= 0x00000400; + localRegistrationId_ = value; + onChanged(); + return this; + } + /** + * optional uint32 localRegistrationId = 11; + */ + public Builder clearLocalRegistrationId() { + bitField0_ = (bitField0_ & ~0x00000400); + localRegistrationId_ = 0; + onChanged(); + return this; + } + + // optional bool needsRefresh = 12; + private boolean needsRefresh_ ; + /** + * optional bool needsRefresh = 12; + */ + public boolean hasNeedsRefresh() { + return ((bitField0_ & 0x00000800) == 0x00000800); + } + /** + * optional bool needsRefresh = 12; + */ + public boolean getNeedsRefresh() { + return needsRefresh_; + } + /** + * optional bool needsRefresh = 12; + */ + public Builder setNeedsRefresh(boolean value) { + bitField0_ |= 0x00000800; + needsRefresh_ = value; + onChanged(); + return this; + } + /** + * optional bool needsRefresh = 12; + */ + public Builder clearNeedsRefresh() { + bitField0_ = (bitField0_ & ~0x00000800); + needsRefresh_ = false; + onChanged(); + return this; + } + + // optional bytes aliceBaseKey = 13; + private com.google.protobuf.ByteString aliceBaseKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes aliceBaseKey = 13; + */ + public boolean hasAliceBaseKey() { + return ((bitField0_ & 0x00001000) == 0x00001000); + } + /** + * optional bytes aliceBaseKey = 13; + */ + public com.google.protobuf.ByteString getAliceBaseKey() { + return aliceBaseKey_; + } + /** + * optional bytes aliceBaseKey = 13; + */ + public Builder setAliceBaseKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00001000; + aliceBaseKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes aliceBaseKey = 13; + */ + public Builder clearAliceBaseKey() { + bitField0_ = (bitField0_ & ~0x00001000); + aliceBaseKey_ = getDefaultInstance().getAliceBaseKey(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SessionStructure) + } + + static { + defaultInstance = new SessionStructure(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SessionStructure) + } + + public interface RecordStructureOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional .textsecure.SessionStructure currentSession = 1; + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + boolean hasCurrentSession(); + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure getCurrentSession(); + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructureOrBuilder getCurrentSessionOrBuilder(); + + // repeated .textsecure.SessionStructure previousSessions = 2; + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + java.util.List + getPreviousSessionsList(); + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure getPreviousSessions(int index); + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + int getPreviousSessionsCount(); + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + java.util.List + getPreviousSessionsOrBuilderList(); + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructureOrBuilder getPreviousSessionsOrBuilder( + int index); + } + /** + * Protobuf type {@code textsecure.RecordStructure} + */ + public static final class RecordStructure extends + com.google.protobuf.GeneratedMessage + implements RecordStructureOrBuilder { + // Use RecordStructure.newBuilder() to construct. + private RecordStructure(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private RecordStructure(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final RecordStructure defaultInstance; + public static RecordStructure getDefaultInstance() { + return defaultInstance; + } + + public RecordStructure getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private RecordStructure( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Builder subBuilder = null; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + subBuilder = currentSession_.toBuilder(); + } + currentSession_ = input.readMessage(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(currentSession_); + currentSession_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000001; + break; + } + case 18: { + if (!((mutable_bitField0_ & 0x00000002) == 0x00000002)) { + previousSessions_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000002; + } + previousSessions_.add(input.readMessage(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.PARSER, extensionRegistry)); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000002) == 0x00000002)) { + previousSessions_ = java.util.Collections.unmodifiableList(previousSessions_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_RecordStructure_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_RecordStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure.class, org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public RecordStructure parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new RecordStructure(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional .textsecure.SessionStructure currentSession = 1; + public static final int CURRENTSESSION_FIELD_NUMBER = 1; + private org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure currentSession_; + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public boolean hasCurrentSession() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure getCurrentSession() { + return currentSession_; + } + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructureOrBuilder getCurrentSessionOrBuilder() { + return currentSession_; + } + + // repeated .textsecure.SessionStructure previousSessions = 2; + public static final int PREVIOUSSESSIONS_FIELD_NUMBER = 2; + private java.util.List previousSessions_; + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public java.util.List getPreviousSessionsList() { + return previousSessions_; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public java.util.List + getPreviousSessionsOrBuilderList() { + return previousSessions_; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public int getPreviousSessionsCount() { + return previousSessions_.size(); + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure getPreviousSessions(int index) { + return previousSessions_.get(index); + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructureOrBuilder getPreviousSessionsOrBuilder( + int index) { + return previousSessions_.get(index); + } + + private void initFields() { + currentSession_ = org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.getDefaultInstance(); + previousSessions_ = java.util.Collections.emptyList(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeMessage(1, currentSession_); + } + for (int i = 0; i < previousSessions_.size(); i++) { + output.writeMessage(2, previousSessions_.get(i)); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, currentSession_); + } + for (int i = 0; i < previousSessions_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, previousSessions_.get(i)); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.RecordStructure} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.libaxolotl.state.StorageProtos.RecordStructureOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_RecordStructure_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_RecordStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure.class, org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure.Builder.class); + } + + // Construct using org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getCurrentSessionFieldBuilder(); + getPreviousSessionsFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + if (currentSessionBuilder_ == null) { + currentSession_ = org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.getDefaultInstance(); + } else { + currentSessionBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + if (previousSessionsBuilder_ == null) { + previousSessions_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000002); + } else { + previousSessionsBuilder_.clear(); + } + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_RecordStructure_descriptor; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure getDefaultInstanceForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure.getDefaultInstance(); + } + + public org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure build() { + org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure buildPartial() { + org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure result = new org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + if (currentSessionBuilder_ == null) { + result.currentSession_ = currentSession_; + } else { + result.currentSession_ = currentSessionBuilder_.build(); + } + if (previousSessionsBuilder_ == null) { + if (((bitField0_ & 0x00000002) == 0x00000002)) { + previousSessions_ = java.util.Collections.unmodifiableList(previousSessions_); + bitField0_ = (bitField0_ & ~0x00000002); + } + result.previousSessions_ = previousSessions_; + } else { + result.previousSessions_ = previousSessionsBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure) { + return mergeFrom((org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure other) { + if (other == org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure.getDefaultInstance()) return this; + if (other.hasCurrentSession()) { + mergeCurrentSession(other.getCurrentSession()); + } + if (previousSessionsBuilder_ == null) { + if (!other.previousSessions_.isEmpty()) { + if (previousSessions_.isEmpty()) { + previousSessions_ = other.previousSessions_; + bitField0_ = (bitField0_ & ~0x00000002); + } else { + ensurePreviousSessionsIsMutable(); + previousSessions_.addAll(other.previousSessions_); + } + onChanged(); + } + } else { + if (!other.previousSessions_.isEmpty()) { + if (previousSessionsBuilder_.isEmpty()) { + previousSessionsBuilder_.dispose(); + previousSessionsBuilder_ = null; + previousSessions_ = other.previousSessions_; + bitField0_ = (bitField0_ & ~0x00000002); + previousSessionsBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getPreviousSessionsFieldBuilder() : null; + } else { + previousSessionsBuilder_.addAllMessages(other.previousSessions_); + } + } + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.libaxolotl.state.StorageProtos.RecordStructure) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional .textsecure.SessionStructure currentSession = 1; + private org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure currentSession_ = org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructureOrBuilder> currentSessionBuilder_; + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public boolean hasCurrentSession() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure getCurrentSession() { + if (currentSessionBuilder_ == null) { + return currentSession_; + } else { + return currentSessionBuilder_.getMessage(); + } + } + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public Builder setCurrentSession(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure value) { + if (currentSessionBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + currentSession_ = value; + onChanged(); + } else { + currentSessionBuilder_.setMessage(value); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public Builder setCurrentSession( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Builder builderForValue) { + if (currentSessionBuilder_ == null) { + currentSession_ = builderForValue.build(); + onChanged(); + } else { + currentSessionBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public Builder mergeCurrentSession(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure value) { + if (currentSessionBuilder_ == null) { + if (((bitField0_ & 0x00000001) == 0x00000001) && + currentSession_ != org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.getDefaultInstance()) { + currentSession_ = + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.newBuilder(currentSession_).mergeFrom(value).buildPartial(); + } else { + currentSession_ = value; + } + onChanged(); + } else { + currentSessionBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000001; + return this; + } + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public Builder clearCurrentSession() { + if (currentSessionBuilder_ == null) { + currentSession_ = org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.getDefaultInstance(); + onChanged(); + } else { + currentSessionBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Builder getCurrentSessionBuilder() { + bitField0_ |= 0x00000001; + onChanged(); + return getCurrentSessionFieldBuilder().getBuilder(); + } + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructureOrBuilder getCurrentSessionOrBuilder() { + if (currentSessionBuilder_ != null) { + return currentSessionBuilder_.getMessageOrBuilder(); + } else { + return currentSession_; + } + } + /** + * optional .textsecure.SessionStructure currentSession = 1; + */ + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructureOrBuilder> + getCurrentSessionFieldBuilder() { + if (currentSessionBuilder_ == null) { + currentSessionBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructureOrBuilder>( + currentSession_, + getParentForChildren(), + isClean()); + currentSession_ = null; + } + return currentSessionBuilder_; + } + + // repeated .textsecure.SessionStructure previousSessions = 2; + private java.util.List previousSessions_ = + java.util.Collections.emptyList(); + private void ensurePreviousSessionsIsMutable() { + if (!((bitField0_ & 0x00000002) == 0x00000002)) { + previousSessions_ = new java.util.ArrayList(previousSessions_); + bitField0_ |= 0x00000002; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructureOrBuilder> previousSessionsBuilder_; + + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public java.util.List getPreviousSessionsList() { + if (previousSessionsBuilder_ == null) { + return java.util.Collections.unmodifiableList(previousSessions_); + } else { + return previousSessionsBuilder_.getMessageList(); + } + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public int getPreviousSessionsCount() { + if (previousSessionsBuilder_ == null) { + return previousSessions_.size(); + } else { + return previousSessionsBuilder_.getCount(); + } + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure getPreviousSessions(int index) { + if (previousSessionsBuilder_ == null) { + return previousSessions_.get(index); + } else { + return previousSessionsBuilder_.getMessage(index); + } + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public Builder setPreviousSessions( + int index, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure value) { + if (previousSessionsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensurePreviousSessionsIsMutable(); + previousSessions_.set(index, value); + onChanged(); + } else { + previousSessionsBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public Builder setPreviousSessions( + int index, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Builder builderForValue) { + if (previousSessionsBuilder_ == null) { + ensurePreviousSessionsIsMutable(); + previousSessions_.set(index, builderForValue.build()); + onChanged(); + } else { + previousSessionsBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public Builder addPreviousSessions(org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure value) { + if (previousSessionsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensurePreviousSessionsIsMutable(); + previousSessions_.add(value); + onChanged(); + } else { + previousSessionsBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public Builder addPreviousSessions( + int index, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure value) { + if (previousSessionsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensurePreviousSessionsIsMutable(); + previousSessions_.add(index, value); + onChanged(); + } else { + previousSessionsBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public Builder addPreviousSessions( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Builder builderForValue) { + if (previousSessionsBuilder_ == null) { + ensurePreviousSessionsIsMutable(); + previousSessions_.add(builderForValue.build()); + onChanged(); + } else { + previousSessionsBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public Builder addPreviousSessions( + int index, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Builder builderForValue) { + if (previousSessionsBuilder_ == null) { + ensurePreviousSessionsIsMutable(); + previousSessions_.add(index, builderForValue.build()); + onChanged(); + } else { + previousSessionsBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public Builder addAllPreviousSessions( + java.lang.Iterable values) { + if (previousSessionsBuilder_ == null) { + ensurePreviousSessionsIsMutable(); + super.addAll(values, previousSessions_); + onChanged(); + } else { + previousSessionsBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public Builder clearPreviousSessions() { + if (previousSessionsBuilder_ == null) { + previousSessions_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + } else { + previousSessionsBuilder_.clear(); + } + return this; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public Builder removePreviousSessions(int index) { + if (previousSessionsBuilder_ == null) { + ensurePreviousSessionsIsMutable(); + previousSessions_.remove(index); + onChanged(); + } else { + previousSessionsBuilder_.remove(index); + } + return this; + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Builder getPreviousSessionsBuilder( + int index) { + return getPreviousSessionsFieldBuilder().getBuilder(index); + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructureOrBuilder getPreviousSessionsOrBuilder( + int index) { + if (previousSessionsBuilder_ == null) { + return previousSessions_.get(index); } else { + return previousSessionsBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public java.util.List + getPreviousSessionsOrBuilderList() { + if (previousSessionsBuilder_ != null) { + return previousSessionsBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(previousSessions_); + } + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Builder addPreviousSessionsBuilder() { + return getPreviousSessionsFieldBuilder().addBuilder( + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.getDefaultInstance()); + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Builder addPreviousSessionsBuilder( + int index) { + return getPreviousSessionsFieldBuilder().addBuilder( + index, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.getDefaultInstance()); + } + /** + * repeated .textsecure.SessionStructure previousSessions = 2; + */ + public java.util.List + getPreviousSessionsBuilderList() { + return getPreviousSessionsFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructureOrBuilder> + getPreviousSessionsFieldBuilder() { + if (previousSessionsBuilder_ == null) { + previousSessionsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructure.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SessionStructureOrBuilder>( + previousSessions_, + ((bitField0_ & 0x00000002) == 0x00000002), + getParentForChildren(), + isClean()); + previousSessions_ = null; + } + return previousSessionsBuilder_; + } + + // @@protoc_insertion_point(builder_scope:textsecure.RecordStructure) + } + + static { + defaultInstance = new RecordStructure(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.RecordStructure) + } + + public interface PreKeyRecordStructureOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 id = 1; + /** + * optional uint32 id = 1; + */ + boolean hasId(); + /** + * optional uint32 id = 1; + */ + int getId(); + + // optional bytes publicKey = 2; + /** + * optional bytes publicKey = 2; + */ + boolean hasPublicKey(); + /** + * optional bytes publicKey = 2; + */ + com.google.protobuf.ByteString getPublicKey(); + + // optional bytes privateKey = 3; + /** + * optional bytes privateKey = 3; + */ + boolean hasPrivateKey(); + /** + * optional bytes privateKey = 3; + */ + com.google.protobuf.ByteString getPrivateKey(); + } + /** + * Protobuf type {@code textsecure.PreKeyRecordStructure} + */ + public static final class PreKeyRecordStructure extends + com.google.protobuf.GeneratedMessage + implements PreKeyRecordStructureOrBuilder { + // Use PreKeyRecordStructure.newBuilder() to construct. + private PreKeyRecordStructure(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private PreKeyRecordStructure(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final PreKeyRecordStructure defaultInstance; + public static PreKeyRecordStructure getDefaultInstance() { + return defaultInstance; + } + + public PreKeyRecordStructure getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private PreKeyRecordStructure( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + publicKey_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + privateKey_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_PreKeyRecordStructure_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_PreKeyRecordStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure.class, org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public PreKeyRecordStructure parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new PreKeyRecordStructure(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private int id_; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + + // optional bytes publicKey = 2; + public static final int PUBLICKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString publicKey_; + /** + * optional bytes publicKey = 2; + */ + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes publicKey = 2; + */ + public com.google.protobuf.ByteString getPublicKey() { + return publicKey_; + } + + // optional bytes privateKey = 3; + public static final int PRIVATEKEY_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString privateKey_; + /** + * optional bytes privateKey = 3; + */ + public boolean hasPrivateKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes privateKey = 3; + */ + public com.google.protobuf.ByteString getPrivateKey() { + return privateKey_; + } + + private void initFields() { + id_ = 0; + publicKey_ = com.google.protobuf.ByteString.EMPTY; + privateKey_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, publicKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, privateKey_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, publicKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, privateKey_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.PreKeyRecordStructure} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructureOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_PreKeyRecordStructure_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_PreKeyRecordStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure.class, org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure.Builder.class); + } + + // Construct using org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + publicKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + privateKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_PreKeyRecordStructure_descriptor; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure getDefaultInstanceForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure.getDefaultInstance(); + } + + public org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure build() { + org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure buildPartial() { + org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure result = new org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.publicKey_ = publicKey_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.privateKey_ = privateKey_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure) { + return mergeFrom((org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure other) { + if (other == org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasPublicKey()) { + setPublicKey(other.getPublicKey()); + } + if (other.hasPrivateKey()) { + setPrivateKey(other.getPrivateKey()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 id = 1; + private int id_ ; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + /** + * optional uint32 id = 1; + */ + public Builder setId(int value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint32 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0; + onChanged(); + return this; + } + + // optional bytes publicKey = 2; + private com.google.protobuf.ByteString publicKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes publicKey = 2; + */ + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes publicKey = 2; + */ + public com.google.protobuf.ByteString getPublicKey() { + return publicKey_; + } + /** + * optional bytes publicKey = 2; + */ + public Builder setPublicKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + publicKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes publicKey = 2; + */ + public Builder clearPublicKey() { + bitField0_ = (bitField0_ & ~0x00000002); + publicKey_ = getDefaultInstance().getPublicKey(); + onChanged(); + return this; + } + + // optional bytes privateKey = 3; + private com.google.protobuf.ByteString privateKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes privateKey = 3; + */ + public boolean hasPrivateKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes privateKey = 3; + */ + public com.google.protobuf.ByteString getPrivateKey() { + return privateKey_; + } + /** + * optional bytes privateKey = 3; + */ + public Builder setPrivateKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + privateKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes privateKey = 3; + */ + public Builder clearPrivateKey() { + bitField0_ = (bitField0_ & ~0x00000004); + privateKey_ = getDefaultInstance().getPrivateKey(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.PreKeyRecordStructure) + } + + static { + defaultInstance = new PreKeyRecordStructure(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.PreKeyRecordStructure) + } + + public interface SignedPreKeyRecordStructureOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 id = 1; + /** + * optional uint32 id = 1; + */ + boolean hasId(); + /** + * optional uint32 id = 1; + */ + int getId(); + + // optional bytes publicKey = 2; + /** + * optional bytes publicKey = 2; + */ + boolean hasPublicKey(); + /** + * optional bytes publicKey = 2; + */ + com.google.protobuf.ByteString getPublicKey(); + + // optional bytes privateKey = 3; + /** + * optional bytes privateKey = 3; + */ + boolean hasPrivateKey(); + /** + * optional bytes privateKey = 3; + */ + com.google.protobuf.ByteString getPrivateKey(); + + // optional bytes signature = 4; + /** + * optional bytes signature = 4; + */ + boolean hasSignature(); + /** + * optional bytes signature = 4; + */ + com.google.protobuf.ByteString getSignature(); + + // optional fixed64 timestamp = 5; + /** + * optional fixed64 timestamp = 5; + */ + boolean hasTimestamp(); + /** + * optional fixed64 timestamp = 5; + */ + long getTimestamp(); + } + /** + * Protobuf type {@code textsecure.SignedPreKeyRecordStructure} + */ + public static final class SignedPreKeyRecordStructure extends + com.google.protobuf.GeneratedMessage + implements SignedPreKeyRecordStructureOrBuilder { + // Use SignedPreKeyRecordStructure.newBuilder() to construct. + private SignedPreKeyRecordStructure(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SignedPreKeyRecordStructure(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SignedPreKeyRecordStructure defaultInstance; + public static SignedPreKeyRecordStructure getDefaultInstance() { + return defaultInstance; + } + + public SignedPreKeyRecordStructure getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SignedPreKeyRecordStructure( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + publicKey_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + privateKey_ = input.readBytes(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + signature_ = input.readBytes(); + break; + } + case 41: { + bitField0_ |= 0x00000010; + timestamp_ = input.readFixed64(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SignedPreKeyRecordStructure_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SignedPreKeyRecordStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure.class, org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SignedPreKeyRecordStructure parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SignedPreKeyRecordStructure(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private int id_; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + + // optional bytes publicKey = 2; + public static final int PUBLICKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString publicKey_; + /** + * optional bytes publicKey = 2; + */ + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes publicKey = 2; + */ + public com.google.protobuf.ByteString getPublicKey() { + return publicKey_; + } + + // optional bytes privateKey = 3; + public static final int PRIVATEKEY_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString privateKey_; + /** + * optional bytes privateKey = 3; + */ + public boolean hasPrivateKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes privateKey = 3; + */ + public com.google.protobuf.ByteString getPrivateKey() { + return privateKey_; + } + + // optional bytes signature = 4; + public static final int SIGNATURE_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString signature_; + /** + * optional bytes signature = 4; + */ + public boolean hasSignature() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes signature = 4; + */ + public com.google.protobuf.ByteString getSignature() { + return signature_; + } + + // optional fixed64 timestamp = 5; + public static final int TIMESTAMP_FIELD_NUMBER = 5; + private long timestamp_; + /** + * optional fixed64 timestamp = 5; + */ + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional fixed64 timestamp = 5; + */ + public long getTimestamp() { + return timestamp_; + } + + private void initFields() { + id_ = 0; + publicKey_ = com.google.protobuf.ByteString.EMPTY; + privateKey_ = com.google.protobuf.ByteString.EMPTY; + signature_ = com.google.protobuf.ByteString.EMPTY; + timestamp_ = 0L; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, publicKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, privateKey_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, signature_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeFixed64(5, timestamp_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, publicKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, privateKey_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, signature_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeFixed64Size(5, timestamp_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SignedPreKeyRecordStructure} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructureOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SignedPreKeyRecordStructure_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SignedPreKeyRecordStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure.class, org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure.Builder.class); + } + + // Construct using org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + publicKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + privateKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + signature_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + timestamp_ = 0L; + bitField0_ = (bitField0_ & ~0x00000010); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SignedPreKeyRecordStructure_descriptor; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure getDefaultInstanceForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure.getDefaultInstance(); + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure build() { + org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure buildPartial() { + org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure result = new org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.publicKey_ = publicKey_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.privateKey_ = privateKey_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.signature_ = signature_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + result.timestamp_ = timestamp_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure) { + return mergeFrom((org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure other) { + if (other == org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasPublicKey()) { + setPublicKey(other.getPublicKey()); + } + if (other.hasPrivateKey()) { + setPrivateKey(other.getPrivateKey()); + } + if (other.hasSignature()) { + setSignature(other.getSignature()); + } + if (other.hasTimestamp()) { + setTimestamp(other.getTimestamp()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.libaxolotl.state.StorageProtos.SignedPreKeyRecordStructure) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 id = 1; + private int id_ ; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + /** + * optional uint32 id = 1; + */ + public Builder setId(int value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint32 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0; + onChanged(); + return this; + } + + // optional bytes publicKey = 2; + private com.google.protobuf.ByteString publicKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes publicKey = 2; + */ + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes publicKey = 2; + */ + public com.google.protobuf.ByteString getPublicKey() { + return publicKey_; + } + /** + * optional bytes publicKey = 2; + */ + public Builder setPublicKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + publicKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes publicKey = 2; + */ + public Builder clearPublicKey() { + bitField0_ = (bitField0_ & ~0x00000002); + publicKey_ = getDefaultInstance().getPublicKey(); + onChanged(); + return this; + } + + // optional bytes privateKey = 3; + private com.google.protobuf.ByteString privateKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes privateKey = 3; + */ + public boolean hasPrivateKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes privateKey = 3; + */ + public com.google.protobuf.ByteString getPrivateKey() { + return privateKey_; + } + /** + * optional bytes privateKey = 3; + */ + public Builder setPrivateKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + privateKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes privateKey = 3; + */ + public Builder clearPrivateKey() { + bitField0_ = (bitField0_ & ~0x00000004); + privateKey_ = getDefaultInstance().getPrivateKey(); + onChanged(); + return this; + } + + // optional bytes signature = 4; + private com.google.protobuf.ByteString signature_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes signature = 4; + */ + public boolean hasSignature() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes signature = 4; + */ + public com.google.protobuf.ByteString getSignature() { + return signature_; + } + /** + * optional bytes signature = 4; + */ + public Builder setSignature(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + signature_ = value; + onChanged(); + return this; + } + /** + * optional bytes signature = 4; + */ + public Builder clearSignature() { + bitField0_ = (bitField0_ & ~0x00000008); + signature_ = getDefaultInstance().getSignature(); + onChanged(); + return this; + } + + // optional fixed64 timestamp = 5; + private long timestamp_ ; + /** + * optional fixed64 timestamp = 5; + */ + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional fixed64 timestamp = 5; + */ + public long getTimestamp() { + return timestamp_; + } + /** + * optional fixed64 timestamp = 5; + */ + public Builder setTimestamp(long value) { + bitField0_ |= 0x00000010; + timestamp_ = value; + onChanged(); + return this; + } + /** + * optional fixed64 timestamp = 5; + */ + public Builder clearTimestamp() { + bitField0_ = (bitField0_ & ~0x00000010); + timestamp_ = 0L; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SignedPreKeyRecordStructure) + } + + static { + defaultInstance = new SignedPreKeyRecordStructure(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SignedPreKeyRecordStructure) + } + + public interface IdentityKeyPairStructureOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes publicKey = 1; + /** + * optional bytes publicKey = 1; + */ + boolean hasPublicKey(); + /** + * optional bytes publicKey = 1; + */ + com.google.protobuf.ByteString getPublicKey(); + + // optional bytes privateKey = 2; + /** + * optional bytes privateKey = 2; + */ + boolean hasPrivateKey(); + /** + * optional bytes privateKey = 2; + */ + com.google.protobuf.ByteString getPrivateKey(); + } + /** + * Protobuf type {@code textsecure.IdentityKeyPairStructure} + */ + public static final class IdentityKeyPairStructure extends + com.google.protobuf.GeneratedMessage + implements IdentityKeyPairStructureOrBuilder { + // Use IdentityKeyPairStructure.newBuilder() to construct. + private IdentityKeyPairStructure(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private IdentityKeyPairStructure(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final IdentityKeyPairStructure defaultInstance; + public static IdentityKeyPairStructure getDefaultInstance() { + return defaultInstance; + } + + public IdentityKeyPairStructure getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private IdentityKeyPairStructure( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + publicKey_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + privateKey_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_IdentityKeyPairStructure_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_IdentityKeyPairStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure.class, org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public IdentityKeyPairStructure parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new IdentityKeyPairStructure(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional bytes publicKey = 1; + public static final int PUBLICKEY_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString publicKey_; + /** + * optional bytes publicKey = 1; + */ + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes publicKey = 1; + */ + public com.google.protobuf.ByteString getPublicKey() { + return publicKey_; + } + + // optional bytes privateKey = 2; + public static final int PRIVATEKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString privateKey_; + /** + * optional bytes privateKey = 2; + */ + public boolean hasPrivateKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes privateKey = 2; + */ + public com.google.protobuf.ByteString getPrivateKey() { + return privateKey_; + } + + private void initFields() { + publicKey_ = com.google.protobuf.ByteString.EMPTY; + privateKey_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, publicKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, privateKey_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, publicKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, privateKey_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.IdentityKeyPairStructure} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructureOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_IdentityKeyPairStructure_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_IdentityKeyPairStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure.class, org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure.Builder.class); + } + + // Construct using org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + publicKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + privateKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_IdentityKeyPairStructure_descriptor; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure getDefaultInstanceForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure.getDefaultInstance(); + } + + public org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure build() { + org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure buildPartial() { + org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure result = new org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.publicKey_ = publicKey_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.privateKey_ = privateKey_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure) { + return mergeFrom((org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure other) { + if (other == org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure.getDefaultInstance()) return this; + if (other.hasPublicKey()) { + setPublicKey(other.getPublicKey()); + } + if (other.hasPrivateKey()) { + setPrivateKey(other.getPrivateKey()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes publicKey = 1; + private com.google.protobuf.ByteString publicKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes publicKey = 1; + */ + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes publicKey = 1; + */ + public com.google.protobuf.ByteString getPublicKey() { + return publicKey_; + } + /** + * optional bytes publicKey = 1; + */ + public Builder setPublicKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + publicKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes publicKey = 1; + */ + public Builder clearPublicKey() { + bitField0_ = (bitField0_ & ~0x00000001); + publicKey_ = getDefaultInstance().getPublicKey(); + onChanged(); + return this; + } + + // optional bytes privateKey = 2; + private com.google.protobuf.ByteString privateKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes privateKey = 2; + */ + public boolean hasPrivateKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes privateKey = 2; + */ + public com.google.protobuf.ByteString getPrivateKey() { + return privateKey_; + } + /** + * optional bytes privateKey = 2; + */ + public Builder setPrivateKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + privateKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes privateKey = 2; + */ + public Builder clearPrivateKey() { + bitField0_ = (bitField0_ & ~0x00000002); + privateKey_ = getDefaultInstance().getPrivateKey(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.IdentityKeyPairStructure) + } + + static { + defaultInstance = new IdentityKeyPairStructure(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.IdentityKeyPairStructure) + } + + public interface SenderKeyStateStructureOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 senderKeyId = 1; + /** + * optional uint32 senderKeyId = 1; + */ + boolean hasSenderKeyId(); + /** + * optional uint32 senderKeyId = 1; + */ + int getSenderKeyId(); + + // optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + boolean hasSenderChainKey(); + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey getSenderChainKey(); + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKeyOrBuilder getSenderChainKeyOrBuilder(); + + // optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + boolean hasSenderSigningKey(); + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey getSenderSigningKey(); + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKeyOrBuilder getSenderSigningKeyOrBuilder(); + + // repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + java.util.List + getSenderMessageKeysList(); + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey getSenderMessageKeys(int index); + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + int getSenderMessageKeysCount(); + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + java.util.List + getSenderMessageKeysOrBuilderList(); + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKeyOrBuilder getSenderMessageKeysOrBuilder( + int index); + } + /** + * Protobuf type {@code textsecure.SenderKeyStateStructure} + */ + public static final class SenderKeyStateStructure extends + com.google.protobuf.GeneratedMessage + implements SenderKeyStateStructureOrBuilder { + // Use SenderKeyStateStructure.newBuilder() to construct. + private SenderKeyStateStructure(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SenderKeyStateStructure(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SenderKeyStateStructure defaultInstance; + public static SenderKeyStateStructure getDefaultInstance() { + return defaultInstance; + } + + public SenderKeyStateStructure getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SenderKeyStateStructure( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + senderKeyId_ = input.readUInt32(); + break; + } + case 18: { + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder subBuilder = null; + if (((bitField0_ & 0x00000002) == 0x00000002)) { + subBuilder = senderChainKey_.toBuilder(); + } + senderChainKey_ = input.readMessage(org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(senderChainKey_); + senderChainKey_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000002; + break; + } + case 26: { + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder subBuilder = null; + if (((bitField0_ & 0x00000004) == 0x00000004)) { + subBuilder = senderSigningKey_.toBuilder(); + } + senderSigningKey_ = input.readMessage(org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(senderSigningKey_); + senderSigningKey_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000004; + break; + } + case 34: { + if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + senderMessageKeys_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000008; + } + senderMessageKeys_.add(input.readMessage(org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.PARSER, extensionRegistry)); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + senderMessageKeys_ = java.util.Collections.unmodifiableList(senderMessageKeys_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.class, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SenderKeyStateStructure parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SenderKeyStateStructure(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public interface SenderChainKeyOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 iteration = 1; + /** + * optional uint32 iteration = 1; + */ + boolean hasIteration(); + /** + * optional uint32 iteration = 1; + */ + int getIteration(); + + // optional bytes seed = 2; + /** + * optional bytes seed = 2; + */ + boolean hasSeed(); + /** + * optional bytes seed = 2; + */ + com.google.protobuf.ByteString getSeed(); + } + /** + * Protobuf type {@code textsecure.SenderKeyStateStructure.SenderChainKey} + */ + public static final class SenderChainKey extends + com.google.protobuf.GeneratedMessage + implements SenderChainKeyOrBuilder { + // Use SenderChainKey.newBuilder() to construct. + private SenderChainKey(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SenderChainKey(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SenderChainKey defaultInstance; + public static SenderChainKey getDefaultInstance() { + return defaultInstance; + } + + public SenderChainKey getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SenderChainKey( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + iteration_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + seed_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.class, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SenderChainKey parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SenderChainKey(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 iteration = 1; + public static final int ITERATION_FIELD_NUMBER = 1; + private int iteration_; + /** + * optional uint32 iteration = 1; + */ + public boolean hasIteration() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 iteration = 1; + */ + public int getIteration() { + return iteration_; + } + + // optional bytes seed = 2; + public static final int SEED_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString seed_; + /** + * optional bytes seed = 2; + */ + public boolean hasSeed() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes seed = 2; + */ + public com.google.protobuf.ByteString getSeed() { + return seed_; + } + + private void initFields() { + iteration_ = 0; + seed_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, iteration_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, seed_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, iteration_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, seed_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SenderKeyStateStructure.SenderChainKey} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKeyOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.class, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder.class); + } + + // Construct using org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + iteration_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + seed_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_descriptor; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey getDefaultInstanceForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.getDefaultInstance(); + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey build() { + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey buildPartial() { + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey result = new org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.iteration_ = iteration_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.seed_ = seed_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey) { + return mergeFrom((org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey other) { + if (other == org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.getDefaultInstance()) return this; + if (other.hasIteration()) { + setIteration(other.getIteration()); + } + if (other.hasSeed()) { + setSeed(other.getSeed()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 iteration = 1; + private int iteration_ ; + /** + * optional uint32 iteration = 1; + */ + public boolean hasIteration() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 iteration = 1; + */ + public int getIteration() { + return iteration_; + } + /** + * optional uint32 iteration = 1; + */ + public Builder setIteration(int value) { + bitField0_ |= 0x00000001; + iteration_ = value; + onChanged(); + return this; + } + /** + * optional uint32 iteration = 1; + */ + public Builder clearIteration() { + bitField0_ = (bitField0_ & ~0x00000001); + iteration_ = 0; + onChanged(); + return this; + } + + // optional bytes seed = 2; + private com.google.protobuf.ByteString seed_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes seed = 2; + */ + public boolean hasSeed() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes seed = 2; + */ + public com.google.protobuf.ByteString getSeed() { + return seed_; + } + /** + * optional bytes seed = 2; + */ + public Builder setSeed(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + seed_ = value; + onChanged(); + return this; + } + /** + * optional bytes seed = 2; + */ + public Builder clearSeed() { + bitField0_ = (bitField0_ & ~0x00000002); + seed_ = getDefaultInstance().getSeed(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SenderKeyStateStructure.SenderChainKey) + } + + static { + defaultInstance = new SenderChainKey(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SenderKeyStateStructure.SenderChainKey) + } + + public interface SenderMessageKeyOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 iteration = 1; + /** + * optional uint32 iteration = 1; + */ + boolean hasIteration(); + /** + * optional uint32 iteration = 1; + */ + int getIteration(); + + // optional bytes seed = 2; + /** + * optional bytes seed = 2; + */ + boolean hasSeed(); + /** + * optional bytes seed = 2; + */ + com.google.protobuf.ByteString getSeed(); + } + /** + * Protobuf type {@code textsecure.SenderKeyStateStructure.SenderMessageKey} + */ + public static final class SenderMessageKey extends + com.google.protobuf.GeneratedMessage + implements SenderMessageKeyOrBuilder { + // Use SenderMessageKey.newBuilder() to construct. + private SenderMessageKey(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SenderMessageKey(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SenderMessageKey defaultInstance; + public static SenderMessageKey getDefaultInstance() { + return defaultInstance; + } + + public SenderMessageKey getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SenderMessageKey( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + iteration_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + seed_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.class, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SenderMessageKey parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SenderMessageKey(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 iteration = 1; + public static final int ITERATION_FIELD_NUMBER = 1; + private int iteration_; + /** + * optional uint32 iteration = 1; + */ + public boolean hasIteration() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 iteration = 1; + */ + public int getIteration() { + return iteration_; + } + + // optional bytes seed = 2; + public static final int SEED_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString seed_; + /** + * optional bytes seed = 2; + */ + public boolean hasSeed() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes seed = 2; + */ + public com.google.protobuf.ByteString getSeed() { + return seed_; + } + + private void initFields() { + iteration_ = 0; + seed_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, iteration_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, seed_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, iteration_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, seed_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SenderKeyStateStructure.SenderMessageKey} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKeyOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.class, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder.class); + } + + // Construct using org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + iteration_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + seed_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_descriptor; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey getDefaultInstanceForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.getDefaultInstance(); + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey build() { + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey buildPartial() { + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey result = new org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.iteration_ = iteration_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.seed_ = seed_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey) { + return mergeFrom((org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey other) { + if (other == org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.getDefaultInstance()) return this; + if (other.hasIteration()) { + setIteration(other.getIteration()); + } + if (other.hasSeed()) { + setSeed(other.getSeed()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 iteration = 1; + private int iteration_ ; + /** + * optional uint32 iteration = 1; + */ + public boolean hasIteration() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 iteration = 1; + */ + public int getIteration() { + return iteration_; + } + /** + * optional uint32 iteration = 1; + */ + public Builder setIteration(int value) { + bitField0_ |= 0x00000001; + iteration_ = value; + onChanged(); + return this; + } + /** + * optional uint32 iteration = 1; + */ + public Builder clearIteration() { + bitField0_ = (bitField0_ & ~0x00000001); + iteration_ = 0; + onChanged(); + return this; + } + + // optional bytes seed = 2; + private com.google.protobuf.ByteString seed_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes seed = 2; + */ + public boolean hasSeed() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes seed = 2; + */ + public com.google.protobuf.ByteString getSeed() { + return seed_; + } + /** + * optional bytes seed = 2; + */ + public Builder setSeed(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + seed_ = value; + onChanged(); + return this; + } + /** + * optional bytes seed = 2; + */ + public Builder clearSeed() { + bitField0_ = (bitField0_ & ~0x00000002); + seed_ = getDefaultInstance().getSeed(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SenderKeyStateStructure.SenderMessageKey) + } + + static { + defaultInstance = new SenderMessageKey(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SenderKeyStateStructure.SenderMessageKey) + } + + public interface SenderSigningKeyOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes public = 1; + /** + * optional bytes public = 1; + */ + boolean hasPublic(); + /** + * optional bytes public = 1; + */ + com.google.protobuf.ByteString getPublic(); + + // optional bytes private = 2; + /** + * optional bytes private = 2; + */ + boolean hasPrivate(); + /** + * optional bytes private = 2; + */ + com.google.protobuf.ByteString getPrivate(); + } + /** + * Protobuf type {@code textsecure.SenderKeyStateStructure.SenderSigningKey} + */ + public static final class SenderSigningKey extends + com.google.protobuf.GeneratedMessage + implements SenderSigningKeyOrBuilder { + // Use SenderSigningKey.newBuilder() to construct. + private SenderSigningKey(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SenderSigningKey(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SenderSigningKey defaultInstance; + public static SenderSigningKey getDefaultInstance() { + return defaultInstance; + } + + public SenderSigningKey getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SenderSigningKey( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + public_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + private_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.class, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SenderSigningKey parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SenderSigningKey(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional bytes public = 1; + public static final int PUBLIC_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString public_; + /** + * optional bytes public = 1; + */ + public boolean hasPublic() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes public = 1; + */ + public com.google.protobuf.ByteString getPublic() { + return public_; + } + + // optional bytes private = 2; + public static final int PRIVATE_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString private_; + /** + * optional bytes private = 2; + */ + public boolean hasPrivate() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes private = 2; + */ + public com.google.protobuf.ByteString getPrivate() { + return private_; + } + + private void initFields() { + public_ = com.google.protobuf.ByteString.EMPTY; + private_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, public_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, private_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, public_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, private_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SenderKeyStateStructure.SenderSigningKey} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKeyOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.class, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder.class); + } + + // Construct using org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + public_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + private_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_descriptor; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey getDefaultInstanceForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.getDefaultInstance(); + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey build() { + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey buildPartial() { + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey result = new org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.public_ = public_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.private_ = private_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey) { + return mergeFrom((org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey other) { + if (other == org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.getDefaultInstance()) return this; + if (other.hasPublic()) { + setPublic(other.getPublic()); + } + if (other.hasPrivate()) { + setPrivate(other.getPrivate()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes public = 1; + private com.google.protobuf.ByteString public_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes public = 1; + */ + public boolean hasPublic() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes public = 1; + */ + public com.google.protobuf.ByteString getPublic() { + return public_; + } + /** + * optional bytes public = 1; + */ + public Builder setPublic(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + public_ = value; + onChanged(); + return this; + } + /** + * optional bytes public = 1; + */ + public Builder clearPublic() { + bitField0_ = (bitField0_ & ~0x00000001); + public_ = getDefaultInstance().getPublic(); + onChanged(); + return this; + } + + // optional bytes private = 2; + private com.google.protobuf.ByteString private_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes private = 2; + */ + public boolean hasPrivate() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes private = 2; + */ + public com.google.protobuf.ByteString getPrivate() { + return private_; + } + /** + * optional bytes private = 2; + */ + public Builder setPrivate(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + private_ = value; + onChanged(); + return this; + } + /** + * optional bytes private = 2; + */ + public Builder clearPrivate() { + bitField0_ = (bitField0_ & ~0x00000002); + private_ = getDefaultInstance().getPrivate(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SenderKeyStateStructure.SenderSigningKey) + } + + static { + defaultInstance = new SenderSigningKey(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SenderKeyStateStructure.SenderSigningKey) + } + + private int bitField0_; + // optional uint32 senderKeyId = 1; + public static final int SENDERKEYID_FIELD_NUMBER = 1; + private int senderKeyId_; + /** + * optional uint32 senderKeyId = 1; + */ + public boolean hasSenderKeyId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 senderKeyId = 1; + */ + public int getSenderKeyId() { + return senderKeyId_; + } + + // optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + public static final int SENDERCHAINKEY_FIELD_NUMBER = 2; + private org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey senderChainKey_; + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public boolean hasSenderChainKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey getSenderChainKey() { + return senderChainKey_; + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKeyOrBuilder getSenderChainKeyOrBuilder() { + return senderChainKey_; + } + + // optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + public static final int SENDERSIGNINGKEY_FIELD_NUMBER = 3; + private org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey senderSigningKey_; + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public boolean hasSenderSigningKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey getSenderSigningKey() { + return senderSigningKey_; + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKeyOrBuilder getSenderSigningKeyOrBuilder() { + return senderSigningKey_; + } + + // repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + public static final int SENDERMESSAGEKEYS_FIELD_NUMBER = 4; + private java.util.List senderMessageKeys_; + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public java.util.List getSenderMessageKeysList() { + return senderMessageKeys_; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public java.util.List + getSenderMessageKeysOrBuilderList() { + return senderMessageKeys_; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public int getSenderMessageKeysCount() { + return senderMessageKeys_.size(); + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey getSenderMessageKeys(int index) { + return senderMessageKeys_.get(index); + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKeyOrBuilder getSenderMessageKeysOrBuilder( + int index) { + return senderMessageKeys_.get(index); + } + + private void initFields() { + senderKeyId_ = 0; + senderChainKey_ = org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.getDefaultInstance(); + senderSigningKey_ = org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.getDefaultInstance(); + senderMessageKeys_ = java.util.Collections.emptyList(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, senderKeyId_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeMessage(2, senderChainKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeMessage(3, senderSigningKey_); + } + for (int i = 0; i < senderMessageKeys_.size(); i++) { + output.writeMessage(4, senderMessageKeys_.get(i)); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, senderKeyId_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, senderChainKey_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, senderSigningKey_); + } + for (int i = 0; i < senderMessageKeys_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, senderMessageKeys_.get(i)); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SenderKeyStateStructure} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructureOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.class, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.Builder.class); + } + + // Construct using org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getSenderChainKeyFieldBuilder(); + getSenderSigningKeyFieldBuilder(); + getSenderMessageKeysFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + senderKeyId_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + if (senderChainKeyBuilder_ == null) { + senderChainKey_ = org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.getDefaultInstance(); + } else { + senderChainKeyBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + if (senderSigningKeyBuilder_ == null) { + senderSigningKey_ = org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.getDefaultInstance(); + } else { + senderSigningKeyBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + if (senderMessageKeysBuilder_ == null) { + senderMessageKeys_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + } else { + senderMessageKeysBuilder_.clear(); + } + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyStateStructure_descriptor; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure getDefaultInstanceForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.getDefaultInstance(); + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure build() { + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure buildPartial() { + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure result = new org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.senderKeyId_ = senderKeyId_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + if (senderChainKeyBuilder_ == null) { + result.senderChainKey_ = senderChainKey_; + } else { + result.senderChainKey_ = senderChainKeyBuilder_.build(); + } + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + if (senderSigningKeyBuilder_ == null) { + result.senderSigningKey_ = senderSigningKey_; + } else { + result.senderSigningKey_ = senderSigningKeyBuilder_.build(); + } + if (senderMessageKeysBuilder_ == null) { + if (((bitField0_ & 0x00000008) == 0x00000008)) { + senderMessageKeys_ = java.util.Collections.unmodifiableList(senderMessageKeys_); + bitField0_ = (bitField0_ & ~0x00000008); + } + result.senderMessageKeys_ = senderMessageKeys_; + } else { + result.senderMessageKeys_ = senderMessageKeysBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure) { + return mergeFrom((org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure other) { + if (other == org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.getDefaultInstance()) return this; + if (other.hasSenderKeyId()) { + setSenderKeyId(other.getSenderKeyId()); + } + if (other.hasSenderChainKey()) { + mergeSenderChainKey(other.getSenderChainKey()); + } + if (other.hasSenderSigningKey()) { + mergeSenderSigningKey(other.getSenderSigningKey()); + } + if (senderMessageKeysBuilder_ == null) { + if (!other.senderMessageKeys_.isEmpty()) { + if (senderMessageKeys_.isEmpty()) { + senderMessageKeys_ = other.senderMessageKeys_; + bitField0_ = (bitField0_ & ~0x00000008); + } else { + ensureSenderMessageKeysIsMutable(); + senderMessageKeys_.addAll(other.senderMessageKeys_); + } + onChanged(); + } + } else { + if (!other.senderMessageKeys_.isEmpty()) { + if (senderMessageKeysBuilder_.isEmpty()) { + senderMessageKeysBuilder_.dispose(); + senderMessageKeysBuilder_ = null; + senderMessageKeys_ = other.senderMessageKeys_; + bitField0_ = (bitField0_ & ~0x00000008); + senderMessageKeysBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getSenderMessageKeysFieldBuilder() : null; + } else { + senderMessageKeysBuilder_.addAllMessages(other.senderMessageKeys_); + } + } + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 senderKeyId = 1; + private int senderKeyId_ ; + /** + * optional uint32 senderKeyId = 1; + */ + public boolean hasSenderKeyId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 senderKeyId = 1; + */ + public int getSenderKeyId() { + return senderKeyId_; + } + /** + * optional uint32 senderKeyId = 1; + */ + public Builder setSenderKeyId(int value) { + bitField0_ |= 0x00000001; + senderKeyId_ = value; + onChanged(); + return this; + } + /** + * optional uint32 senderKeyId = 1; + */ + public Builder clearSenderKeyId() { + bitField0_ = (bitField0_ & ~0x00000001); + senderKeyId_ = 0; + onChanged(); + return this; + } + + // optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + private org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey senderChainKey_ = org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKeyOrBuilder> senderChainKeyBuilder_; + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public boolean hasSenderChainKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey getSenderChainKey() { + if (senderChainKeyBuilder_ == null) { + return senderChainKey_; + } else { + return senderChainKeyBuilder_.getMessage(); + } + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public Builder setSenderChainKey(org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey value) { + if (senderChainKeyBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + senderChainKey_ = value; + onChanged(); + } else { + senderChainKeyBuilder_.setMessage(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public Builder setSenderChainKey( + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder builderForValue) { + if (senderChainKeyBuilder_ == null) { + senderChainKey_ = builderForValue.build(); + onChanged(); + } else { + senderChainKeyBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public Builder mergeSenderChainKey(org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey value) { + if (senderChainKeyBuilder_ == null) { + if (((bitField0_ & 0x00000002) == 0x00000002) && + senderChainKey_ != org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.getDefaultInstance()) { + senderChainKey_ = + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.newBuilder(senderChainKey_).mergeFrom(value).buildPartial(); + } else { + senderChainKey_ = value; + } + onChanged(); + } else { + senderChainKeyBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public Builder clearSenderChainKey() { + if (senderChainKeyBuilder_ == null) { + senderChainKey_ = org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.getDefaultInstance(); + onChanged(); + } else { + senderChainKeyBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder getSenderChainKeyBuilder() { + bitField0_ |= 0x00000002; + onChanged(); + return getSenderChainKeyFieldBuilder().getBuilder(); + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKeyOrBuilder getSenderChainKeyOrBuilder() { + if (senderChainKeyBuilder_ != null) { + return senderChainKeyBuilder_.getMessageOrBuilder(); + } else { + return senderChainKey_; + } + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderChainKey senderChainKey = 2; + */ + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKeyOrBuilder> + getSenderChainKeyFieldBuilder() { + if (senderChainKeyBuilder_ == null) { + senderChainKeyBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKey.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderChainKeyOrBuilder>( + senderChainKey_, + getParentForChildren(), + isClean()); + senderChainKey_ = null; + } + return senderChainKeyBuilder_; + } + + // optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + private org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey senderSigningKey_ = org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKeyOrBuilder> senderSigningKeyBuilder_; + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public boolean hasSenderSigningKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey getSenderSigningKey() { + if (senderSigningKeyBuilder_ == null) { + return senderSigningKey_; + } else { + return senderSigningKeyBuilder_.getMessage(); + } + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public Builder setSenderSigningKey(org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey value) { + if (senderSigningKeyBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + senderSigningKey_ = value; + onChanged(); + } else { + senderSigningKeyBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public Builder setSenderSigningKey( + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder builderForValue) { + if (senderSigningKeyBuilder_ == null) { + senderSigningKey_ = builderForValue.build(); + onChanged(); + } else { + senderSigningKeyBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public Builder mergeSenderSigningKey(org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey value) { + if (senderSigningKeyBuilder_ == null) { + if (((bitField0_ & 0x00000004) == 0x00000004) && + senderSigningKey_ != org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.getDefaultInstance()) { + senderSigningKey_ = + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.newBuilder(senderSigningKey_).mergeFrom(value).buildPartial(); + } else { + senderSigningKey_ = value; + } + onChanged(); + } else { + senderSigningKeyBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public Builder clearSenderSigningKey() { + if (senderSigningKeyBuilder_ == null) { + senderSigningKey_ = org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.getDefaultInstance(); + onChanged(); + } else { + senderSigningKeyBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder getSenderSigningKeyBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return getSenderSigningKeyFieldBuilder().getBuilder(); + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKeyOrBuilder getSenderSigningKeyOrBuilder() { + if (senderSigningKeyBuilder_ != null) { + return senderSigningKeyBuilder_.getMessageOrBuilder(); + } else { + return senderSigningKey_; + } + } + /** + * optional .textsecure.SenderKeyStateStructure.SenderSigningKey senderSigningKey = 3; + */ + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKeyOrBuilder> + getSenderSigningKeyFieldBuilder() { + if (senderSigningKeyBuilder_ == null) { + senderSigningKeyBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKey.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderSigningKeyOrBuilder>( + senderSigningKey_, + getParentForChildren(), + isClean()); + senderSigningKey_ = null; + } + return senderSigningKeyBuilder_; + } + + // repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + private java.util.List senderMessageKeys_ = + java.util.Collections.emptyList(); + private void ensureSenderMessageKeysIsMutable() { + if (!((bitField0_ & 0x00000008) == 0x00000008)) { + senderMessageKeys_ = new java.util.ArrayList(senderMessageKeys_); + bitField0_ |= 0x00000008; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKeyOrBuilder> senderMessageKeysBuilder_; + + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public java.util.List getSenderMessageKeysList() { + if (senderMessageKeysBuilder_ == null) { + return java.util.Collections.unmodifiableList(senderMessageKeys_); + } else { + return senderMessageKeysBuilder_.getMessageList(); + } + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public int getSenderMessageKeysCount() { + if (senderMessageKeysBuilder_ == null) { + return senderMessageKeys_.size(); + } else { + return senderMessageKeysBuilder_.getCount(); + } + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey getSenderMessageKeys(int index) { + if (senderMessageKeysBuilder_ == null) { + return senderMessageKeys_.get(index); + } else { + return senderMessageKeysBuilder_.getMessage(index); + } + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public Builder setSenderMessageKeys( + int index, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey value) { + if (senderMessageKeysBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSenderMessageKeysIsMutable(); + senderMessageKeys_.set(index, value); + onChanged(); + } else { + senderMessageKeysBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public Builder setSenderMessageKeys( + int index, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder builderForValue) { + if (senderMessageKeysBuilder_ == null) { + ensureSenderMessageKeysIsMutable(); + senderMessageKeys_.set(index, builderForValue.build()); + onChanged(); + } else { + senderMessageKeysBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public Builder addSenderMessageKeys(org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey value) { + if (senderMessageKeysBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSenderMessageKeysIsMutable(); + senderMessageKeys_.add(value); + onChanged(); + } else { + senderMessageKeysBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public Builder addSenderMessageKeys( + int index, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey value) { + if (senderMessageKeysBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSenderMessageKeysIsMutable(); + senderMessageKeys_.add(index, value); + onChanged(); + } else { + senderMessageKeysBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public Builder addSenderMessageKeys( + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder builderForValue) { + if (senderMessageKeysBuilder_ == null) { + ensureSenderMessageKeysIsMutable(); + senderMessageKeys_.add(builderForValue.build()); + onChanged(); + } else { + senderMessageKeysBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public Builder addSenderMessageKeys( + int index, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder builderForValue) { + if (senderMessageKeysBuilder_ == null) { + ensureSenderMessageKeysIsMutable(); + senderMessageKeys_.add(index, builderForValue.build()); + onChanged(); + } else { + senderMessageKeysBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public Builder addAllSenderMessageKeys( + java.lang.Iterable values) { + if (senderMessageKeysBuilder_ == null) { + ensureSenderMessageKeysIsMutable(); + super.addAll(values, senderMessageKeys_); + onChanged(); + } else { + senderMessageKeysBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public Builder clearSenderMessageKeys() { + if (senderMessageKeysBuilder_ == null) { + senderMessageKeys_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); + } else { + senderMessageKeysBuilder_.clear(); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public Builder removeSenderMessageKeys(int index) { + if (senderMessageKeysBuilder_ == null) { + ensureSenderMessageKeysIsMutable(); + senderMessageKeys_.remove(index); + onChanged(); + } else { + senderMessageKeysBuilder_.remove(index); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder getSenderMessageKeysBuilder( + int index) { + return getSenderMessageKeysFieldBuilder().getBuilder(index); + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKeyOrBuilder getSenderMessageKeysOrBuilder( + int index) { + if (senderMessageKeysBuilder_ == null) { + return senderMessageKeys_.get(index); } else { + return senderMessageKeysBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public java.util.List + getSenderMessageKeysOrBuilderList() { + if (senderMessageKeysBuilder_ != null) { + return senderMessageKeysBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(senderMessageKeys_); + } + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder addSenderMessageKeysBuilder() { + return getSenderMessageKeysFieldBuilder().addBuilder( + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.getDefaultInstance()); + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder addSenderMessageKeysBuilder( + int index) { + return getSenderMessageKeysFieldBuilder().addBuilder( + index, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.getDefaultInstance()); + } + /** + * repeated .textsecure.SenderKeyStateStructure.SenderMessageKey senderMessageKeys = 4; + */ + public java.util.List + getSenderMessageKeysBuilderList() { + return getSenderMessageKeysFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKeyOrBuilder> + getSenderMessageKeysFieldBuilder() { + if (senderMessageKeysBuilder_ == null) { + senderMessageKeysBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKey.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.SenderMessageKeyOrBuilder>( + senderMessageKeys_, + ((bitField0_ & 0x00000008) == 0x00000008), + getParentForChildren(), + isClean()); + senderMessageKeys_ = null; + } + return senderMessageKeysBuilder_; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SenderKeyStateStructure) + } + + static { + defaultInstance = new SenderKeyStateStructure(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SenderKeyStateStructure) + } + + public interface SenderKeyRecordStructureOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + java.util.List + getSenderKeyStatesList(); + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure getSenderKeyStates(int index); + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + int getSenderKeyStatesCount(); + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + java.util.List + getSenderKeyStatesOrBuilderList(); + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructureOrBuilder getSenderKeyStatesOrBuilder( + int index); + } + /** + * Protobuf type {@code textsecure.SenderKeyRecordStructure} + */ + public static final class SenderKeyRecordStructure extends + com.google.protobuf.GeneratedMessage + implements SenderKeyRecordStructureOrBuilder { + // Use SenderKeyRecordStructure.newBuilder() to construct. + private SenderKeyRecordStructure(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SenderKeyRecordStructure(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SenderKeyRecordStructure defaultInstance; + public static SenderKeyRecordStructure getDefaultInstance() { + return defaultInstance; + } + + public SenderKeyRecordStructure getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SenderKeyRecordStructure( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + if (!((mutable_bitField0_ & 0x00000001) == 0x00000001)) { + senderKeyStates_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000001; + } + senderKeyStates_.add(input.readMessage(org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.PARSER, extensionRegistry)); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) == 0x00000001)) { + senderKeyStates_ = java.util.Collections.unmodifiableList(senderKeyStates_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyRecordStructure_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyRecordStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure.class, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SenderKeyRecordStructure parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SenderKeyRecordStructure(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + // repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + public static final int SENDERKEYSTATES_FIELD_NUMBER = 1; + private java.util.List senderKeyStates_; + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public java.util.List getSenderKeyStatesList() { + return senderKeyStates_; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public java.util.List + getSenderKeyStatesOrBuilderList() { + return senderKeyStates_; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public int getSenderKeyStatesCount() { + return senderKeyStates_.size(); + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure getSenderKeyStates(int index) { + return senderKeyStates_.get(index); + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructureOrBuilder getSenderKeyStatesOrBuilder( + int index) { + return senderKeyStates_.get(index); + } + + private void initFields() { + senderKeyStates_ = java.util.Collections.emptyList(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + for (int i = 0; i < senderKeyStates_.size(); i++) { + output.writeMessage(1, senderKeyStates_.get(i)); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + for (int i = 0; i < senderKeyStates_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, senderKeyStates_.get(i)); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.SenderKeyRecordStructure} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructureOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyRecordStructure_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyRecordStructure_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure.class, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure.Builder.class); + } + + // Construct using org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getSenderKeyStatesFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + if (senderKeyStatesBuilder_ == null) { + senderKeyStates_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + } else { + senderKeyStatesBuilder_.clear(); + } + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_SenderKeyRecordStructure_descriptor; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure getDefaultInstanceForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure.getDefaultInstance(); + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure build() { + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure buildPartial() { + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure result = new org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure(this); + int from_bitField0_ = bitField0_; + if (senderKeyStatesBuilder_ == null) { + if (((bitField0_ & 0x00000001) == 0x00000001)) { + senderKeyStates_ = java.util.Collections.unmodifiableList(senderKeyStates_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.senderKeyStates_ = senderKeyStates_; + } else { + result.senderKeyStates_ = senderKeyStatesBuilder_.build(); + } + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure) { + return mergeFrom((org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure other) { + if (other == org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure.getDefaultInstance()) return this; + if (senderKeyStatesBuilder_ == null) { + if (!other.senderKeyStates_.isEmpty()) { + if (senderKeyStates_.isEmpty()) { + senderKeyStates_ = other.senderKeyStates_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureSenderKeyStatesIsMutable(); + senderKeyStates_.addAll(other.senderKeyStates_); + } + onChanged(); + } + } else { + if (!other.senderKeyStates_.isEmpty()) { + if (senderKeyStatesBuilder_.isEmpty()) { + senderKeyStatesBuilder_.dispose(); + senderKeyStatesBuilder_ = null; + senderKeyStates_ = other.senderKeyStates_; + bitField0_ = (bitField0_ & ~0x00000001); + senderKeyStatesBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getSenderKeyStatesFieldBuilder() : null; + } else { + senderKeyStatesBuilder_.addAllMessages(other.senderKeyStates_); + } + } + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + private java.util.List senderKeyStates_ = + java.util.Collections.emptyList(); + private void ensureSenderKeyStatesIsMutable() { + if (!((bitField0_ & 0x00000001) == 0x00000001)) { + senderKeyStates_ = new java.util.ArrayList(senderKeyStates_); + bitField0_ |= 0x00000001; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructureOrBuilder> senderKeyStatesBuilder_; + + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public java.util.List getSenderKeyStatesList() { + if (senderKeyStatesBuilder_ == null) { + return java.util.Collections.unmodifiableList(senderKeyStates_); + } else { + return senderKeyStatesBuilder_.getMessageList(); + } + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public int getSenderKeyStatesCount() { + if (senderKeyStatesBuilder_ == null) { + return senderKeyStates_.size(); + } else { + return senderKeyStatesBuilder_.getCount(); + } + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure getSenderKeyStates(int index) { + if (senderKeyStatesBuilder_ == null) { + return senderKeyStates_.get(index); + } else { + return senderKeyStatesBuilder_.getMessage(index); + } + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public Builder setSenderKeyStates( + int index, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure value) { + if (senderKeyStatesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSenderKeyStatesIsMutable(); + senderKeyStates_.set(index, value); + onChanged(); + } else { + senderKeyStatesBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public Builder setSenderKeyStates( + int index, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.Builder builderForValue) { + if (senderKeyStatesBuilder_ == null) { + ensureSenderKeyStatesIsMutable(); + senderKeyStates_.set(index, builderForValue.build()); + onChanged(); + } else { + senderKeyStatesBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public Builder addSenderKeyStates(org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure value) { + if (senderKeyStatesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSenderKeyStatesIsMutable(); + senderKeyStates_.add(value); + onChanged(); + } else { + senderKeyStatesBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public Builder addSenderKeyStates( + int index, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure value) { + if (senderKeyStatesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSenderKeyStatesIsMutable(); + senderKeyStates_.add(index, value); + onChanged(); + } else { + senderKeyStatesBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public Builder addSenderKeyStates( + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.Builder builderForValue) { + if (senderKeyStatesBuilder_ == null) { + ensureSenderKeyStatesIsMutable(); + senderKeyStates_.add(builderForValue.build()); + onChanged(); + } else { + senderKeyStatesBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public Builder addSenderKeyStates( + int index, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.Builder builderForValue) { + if (senderKeyStatesBuilder_ == null) { + ensureSenderKeyStatesIsMutable(); + senderKeyStates_.add(index, builderForValue.build()); + onChanged(); + } else { + senderKeyStatesBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public Builder addAllSenderKeyStates( + java.lang.Iterable values) { + if (senderKeyStatesBuilder_ == null) { + ensureSenderKeyStatesIsMutable(); + super.addAll(values, senderKeyStates_); + onChanged(); + } else { + senderKeyStatesBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public Builder clearSenderKeyStates() { + if (senderKeyStatesBuilder_ == null) { + senderKeyStates_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + } else { + senderKeyStatesBuilder_.clear(); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public Builder removeSenderKeyStates(int index) { + if (senderKeyStatesBuilder_ == null) { + ensureSenderKeyStatesIsMutable(); + senderKeyStates_.remove(index); + onChanged(); + } else { + senderKeyStatesBuilder_.remove(index); + } + return this; + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.Builder getSenderKeyStatesBuilder( + int index) { + return getSenderKeyStatesFieldBuilder().getBuilder(index); + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructureOrBuilder getSenderKeyStatesOrBuilder( + int index) { + if (senderKeyStatesBuilder_ == null) { + return senderKeyStates_.get(index); } else { + return senderKeyStatesBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public java.util.List + getSenderKeyStatesOrBuilderList() { + if (senderKeyStatesBuilder_ != null) { + return senderKeyStatesBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(senderKeyStates_); + } + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.Builder addSenderKeyStatesBuilder() { + return getSenderKeyStatesFieldBuilder().addBuilder( + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.getDefaultInstance()); + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.Builder addSenderKeyStatesBuilder( + int index) { + return getSenderKeyStatesFieldBuilder().addBuilder( + index, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.getDefaultInstance()); + } + /** + * repeated .textsecure.SenderKeyStateStructure senderKeyStates = 1; + */ + public java.util.List + getSenderKeyStatesBuilderList() { + return getSenderKeyStatesFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructureOrBuilder> + getSenderKeyStatesFieldBuilder() { + if (senderKeyStatesBuilder_ == null) { + senderKeyStatesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure.Builder, org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructureOrBuilder>( + senderKeyStates_, + ((bitField0_ & 0x00000001) == 0x00000001), + getParentForChildren(), + isClean()); + senderKeyStates_ = null; + } + return senderKeyStatesBuilder_; + } + + // @@protoc_insertion_point(builder_scope:textsecure.SenderKeyRecordStructure) + } + + static { + defaultInstance = new SenderKeyRecordStructure(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.SenderKeyRecordStructure) + } + + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SessionStructure_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SessionStructure_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SessionStructure_Chain_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SessionStructure_Chain_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SessionStructure_Chain_ChainKey_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SessionStructure_Chain_ChainKey_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SessionStructure_Chain_MessageKey_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SessionStructure_Chain_MessageKey_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SessionStructure_PendingKeyExchange_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SessionStructure_PendingKeyExchange_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SessionStructure_PendingPreKey_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SessionStructure_PendingPreKey_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_RecordStructure_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_RecordStructure_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_PreKeyRecordStructure_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_PreKeyRecordStructure_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SignedPreKeyRecordStructure_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SignedPreKeyRecordStructure_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_IdentityKeyPairStructure_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_IdentityKeyPairStructure_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SenderKeyStateStructure_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SenderKeyStateStructure_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_SenderKeyRecordStructure_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_SenderKeyRecordStructure_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\032LocalStorageProtocol.proto\022\ntextsecure" + + "\"\323\010\n\020SessionStructure\022\026\n\016sessionVersion\030" + + "\001 \001(\r\022\033\n\023localIdentityPublic\030\002 \001(\014\022\034\n\024re" + + "moteIdentityPublic\030\003 \001(\014\022\017\n\007rootKey\030\004 \001(" + + "\014\022\027\n\017previousCounter\030\005 \001(\r\0227\n\013senderChai" + + "n\030\006 \001(\0132\".textsecure.SessionStructure.Ch" + + "ain\022:\n\016receiverChains\030\007 \003(\0132\".textsecure" + + ".SessionStructure.Chain\022K\n\022pendingKeyExc" + + "hange\030\010 \001(\0132/.textsecure.SessionStructur" + + "e.PendingKeyExchange\022A\n\rpendingPreKey\030\t ", + "\001(\0132*.textsecure.SessionStructure.Pendin" + + "gPreKey\022\034\n\024remoteRegistrationId\030\n \001(\r\022\033\n" + + "\023localRegistrationId\030\013 \001(\r\022\024\n\014needsRefre" + + "sh\030\014 \001(\010\022\024\n\014aliceBaseKey\030\r \001(\014\032\271\002\n\005Chain" + + "\022\030\n\020senderRatchetKey\030\001 \001(\014\022\037\n\027senderRatc" + + "hetKeyPrivate\030\002 \001(\014\022=\n\010chainKey\030\003 \001(\0132+." + + "textsecure.SessionStructure.Chain.ChainK" + + "ey\022B\n\013messageKeys\030\004 \003(\0132-.textsecure.Ses" + + "sionStructure.Chain.MessageKey\032&\n\010ChainK" + + "ey\022\r\n\005index\030\001 \001(\r\022\013\n\003key\030\002 \001(\014\032J\n\nMessag", + "eKey\022\r\n\005index\030\001 \001(\r\022\021\n\tcipherKey\030\002 \001(\014\022\016" + + "\n\006macKey\030\003 \001(\014\022\n\n\002iv\030\004 \001(\014\032\315\001\n\022PendingKe" + + "yExchange\022\020\n\010sequence\030\001 \001(\r\022\024\n\014localBase" + + "Key\030\002 \001(\014\022\033\n\023localBaseKeyPrivate\030\003 \001(\014\022\027" + + "\n\017localRatchetKey\030\004 \001(\014\022\036\n\026localRatchetK" + + "eyPrivate\030\005 \001(\014\022\030\n\020localIdentityKey\030\007 \001(" + + "\014\022\037\n\027localIdentityKeyPrivate\030\010 \001(\014\032J\n\rPe" + + "ndingPreKey\022\020\n\010preKeyId\030\001 \001(\r\022\026\n\016signedP" + + "reKeyId\030\003 \001(\005\022\017\n\007baseKey\030\002 \001(\014\"\177\n\017Record" + + "Structure\0224\n\016currentSession\030\001 \001(\0132\034.text", + "secure.SessionStructure\0226\n\020previousSessi" + + "ons\030\002 \003(\0132\034.textsecure.SessionStructure\"" + + "J\n\025PreKeyRecordStructure\022\n\n\002id\030\001 \001(\r\022\021\n\t" + + "publicKey\030\002 \001(\014\022\022\n\nprivateKey\030\003 \001(\014\"v\n\033S" + + "ignedPreKeyRecordStructure\022\n\n\002id\030\001 \001(\r\022\021" + + "\n\tpublicKey\030\002 \001(\014\022\022\n\nprivateKey\030\003 \001(\014\022\021\n" + + "\tsignature\030\004 \001(\014\022\021\n\ttimestamp\030\005 \001(\006\"A\n\030I" + + "dentityKeyPairStructure\022\021\n\tpublicKey\030\001 \001" + + "(\014\022\022\n\nprivateKey\030\002 \001(\014\"\270\003\n\027SenderKeyStat" + + "eStructure\022\023\n\013senderKeyId\030\001 \001(\r\022J\n\016sende", + "rChainKey\030\002 \001(\01322.textsecure.SenderKeySt" + + "ateStructure.SenderChainKey\022N\n\020senderSig" + + "ningKey\030\003 \001(\01324.textsecure.SenderKeyStat" + + "eStructure.SenderSigningKey\022O\n\021senderMes" + + "sageKeys\030\004 \003(\01324.textsecure.SenderKeySta" + + "teStructure.SenderMessageKey\0321\n\016SenderCh" + + "ainKey\022\021\n\titeration\030\001 \001(\r\022\014\n\004seed\030\002 \001(\014\032" + + "3\n\020SenderMessageKey\022\021\n\titeration\030\001 \001(\r\022\014" + + "\n\004seed\030\002 \001(\014\0323\n\020SenderSigningKey\022\016\n\006publ" + + "ic\030\001 \001(\014\022\017\n\007private\030\002 \001(\014\"X\n\030SenderKeyRe", + "cordStructure\022<\n\017senderKeyStates\030\001 \003(\0132#" + + ".textsecure.SenderKeyStateStructureB4\n#o" + + "rg.whispersystems.libaxolotl.stateB\rStor" + + "ageProtos" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + internal_static_textsecure_SessionStructure_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_textsecure_SessionStructure_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SessionStructure_descriptor, + new java.lang.String[] { "SessionVersion", "LocalIdentityPublic", "RemoteIdentityPublic", "RootKey", "PreviousCounter", "SenderChain", "ReceiverChains", "PendingKeyExchange", "PendingPreKey", "RemoteRegistrationId", "LocalRegistrationId", "NeedsRefresh", "AliceBaseKey", }); + internal_static_textsecure_SessionStructure_Chain_descriptor = + internal_static_textsecure_SessionStructure_descriptor.getNestedTypes().get(0); + internal_static_textsecure_SessionStructure_Chain_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SessionStructure_Chain_descriptor, + new java.lang.String[] { "SenderRatchetKey", "SenderRatchetKeyPrivate", "ChainKey", "MessageKeys", }); + internal_static_textsecure_SessionStructure_Chain_ChainKey_descriptor = + internal_static_textsecure_SessionStructure_Chain_descriptor.getNestedTypes().get(0); + internal_static_textsecure_SessionStructure_Chain_ChainKey_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SessionStructure_Chain_ChainKey_descriptor, + new java.lang.String[] { "Index", "Key", }); + internal_static_textsecure_SessionStructure_Chain_MessageKey_descriptor = + internal_static_textsecure_SessionStructure_Chain_descriptor.getNestedTypes().get(1); + internal_static_textsecure_SessionStructure_Chain_MessageKey_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SessionStructure_Chain_MessageKey_descriptor, + new java.lang.String[] { "Index", "CipherKey", "MacKey", "Iv", }); + internal_static_textsecure_SessionStructure_PendingKeyExchange_descriptor = + internal_static_textsecure_SessionStructure_descriptor.getNestedTypes().get(1); + internal_static_textsecure_SessionStructure_PendingKeyExchange_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SessionStructure_PendingKeyExchange_descriptor, + new java.lang.String[] { "Sequence", "LocalBaseKey", "LocalBaseKeyPrivate", "LocalRatchetKey", "LocalRatchetKeyPrivate", "LocalIdentityKey", "LocalIdentityKeyPrivate", }); + internal_static_textsecure_SessionStructure_PendingPreKey_descriptor = + internal_static_textsecure_SessionStructure_descriptor.getNestedTypes().get(2); + internal_static_textsecure_SessionStructure_PendingPreKey_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SessionStructure_PendingPreKey_descriptor, + new java.lang.String[] { "PreKeyId", "SignedPreKeyId", "BaseKey", }); + internal_static_textsecure_RecordStructure_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_textsecure_RecordStructure_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_RecordStructure_descriptor, + new java.lang.String[] { "CurrentSession", "PreviousSessions", }); + internal_static_textsecure_PreKeyRecordStructure_descriptor = + getDescriptor().getMessageTypes().get(2); + internal_static_textsecure_PreKeyRecordStructure_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_PreKeyRecordStructure_descriptor, + new java.lang.String[] { "Id", "PublicKey", "PrivateKey", }); + internal_static_textsecure_SignedPreKeyRecordStructure_descriptor = + getDescriptor().getMessageTypes().get(3); + internal_static_textsecure_SignedPreKeyRecordStructure_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SignedPreKeyRecordStructure_descriptor, + new java.lang.String[] { "Id", "PublicKey", "PrivateKey", "Signature", "Timestamp", }); + internal_static_textsecure_IdentityKeyPairStructure_descriptor = + getDescriptor().getMessageTypes().get(4); + internal_static_textsecure_IdentityKeyPairStructure_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_IdentityKeyPairStructure_descriptor, + new java.lang.String[] { "PublicKey", "PrivateKey", }); + internal_static_textsecure_SenderKeyStateStructure_descriptor = + getDescriptor().getMessageTypes().get(5); + internal_static_textsecure_SenderKeyStateStructure_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SenderKeyStateStructure_descriptor, + new java.lang.String[] { "SenderKeyId", "SenderChainKey", "SenderSigningKey", "SenderMessageKeys", }); + internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_descriptor = + internal_static_textsecure_SenderKeyStateStructure_descriptor.getNestedTypes().get(0); + internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SenderKeyStateStructure_SenderChainKey_descriptor, + new java.lang.String[] { "Iteration", "Seed", }); + internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_descriptor = + internal_static_textsecure_SenderKeyStateStructure_descriptor.getNestedTypes().get(1); + internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SenderKeyStateStructure_SenderMessageKey_descriptor, + new java.lang.String[] { "Iteration", "Seed", }); + internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_descriptor = + internal_static_textsecure_SenderKeyStateStructure_descriptor.getNestedTypes().get(2); + internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SenderKeyStateStructure_SenderSigningKey_descriptor, + new java.lang.String[] { "Public", "Private", }); + internal_static_textsecure_SenderKeyRecordStructure_descriptor = + getDescriptor().getMessageTypes().get(6); + internal_static_textsecure_SenderKeyRecordStructure_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_SenderKeyRecordStructure_descriptor, + new java.lang.String[] { "SenderKeyStates", }); + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/src/main/java/org/whispersystems/libaxolotl/util/ByteUtil.java b/src/main/java/org/whispersystems/libaxolotl/util/ByteUtil.java new file mode 100644 index 000000000..c213ba0bb --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/util/ByteUtil.java @@ -0,0 +1,248 @@ +/** + * Copyright (C) 2014 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.whispersystems.libaxolotl.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.text.ParseException; + +public class ByteUtil { + + public static byte[] combine(byte[]... elements) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + for (byte[] element : elements) { + baos.write(element); + } + + return baos.toByteArray(); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + public static byte[][] split(byte[] input, int firstLength, int secondLength) { + byte[][] parts = new byte[2][]; + + parts[0] = new byte[firstLength]; + System.arraycopy(input, 0, parts[0], 0, firstLength); + + parts[1] = new byte[secondLength]; + System.arraycopy(input, firstLength, parts[1], 0, secondLength); + + return parts; + } + + public static byte[][] split(byte[] input, int firstLength, int secondLength, int thirdLength) + throws ParseException + { + if (input == null || firstLength < 0 || secondLength < 0 || thirdLength < 0 || + input.length < firstLength + secondLength + thirdLength) + { + throw new ParseException("Input too small: " + (input == null ? null : Hex.toString(input)), 0); + } + + byte[][] parts = new byte[3][]; + + parts[0] = new byte[firstLength]; + System.arraycopy(input, 0, parts[0], 0, firstLength); + + parts[1] = new byte[secondLength]; + System.arraycopy(input, firstLength, parts[1], 0, secondLength); + + parts[2] = new byte[thirdLength]; + System.arraycopy(input, firstLength + secondLength, parts[2], 0, thirdLength); + + return parts; + } + + public static byte[] trim(byte[] input, int length) { + byte[] result = new byte[length]; + System.arraycopy(input, 0, result, 0, result.length); + + return result; + } + + public static byte[] copyFrom(byte[] input) { + byte[] output = new byte[input.length]; + System.arraycopy(input, 0, output, 0, output.length); + + return output; + } + + public static byte intsToByteHighAndLow(int highValue, int lowValue) { + return (byte)((highValue << 4 | lowValue) & 0xFF); + } + + public static int highBitsToInt(byte value) { + return (value & 0xFF) >> 4; + } + + public static int lowBitsToInt(byte value) { + return (value & 0xF); + } + + public static int highBitsToMedium(int value) { + return (value >> 12); + } + + public static int lowBitsToMedium(int value) { + return (value & 0xFFF); + } + + public static byte[] shortToByteArray(int value) { + byte[] bytes = new byte[2]; + shortToByteArray(bytes, 0, value); + return bytes; + } + + public static int shortToByteArray(byte[] bytes, int offset, int value) { + bytes[offset+1] = (byte)value; + bytes[offset] = (byte)(value >> 8); + return 2; + } + + public static int shortToLittleEndianByteArray(byte[] bytes, int offset, int value) { + bytes[offset] = (byte)value; + bytes[offset+1] = (byte)(value >> 8); + return 2; + } + + public static byte[] mediumToByteArray(int value) { + byte[] bytes = new byte[3]; + mediumToByteArray(bytes, 0, value); + return bytes; + } + + public static int mediumToByteArray(byte[] bytes, int offset, int value) { + bytes[offset + 2] = (byte)value; + bytes[offset + 1] = (byte)(value >> 8); + bytes[offset] = (byte)(value >> 16); + return 3; + } + + public static byte[] intToByteArray(int value) { + byte[] bytes = new byte[4]; + intToByteArray(bytes, 0, value); + return bytes; + } + + public static int intToByteArray(byte[] bytes, int offset, int value) { + bytes[offset + 3] = (byte)value; + bytes[offset + 2] = (byte)(value >> 8); + bytes[offset + 1] = (byte)(value >> 16); + bytes[offset] = (byte)(value >> 24); + return 4; + } + + public static int intToLittleEndianByteArray(byte[] bytes, int offset, int value) { + bytes[offset] = (byte)value; + bytes[offset+1] = (byte)(value >> 8); + bytes[offset+2] = (byte)(value >> 16); + bytes[offset+3] = (byte)(value >> 24); + return 4; + } + + public static byte[] longToByteArray(long l) { + byte[] bytes = new byte[8]; + longToByteArray(bytes, 0, l); + return bytes; + } + + public static int longToByteArray(byte[] bytes, int offset, long value) { + bytes[offset + 7] = (byte)value; + bytes[offset + 6] = (byte)(value >> 8); + bytes[offset + 5] = (byte)(value >> 16); + bytes[offset + 4] = (byte)(value >> 24); + bytes[offset + 3] = (byte)(value >> 32); + bytes[offset + 2] = (byte)(value >> 40); + bytes[offset + 1] = (byte)(value >> 48); + bytes[offset] = (byte)(value >> 56); + return 8; + } + + public static int longTo4ByteArray(byte[] bytes, int offset, long value) { + bytes[offset + 3] = (byte)value; + bytes[offset + 2] = (byte)(value >> 8); + bytes[offset + 1] = (byte)(value >> 16); + bytes[offset + 0] = (byte)(value >> 24); + return 4; + } + + public static int byteArrayToShort(byte[] bytes) { + return byteArrayToShort(bytes, 0); + } + + public static int byteArrayToShort(byte[] bytes, int offset) { + return + (bytes[offset] & 0xff) << 8 | (bytes[offset + 1] & 0xff); + } + + // The SSL patented 3-byte Value. + public static int byteArrayToMedium(byte[] bytes, int offset) { + return + (bytes[offset] & 0xff) << 16 | + (bytes[offset + 1] & 0xff) << 8 | + (bytes[offset + 2] & 0xff); + } + + public static int byteArrayToInt(byte[] bytes) { + return byteArrayToInt(bytes, 0); + } + + public static int byteArrayToInt(byte[] bytes, int offset) { + return + (bytes[offset] & 0xff) << 24 | + (bytes[offset + 1] & 0xff) << 16 | + (bytes[offset + 2] & 0xff) << 8 | + (bytes[offset + 3] & 0xff); + } + + public static int byteArrayToIntLittleEndian(byte[] bytes, int offset) { + return + (bytes[offset + 3] & 0xff) << 24 | + (bytes[offset + 2] & 0xff) << 16 | + (bytes[offset + 1] & 0xff) << 8 | + (bytes[offset] & 0xff); + } + + public static long byteArrayToLong(byte[] bytes) { + return byteArrayToLong(bytes, 0); + } + + public static long byteArray4ToLong(byte[] bytes, int offset) { + return + ((bytes[offset + 0] & 0xffL) << 24) | + ((bytes[offset + 1] & 0xffL) << 16) | + ((bytes[offset + 2] & 0xffL) << 8) | + ((bytes[offset + 3] & 0xffL)); + } + + public static long byteArrayToLong(byte[] bytes, int offset) { + return + ((bytes[offset] & 0xffL) << 56) | + ((bytes[offset + 1] & 0xffL) << 48) | + ((bytes[offset + 2] & 0xffL) << 40) | + ((bytes[offset + 3] & 0xffL) << 32) | + ((bytes[offset + 4] & 0xffL) << 24) | + ((bytes[offset + 5] & 0xffL) << 16) | + ((bytes[offset + 6] & 0xffL) << 8) | + ((bytes[offset + 7] & 0xffL)); + } + +} diff --git a/src/main/java/org/whispersystems/libaxolotl/util/Hex.java b/src/main/java/org/whispersystems/libaxolotl/util/Hex.java new file mode 100644 index 000000000..192854642 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/util/Hex.java @@ -0,0 +1,77 @@ +/** + * Copyright (C) 2014 Open Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.whispersystems.libaxolotl.util; + +import java.io.IOException; + +/** + * Utility for generating hex dumps. + */ +public class Hex { + + private final static char[] HEX_DIGITS = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + public static String toString(byte[] bytes) { + return toString(bytes, 0, bytes.length); + } + + public static String toString(byte[] bytes, int offset, int length) { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < length; i++) { + appendHexChar(buf, bytes[offset + i]); + buf.append(" "); + } + return buf.toString(); + } + + public static String toStringCondensed(byte[] bytes) { + StringBuffer buf = new StringBuffer(); + for (int i=0;i> 1]; + + for (int i = 0, j = 0; j < len; i++) { + int f = Character.digit(data[j], 16) << 4; + j++; + f = f | Character.digit(data[j], 16); + j++; + out[i] = (byte) (f & 0xFF); + } + + return out; + } + + private static void appendHexChar(StringBuffer buf, int b) { + buf.append(HEX_DIGITS[(b >> 4) & 0xf]); + buf.append(HEX_DIGITS[b & 0xf]); + } + +} diff --git a/src/main/java/org/whispersystems/libaxolotl/util/KeyHelper.java b/src/main/java/org/whispersystems/libaxolotl/util/KeyHelper.java new file mode 100644 index 000000000..96ee65635 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/util/KeyHelper.java @@ -0,0 +1,143 @@ +package org.whispersystems.libaxolotl.util; + +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.IdentityKeyPair; +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.state.PreKeyRecord; +import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; + +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.LinkedList; +import java.util.List; + +/** + * Helper class for generating keys of different types. + * + * @author Moxie Marlinspike + */ +public class KeyHelper { + + private KeyHelper() {} + + /** + * Generate an identity key pair. Clients should only do this once, + * at install time. + * + * @return the generated IdentityKeyPair. + */ + public static IdentityKeyPair generateIdentityKeyPair() { + ECKeyPair keyPair = Curve.generateKeyPair(); + IdentityKey publicKey = new IdentityKey(keyPair.getPublicKey()); + return new IdentityKeyPair(publicKey, keyPair.getPrivateKey()); + } + + /** + * Generate a registration ID. Clients should only do this once, + * at install time. + * + * @param extendedRange By default (false), the generated registration + * ID is sized to require the minimal possible protobuf + * encoding overhead. Specify true if the caller needs + * the full range of MAX_INT at the cost of slightly + * higher encoding overhead. + * @return the generated registration ID. + */ + public static int generateRegistrationId(boolean extendedRange) { + try { + SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); + if (extendedRange) return secureRandom.nextInt(Integer.MAX_VALUE - 1) + 1; + else return secureRandom.nextInt(16380) + 1; + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + public static int getRandomSequence(int max) { + try { + return SecureRandom.getInstance("SHA1PRNG").nextInt(max); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + /** + * Generate a list of PreKeys. Clients should do this at install time, and + * subsequently any time the list of PreKeys stored on the server runs low. + *

+ * PreKey IDs are shorts, so they will eventually be repeated. Clients should + * store PreKeys in a circular buffer, so that they are repeated as infrequently + * as possible. + * + * @param start The starting PreKey ID, inclusive. + * @param count The number of PreKeys to generate. + * @return the list of generated PreKeyRecords. + */ + public static List generatePreKeys(int start, int count) { + List results = new LinkedList<>(); + + start--; + + for (int i=0;i. + */ +package org.whispersystems.libaxolotl.util; + +public class Pair { + private final T1 v1; + private final T2 v2; + + public Pair(T1 v1, T2 v2) { + this.v1 = v1; + this.v2 = v2; + } + + public T1 first(){ + return v1; + } + + public T2 second(){ + return v2; + } + + public boolean equals(Object o) { + return o instanceof Pair && + equal(((Pair) o).first(), first()) && + equal(((Pair) o).second(), second()); + } + + public int hashCode() { + return first().hashCode() ^ second().hashCode(); + } + + private boolean equal(Object first, Object second) { + if (first == null && second == null) return true; + if (first == null || second == null) return false; + return first.equals(second); + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/util/guava/Absent.java b/src/main/java/org/whispersystems/libaxolotl/util/guava/Absent.java new file mode 100644 index 000000000..bd06ded96 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/util/guava/Absent.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.whispersystems.libaxolotl.util.guava; + +import static org.whispersystems.libaxolotl.util.guava.Preconditions.checkNotNull; + + + +import java.util.Collections; +import java.util.Set; + + +/** + * Implementation of an {@link Optional} not containing a reference. + */ + +final class Absent extends Optional { + static final Absent INSTANCE = new Absent(); + + @Override public boolean isPresent() { + return false; + } + + @Override public Object get() { + throw new IllegalStateException("value is absent"); + } + + @Override public Object or(Object defaultValue) { + return checkNotNull(defaultValue, "use orNull() instead of or(null)"); + } + + @SuppressWarnings("unchecked") // safe covariant cast + @Override public Optional or(Optional secondChoice) { + return (Optional) checkNotNull(secondChoice); + } + + @Override public Object or(Supplier supplier) { + return checkNotNull(supplier.get(), + "use orNull() instead of a Supplier that returns null"); + } + + @Override public Object orNull() { + return null; + } + + @Override public Set asSet() { + return Collections.emptySet(); + } + + @Override + public Optional transform(Function function) { + checkNotNull(function); + return Optional.absent(); + } + + @Override public boolean equals(Object object) { + return object == this; + } + + @Override public int hashCode() { + return 0x598df91c; + } + + @Override public String toString() { + return "Optional.absent()"; + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 0; +} diff --git a/src/main/java/org/whispersystems/libaxolotl/util/guava/Function.java b/src/main/java/org/whispersystems/libaxolotl/util/guava/Function.java new file mode 100644 index 000000000..1ad516c5f --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/util/guava/Function.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.whispersystems.libaxolotl.util.guava; + + + +/** + * Determines an output value based on an input value. + * + *

See the Guava User Guide article on the use of {@code + * Function}. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ + +public interface Function { + /** + * Returns the result of applying this function to {@code input}. This method is generally + * expected, but not absolutely required, to have the following properties: + * + *

    + *
  • Its execution does not cause any observable side effects. + *
  • The computation is consistent with equals; that is, {@link Objects#equal + * Objects.equal}{@code (a, b)} implies that {@code Objects.equal(function.apply(a), + * function.apply(b))}. + *
+ * + * @throws NullPointerException if {@code input} is null and this function does not accept null + * arguments + */ + T apply(F input); + + /** + * Indicates whether another object is equal to this function. + * + *

Most implementations will have no reason to override the behavior of {@link Object#equals}. + * However, an implementation may also choose to return {@code true} whenever {@code object} is a + * {@link Function} that it considers interchangeable with this one. "Interchangeable" + * typically means that {@code Objects.equal(this.apply(f), that.apply(f))} is true for all + * {@code f} of type {@code F}. Note that a {@code false} result from this method does not imply + * that the functions are known not to be interchangeable. + */ + @Override + boolean equals(Object object); +} diff --git a/src/main/java/org/whispersystems/libaxolotl/util/guava/Optional.java b/src/main/java/org/whispersystems/libaxolotl/util/guava/Optional.java new file mode 100644 index 000000000..4f2de832a --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/util/guava/Optional.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.whispersystems.libaxolotl.util.guava; + +import static org.whispersystems.libaxolotl.util.guava.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.Iterator; +import java.util.Set; + + +/** + * An immutable object that may contain a non-null reference to another object. Each + * instance of this type either contains a non-null reference, or contains nothing (in + * which case we say that the reference is "absent"); it is never said to "contain {@code + * null}". + * + *

A non-null {@code Optional} reference can be used as a replacement for a nullable + * {@code T} reference. It allows you to represent "a {@code T} that must be present" and + * a "a {@code T} that might be absent" as two distinct types in your program, which can + * aid clarity. + * + *

Some uses of this class include + * + *

    + *
  • As a method return type, as an alternative to returning {@code null} to indicate + * that no value was available + *
  • To distinguish between "unknown" (for example, not present in a map) and "known to + * have no value" (present in the map, with value {@code Optional.absent()}) + *
  • To wrap nullable references for storage in a collection that does not support + * {@code null} (though there are + * + * several other approaches to this that should be considered first) + *
+ * + *

A common alternative to using this class is to find or create a suitable + * null object for the + * type in question. + * + *

This class is not intended as a direct analogue of any existing "option" or "maybe" + * construct from other programming environments, though it may bear some similarities. + * + *

See the Guava User Guide article on + * using {@code Optional}. + * + * @param the type of instance that can be contained. {@code Optional} is naturally + * covariant on this type, so it is safe to cast an {@code Optional} to {@code + * Optional} for any supertype {@code S} of {@code T}. + * @author Kurt Alfred Kluever + * @author Kevin Bourrillion + * @since 10.0 + */ +public abstract class Optional implements Serializable { + /** + * Returns an {@code Optional} instance with no contained reference. + */ + @SuppressWarnings("unchecked") + public static Optional absent() { + return (Optional) Absent.INSTANCE; + } + + /** + * Returns an {@code Optional} instance containing the given non-null reference. + */ + public static Optional of(T reference) { + return new Present(checkNotNull(reference)); + } + + /** + * If {@code nullableReference} is non-null, returns an {@code Optional} instance containing that + * reference; otherwise returns {@link Optional#absent}. + */ + public static Optional fromNullable(T nullableReference) { + return (nullableReference == null) + ? Optional.absent() + : new Present(nullableReference); + } + + Optional() {} + + /** + * Returns {@code true} if this holder contains a (non-null) instance. + */ + public abstract boolean isPresent(); + + /** + * Returns the contained instance, which must be present. If the instance might be + * absent, use {@link #or(Object)} or {@link #orNull} instead. + * + * @throws IllegalStateException if the instance is absent ({@link #isPresent} returns + * {@code false}) + */ + public abstract T get(); + + /** + * Returns the contained instance if it is present; {@code defaultValue} otherwise. If + * no default value should be required because the instance is known to be present, use + * {@link #get()} instead. For a default value of {@code null}, use {@link #orNull}. + * + *

Note about generics: The signature {@code public T or(T defaultValue)} is overly + * restrictive. However, the ideal signature, {@code public S or(S)}, is not legal + * Java. As a result, some sensible operations involving subtypes are compile errors: + *

   {@code
+   *
+   *   Optional optionalInt = getSomeOptionalInt();
+   *   Number value = optionalInt.or(0.5); // error
+   *
+   *   FluentIterable numbers = getSomeNumbers();
+   *   Optional first = numbers.first();
+   *   Number value = first.or(0.5); // error}
+ * + * As a workaround, it is always safe to cast an {@code Optional} to {@code + * Optional}. Casting either of the above example {@code Optional} instances to {@code + * Optional} (where {@code Number} is the desired output type) solves the problem: + *
   {@code
+   *
+   *   Optional optionalInt = (Optional) getSomeOptionalInt();
+   *   Number value = optionalInt.or(0.5); // fine
+   *
+   *   FluentIterable numbers = getSomeNumbers();
+   *   Optional first = (Optional) numbers.first();
+   *   Number value = first.or(0.5); // fine}
+ */ + public abstract T or(T defaultValue); + + /** + * Returns this {@code Optional} if it has a value present; {@code secondChoice} + * otherwise. + */ + public abstract Optional or(Optional secondChoice); + + /** + * Returns the contained instance if it is present; {@code supplier.get()} otherwise. If the + * supplier returns {@code null}, a {@link NullPointerException} is thrown. + * + * @throws NullPointerException if the supplier returns {@code null} + */ + public abstract T or(Supplier supplier); + + /** + * Returns the contained instance if it is present; {@code null} otherwise. If the + * instance is known to be present, use {@link #get()} instead. + */ + public abstract T orNull(); + + /** + * Returns an immutable singleton {@link Set} whose only element is the contained instance + * if it is present; an empty immutable {@link Set} otherwise. + * + * @since 11.0 + */ + public abstract Set asSet(); + + /** + * If the instance is present, it is transformed with the given {@link Function}; otherwise, + * {@link Optional#absent} is returned. If the function returns {@code null}, a + * {@link NullPointerException} is thrown. + * + * @throws NullPointerException if the function returns {@code null} + * + * @since 12.0 + */ + + public abstract Optional transform(Function function); + + /** + * Returns {@code true} if {@code object} is an {@code Optional} instance, and either + * the contained references are {@linkplain Object#equals equal} to each other or both + * are absent. Note that {@code Optional} instances of differing parameterized types can + * be equal. + */ + @Override public abstract boolean equals(Object object); + + /** + * Returns a hash code for this instance. + */ + @Override public abstract int hashCode(); + + /** + * Returns a string representation for this instance. The form of this string + * representation is unspecified. + */ + @Override public abstract String toString(); + + /** + * Returns the value of each present instance from the supplied {@code optionals}, in order, + * skipping over occurrences of {@link Optional#absent}. Iterators are unmodifiable and are + * evaluated lazily. + * + * @since 11.0 (generics widened in 13.0) + */ + +// public static Iterable presentInstances( +// final Iterable> optionals) { +// checkNotNull(optionals); +// return new Iterable() { +// @Override public Iterator iterator() { +// return new AbstractIterator() { +// private final Iterator> iterator = +// checkNotNull(optionals.iterator()); +// +// @Override protected T computeNext() { +// while (iterator.hasNext()) { +// Optional optional = iterator.next(); +// if (optional.isPresent()) { +// return optional.get(); +// } +// } +// return endOfData(); +// } +// }; +// }; +// }; +// } + + private static final long serialVersionUID = 0; +} diff --git a/src/main/java/org/whispersystems/libaxolotl/util/guava/Preconditions.java b/src/main/java/org/whispersystems/libaxolotl/util/guava/Preconditions.java new file mode 100644 index 000000000..ce253c65f --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/util/guava/Preconditions.java @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.whispersystems.libaxolotl.util.guava; + + +import java.util.NoSuchElementException; + + + +/** + * Simple static methods to be called at the start of your own methods to verify + * correct arguments and state. This allows constructs such as + *
+ *     if (count <= 0) {
+ *       throw new IllegalArgumentException("must be positive: " + count);
+ *     }
+ * + * to be replaced with the more compact + *
+ *     checkArgument(count > 0, "must be positive: %s", count);
+ * + * Note that the sense of the expression is inverted; with {@code Preconditions} + * you declare what you expect to be true, just as you do with an + * + * {@code assert} or a JUnit {@code assertTrue} call. + * + *

Warning: only the {@code "%s"} specifier is recognized as a + * placeholder in these messages, not the full range of {@link + * String#format(String, Object[])} specifiers. + * + *

Take care not to confuse precondition checking with other similar types + * of checks! Precondition exceptions -- including those provided here, but also + * {@link IndexOutOfBoundsException}, {@link NoSuchElementException}, {@link + * UnsupportedOperationException} and others -- are used to signal that the + * calling method has made an error. This tells the caller that it should + * not have invoked the method when it did, with the arguments it did, or + * perhaps ever. Postcondition or other invariant failures should not throw + * these types of exceptions. + * + *

See the Guava User Guide on + * using {@code Preconditions}. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ + +public final class Preconditions { + private Preconditions() {} + + /** + * Ensures the truth of an expression involving one or more parameters to the + * calling method. + * + * @param expression a boolean expression + * @throws IllegalArgumentException if {@code expression} is false + */ + public static void checkArgument(boolean expression) { + if (!expression) { + throw new IllegalArgumentException(); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the + * calling method. + * + * @param expression a boolean expression + * @param errorMessage the exception message to use if the check fails; will + * be converted to a string using {@link String#valueOf(Object)} + * @throws IllegalArgumentException if {@code expression} is false + */ + public static void checkArgument( + boolean expression, Object errorMessage) { + if (!expression) { + throw new IllegalArgumentException(String.valueOf(errorMessage)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the + * calling method. + * + * @param expression a boolean expression + * @param errorMessageTemplate a template for the exception message should the + * check fail. The message is formed by replacing each {@code %s} + * placeholder in the template with an argument. These are matched by + * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. + * Unmatched arguments will be appended to the formatted message in square + * braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message + * template. Arguments are converted to strings using + * {@link String#valueOf(Object)}. + * @throws IllegalArgumentException if {@code expression} is false + * @throws NullPointerException if the check fails and either {@code + * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let + * this happen) + */ + public static void checkArgument(boolean expression, + String errorMessageTemplate, + Object... errorMessageArgs) { + if (!expression) { + throw new IllegalArgumentException( + format(errorMessageTemplate, errorMessageArgs)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling + * instance, but not involving any parameters to the calling method. + * + * @param expression a boolean expression + * @throws IllegalStateException if {@code expression} is false + */ + public static void checkState(boolean expression) { + if (!expression) { + throw new IllegalStateException(); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling + * instance, but not involving any parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessage the exception message to use if the check fails; will + * be converted to a string using {@link String#valueOf(Object)} + * @throws IllegalStateException if {@code expression} is false + */ + public static void checkState( + boolean expression, Object errorMessage) { + if (!expression) { + throw new IllegalStateException(String.valueOf(errorMessage)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling + * instance, but not involving any parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessageTemplate a template for the exception message should the + * check fail. The message is formed by replacing each {@code %s} + * placeholder in the template with an argument. These are matched by + * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. + * Unmatched arguments will be appended to the formatted message in square + * braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message + * template. Arguments are converted to strings using + * {@link String#valueOf(Object)}. + * @throws IllegalStateException if {@code expression} is false + * @throws NullPointerException if the check fails and either {@code + * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let + * this happen) + */ + public static void checkState(boolean expression, + String errorMessageTemplate, + Object... errorMessageArgs) { + if (!expression) { + throw new IllegalStateException( + format(errorMessageTemplate, errorMessageArgs)); + } + } + + /** + * Ensures that an object reference passed as a parameter to the calling + * method is not null. + * + * @param reference an object reference + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ + public static T checkNotNull(T reference) { + if (reference == null) { + throw new NullPointerException(); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling + * method is not null. + * + * @param reference an object reference + * @param errorMessage the exception message to use if the check fails; will + * be converted to a string using {@link String#valueOf(Object)} + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ + public static T checkNotNull(T reference, Object errorMessage) { + if (reference == null) { + throw new NullPointerException(String.valueOf(errorMessage)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling + * method is not null. + * + * @param reference an object reference + * @param errorMessageTemplate a template for the exception message should the + * check fail. The message is formed by replacing each {@code %s} + * placeholder in the template with an argument. These are matched by + * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. + * Unmatched arguments will be appended to the formatted message in square + * braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message + * template. Arguments are converted to strings using + * {@link String#valueOf(Object)}. + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ + public static T checkNotNull(T reference, + String errorMessageTemplate, + Object... errorMessageArgs) { + if (reference == null) { + // If either of these parameters is null, the right thing happens anyway + throw new NullPointerException( + format(errorMessageTemplate, errorMessageArgs)); + } + return reference; + } + + /* + * All recent hotspots (as of 2009) *really* like to have the natural code + * + * if (guardExpression) { + * throw new BadException(messageExpression); + * } + * + * refactored so that messageExpression is moved to a separate + * String-returning method. + * + * if (guardExpression) { + * throw new BadException(badMsg(...)); + * } + * + * The alternative natural refactorings into void or Exception-returning + * methods are much slower. This is a big deal - we're talking factors of + * 2-8 in microbenchmarks, not just 10-20%. (This is a hotspot optimizer + * bug, which should be fixed, but that's a separate, big project). + * + * The coding pattern above is heavily used in java.util, e.g. in ArrayList. + * There is a RangeCheckMicroBenchmark in the JDK that was used to test this. + * + * But the methods in this class want to throw different exceptions, + * depending on the args, so it appears that this pattern is not directly + * applicable. But we can use the ridiculous, devious trick of throwing an + * exception in the middle of the construction of another exception. + * Hotspot is fine with that. + */ + + /** + * Ensures that {@code index} specifies a valid element in an array, + * list or string of size {@code size}. An element index may range from zero, + * inclusive, to {@code size}, exclusive. + * + * @param index a user-supplied index identifying an element of an array, list + * or string + * @param size the size of that array, list or string + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is not + * less than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkElementIndex(int index, int size) { + return checkElementIndex(index, size, "index"); + } + + /** + * Ensures that {@code index} specifies a valid element in an array, + * list or string of size {@code size}. An element index may range from zero, + * inclusive, to {@code size}, exclusive. + * + * @param index a user-supplied index identifying an element of an array, list + * or string + * @param size the size of that array, list or string + * @param desc the text to use to describe this index in an error message + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is not + * less than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkElementIndex( + int index, int size, String desc) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(badElementIndex(index, size, desc)); + } + return index; + } + + private static String badElementIndex(int index, int size, String desc) { + if (index < 0) { + return format("%s (%s) must not be negative", desc, index); + } else if (size < 0) { + throw new IllegalArgumentException("negative size: " + size); + } else { // index >= size + return format("%s (%s) must be less than size (%s)", desc, index, size); + } + } + + /** + * Ensures that {@code index} specifies a valid position in an array, + * list or string of size {@code size}. A position index may range from zero + * to {@code size}, inclusive. + * + * @param index a user-supplied index identifying a position in an array, list + * or string + * @param size the size of that array, list or string + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is + * greater than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkPositionIndex(int index, int size) { + return checkPositionIndex(index, size, "index"); + } + + /** + * Ensures that {@code index} specifies a valid position in an array, + * list or string of size {@code size}. A position index may range from zero + * to {@code size}, inclusive. + * + * @param index a user-supplied index identifying a position in an array, list + * or string + * @param size the size of that array, list or string + * @param desc the text to use to describe this index in an error message + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is + * greater than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkPositionIndex( + int index, int size, String desc) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc)); + } + return index; + } + + private static String badPositionIndex(int index, int size, String desc) { + if (index < 0) { + return format("%s (%s) must not be negative", desc, index); + } else if (size < 0) { + throw new IllegalArgumentException("negative size: " + size); + } else { // index > size + return format("%s (%s) must not be greater than size (%s)", + desc, index, size); + } + } + + /** + * Ensures that {@code start} and {@code end} specify a valid positions + * in an array, list or string of size {@code size}, and are in order. A + * position index may range from zero to {@code size}, inclusive. + * + * @param start a user-supplied index identifying a starting position in an + * array, list or string + * @param end a user-supplied index identifying a ending position in an array, + * list or string + * @param size the size of that array, list or string + * @throws IndexOutOfBoundsException if either index is negative or is + * greater than {@code size}, or if {@code end} is less than {@code start} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static void checkPositionIndexes(int start, int end, int size) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (start < 0 || end < start || end > size) { + throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size)); + } + } + + private static String badPositionIndexes(int start, int end, int size) { + if (start < 0 || start > size) { + return badPositionIndex(start, size, "start index"); + } + if (end < 0 || end > size) { + return badPositionIndex(end, size, "end index"); + } + // end < start + return format("end index (%s) must not be less than start index (%s)", + end, start); + } + + /** + * Substitutes each {@code %s} in {@code template} with an argument. These + * are matched by position - the first {@code %s} gets {@code args[0]}, etc. + * If there are more arguments than placeholders, the unmatched arguments will + * be appended to the end of the formatted message in square braces. + * + * @param template a non-null string containing 0 or more {@code %s} + * placeholders. + * @param args the arguments to be substituted into the message + * template. Arguments are converted to strings using + * {@link String#valueOf(Object)}. Arguments can be null. + */ + static String format(String template, + Object... args) { + template = String.valueOf(template); // null -> "null" + + // start substituting the arguments into the '%s' placeholders + StringBuilder builder = new StringBuilder( + template.length() + 16 * args.length); + int templateStart = 0; + int i = 0; + while (i < args.length) { + int placeholderStart = template.indexOf("%s", templateStart); + if (placeholderStart == -1) { + break; + } + builder.append(template.substring(templateStart, placeholderStart)); + builder.append(args[i++]); + templateStart = placeholderStart + 2; + } + builder.append(template.substring(templateStart)); + + // if we run out of placeholders, append the extra args in square braces + if (i < args.length) { + builder.append(" ["); + builder.append(args[i++]); + while (i < args.length) { + builder.append(", "); + builder.append(args[i++]); + } + builder.append(']'); + } + + return builder.toString(); + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/util/guava/Present.java b/src/main/java/org/whispersystems/libaxolotl/util/guava/Present.java new file mode 100644 index 000000000..630570ff4 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/util/guava/Present.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.whispersystems.libaxolotl.util.guava; + +import static org.whispersystems.libaxolotl.util.guava.Preconditions.checkNotNull; + +import java.util.Collections; +import java.util.Set; + +/** + * Implementation of an {@link Optional} containing a reference. + */ + +final class Present extends Optional { + private final T reference; + + Present(T reference) { + this.reference = reference; + } + + @Override public boolean isPresent() { + return true; + } + + @Override public T get() { + return reference; + } + + @Override public T or(T defaultValue) { + checkNotNull(defaultValue, "use orNull() instead of or(null)"); + return reference; + } + + @Override public Optional or(Optional secondChoice) { + checkNotNull(secondChoice); + return this; + } + + @Override public T or(Supplier supplier) { + checkNotNull(supplier); + return reference; + } + + @Override public T orNull() { + return reference; + } + + @Override public Set asSet() { + return Collections.singleton(reference); + } + + @Override public Optional transform(Function function) { + return new Present(checkNotNull(function.apply(reference), + "Transformation function cannot return null.")); + } + + @Override public boolean equals(Object object) { + if (object instanceof Present) { + Present other = (Present) object; + return reference.equals(other.reference); + } + return false; + } + + @Override public int hashCode() { + return 0x598df91c + reference.hashCode(); + } + + @Override public String toString() { + return "Optional.of(" + reference + ")"; + } + + private static final long serialVersionUID = 0; +} diff --git a/src/main/java/org/whispersystems/libaxolotl/util/guava/Supplier.java b/src/main/java/org/whispersystems/libaxolotl/util/guava/Supplier.java new file mode 100644 index 000000000..ba8807073 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/util/guava/Supplier.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.whispersystems.libaxolotl.util.guava; + + +/** + * A class that can supply objects of a single type. Semantically, this could + * be a factory, generator, builder, closure, or something else entirely. No + * guarantees are implied by this interface. + * + * @author Harry Heymann + * @since 2.0 (imported from Google Collections Library) + */ +public interface Supplier { + /** + * Retrieves an instance of the appropriate type. The returned object may or + * may not be a new instance, depending on the implementation. + * + * @return an instance of the appropriate type + */ + T get(); +}