Skip to content

Commit

Permalink
Update JavaThemis Secure Cell API (#634)
Browse files Browse the repository at this point in the history
* New API of Secure Cell

Introduce new, interface-based API of Secure Cell. Current API has
a number of issues described in RFC 3.9, notably the run-time mode
setting and not-quite-secure support of String inputs which interferes
with passphrase API. That's why we introduce a new interface which
should rectify these issues.

The users will deal with *interfaces* of the base SecureCell class:
SecureCell.Seal, TokenProtect, ContextImprint. They will take into
account differences in inputs and outputs of these modes. They also
allow to introduce passphrase API which actually has the same API
after the initialization.

Using interfaces as opposed to classes allows us to hide unnecessary
implementation details and do not expose the users to a number of
classes that they don't actually need to interact with.

This is also a nice point to start paying more attention to JavaDocs,
with better descriptions of APIs and versioning.

* Implement new Secure Cell API

Add implementation of SecureCell interfaces. These are package-private
classes constructed by static factory methods of the base class. They
provide improved API with the similar behavior.

The differences include naming and the fact that encryption code path
no longer uses checked SecureCellException, instead wrapping it into
unchecked RuntimeException. This is because encryption code path is
unlikely to fail due to business errors as opposed to programming
errors, so there is no need to enforce immediate error checking with
checked exception. This is not true for decryption path.

Note that native methods do not throw exceptions on their own right now.
Instead they indicate errors by returning "null". This would be nice to
get right later.

* Nullability annotations

Mark new interface methods with @NotNull and @nullable annotations where
appropriate. This improves IDE experience and Kotlin integration.

Contracts enable even more warnings in IDE. For example, all Secure Cell
methods are pure (i.e., do not change the state of the cell itself).
This allows IDE to warn about unused results of Secure Cell calls.

Since we do not support Java 6 & 7, we can go with annotation library
that requires Java 8+. Jetbrains have a compatbility version which
supports earlier JVMs.

* Test for new Secure Cell API

Port Swift test suite to Java to check the new API. There are also tests
for compatibility between the new and old API.

These tests have uncovered a couple of warts in Themis Core
implementation. We'll deal with them later.

* Target Java 7 since that's what Android uses

We require Java 8 for building with embedded Gradle wrapper, however
Android still targets Java 7 by default at the moment. It is possible
to build for Java 8, but that changes the requirements to JVM. We'd
like to avoid cutting support unless we don't really need it.

Make it explicit that we're building for Java 7 for Android, and build
desktop Java code for Java 7 too. We use Java 7 features in the code
so we cannot build with Java 6. That train is now gone.

Also, change the annotation library to use the compatibility version
which supports Java 7.

* Use JUnit 3.x pattern to test for exceptions

Unfortunately, we cannot use "assertThrows" which is so convenient.
First of all, we can't use lambdas from Java 8. However, even if we use
Runnable instead, JUnit wrapper on Android uses older JUnit which does
not have "assertThrows" support in any form.

Thus we fall back to ridiculously ugly, but working approach recommended
by JUnit authors [1].

[1]: https://github.com/junit-team/junit4/wiki/Exception-testing

Maybe when we bump the requirements to Java 8+ we could use better
testing library API. But not today.

* Ignore tests that crash Android runners

These new tests are a pinata of crashes. They have discovered a serious
bug in JNI code which crashes the process on Android if a corrupted
Secure Cell is decrypted.

Disable these tests for now so that we can proceed with other updates.
They will be reenabled later, once the bug is fixed.

* Polyfill base64 decoding

It turns out that not all Android API levels support java.util.Base64
which appeared in Java 8. Since we build for Java 1.7, it's kinda wise
to not rely on that API. Android has its own API for decoding base64,
but we cannot use that for desktop Java.

So choose neither and provide a polyfill with is API-compatible with
java.util.Base64, implemented using an obscure Java API for processing
XML which include base64 support as well.

* Use Apache Commons for polyfill after all

Unfortunately, DatatypeConverter is not available on Android too, so we
have to use an external library.

* Use older version of Common Codec library

Unfortunately, we cannot use the latest version of the codec library
because some Android systems (e.g., API 24) already include an older
version of some of the Commons' classes. They are loaded before our
dependencies fetched by Gradle and break the party.

Investigation of Android source code shows that they're using what
appears to be version 1.2 of the library (the current version is 1.14).
Okay, so that limits us to the API provided by 1.2. Fine *sigh*
  • Loading branch information
ilammy authored May 11, 2020
1 parent f13e957 commit ab6c287
Show file tree
Hide file tree
Showing 11 changed files with 1,853 additions and 5 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,21 @@ _Code:_
Run `./gradlew :desktop:tasks` to learn more
([#633](https://github.com/cossacklabs/themis/pull/633)).

- Secure Cell API updates:

- New encryption/decryption API with consistent naming: `encrypt` and `decrypt`
([#634](https://github.com/cossacklabs/themis/pull/634)).
- Improved Token Protect API
([#634](https://github.com/cossacklabs/themis/pull/634)).
- Decryption no longer requires an intermediate `SecureCellData` object.
- Secure Cell mode can now be selected by instantiating an appropriate interface:

| New API | Old API |
| ------- | ------- |
| `SecureCell.SealWithKey(key)` | `new SecureCell(key, SecureCell.MODE_SEAL)` |
| `SecureCell.TokenProtectWithKey(key)` | `new SecureCell(key, SecureCell.MODE_TOKEN_PROTECT)` |
| `SecureCell.ContextImprintWithKey(key)` | `new SecureCell(key, SecureCell.MODE_CONTEXT_IMPRINT)` |

- **Node.js**

- New class `SymmetricKey` can be used to generate symmetric keys for Secure Cell ([#562](https://github.com/cossacklabs/themis/pull/562)).
Expand Down
10 changes: 10 additions & 0 deletions src/wrappers/themis/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ buildscript {

dependencies {
implementation project(":boringssl")
// Use a compatibility version to support Java 6.
implementation 'org.jetbrains:annotations-java5:16.0.2'
// Instrumentation tests
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test:rules:1.2.0'
// Keep it at 1.2, see tests/themis/wrappers/android/com/cossacklabs/themis/test/Base64.java
androidTestImplementation 'commons-codec:commons-codec:1.2'
}

android {
Expand All @@ -35,6 +39,12 @@ android {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

// Compile for Java 7.
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}

sourceSets {
main {
java.srcDirs = ['../java']
Expand Down
12 changes: 9 additions & 3 deletions src/wrappers/themis/java/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ sourceSets {
}

dependencies {
// Nullable annotations, etc.
// Use a compatibility version to support Java 6.
implementation 'org.jetbrains:annotations-java5:16.0.2'
// Unit tests
testImplementation 'junit:junit:4.13'
// Keep it at 1.2, see tests/themis/wrappers/android/com/cossacklabs/themis/test/Base64.java
testImplementation 'commons-codec:commons-codec:1.2'
}

test {
Expand All @@ -25,10 +31,10 @@ archivesBaseName = 'java-themis'
version = javaThemisVersion
group = 'com.cossacklabs'

// Compile for Java 8.
// Compile for Java 7.
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}

// Tweak compiler options.
Expand Down
Loading

0 comments on commit ab6c287

Please sign in to comment.