{"action":"created","comment":{"url":"https://api.github.com/repos/cloudant/sync-android/pulls/comments/30890054","id":30890054,"diff_hunk":"@@ -0,0 +1,48 @@\n+/**\n+ * Copyright (c) 2015 IBM Cloudant, Inc. All rights reserved.\n+ *\n+ * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file\n+ * except in compliance with the License. You may obtain a copy of the License at\n+ *\n+ * http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software distributed under the\n+ * License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\n+ * either express or implied. See the License for the specific language governing permissions\n+ * and limitations under the License.\n+ */\n+\n+package com.cloudant.sync.datastore.encryption;\n+\n+import java.util.Arrays;\n+\n+/**\n+ * Class to enforce restrictions on encryption keys used\n+ * with the datastore.\n+ *\n+ * SQLCipher requires a 32-byte/256-bit key. This class\n+ * enforces that.\n+ */\n+public class EncryptionKey {\n+\n+    private final static int REQUIRED_KEY_LENGTH = 32;\n+\n+    private final byte[] key;\n+\n+    public EncryptionKey(byte[] key) {\n+        if (key == null) {\n+            throw new IllegalArgumentException(\"Key array must not be null\");\n+        }\n+\n+        if (key.length != REQUIRED_KEY_LENGTH) {\n+            throw new IllegalArgumentException(\"Key array must be 32 bytes\");\n+        }\n+\n+        this.key = Arrays.copyOf(key, REQUIRED_KEY_LENGTH);\n+    }\n+\n+    public byte[] getKey() {","path":"sync-core/src/main/java/com/cloudant/sync/datastore/encryption/EncryptionKey.java","position":44,"original_position":44,"commit_id":"432b064848931acd3e040abc033d8f79bc339514","original_commit_id":"aa39958b7de4cb9d15c24165ac1d66363101270e","user":{"login":"mikerhodes","id":453210,"avatar_url":"https://avatars.githubusercontent.com/u/453210?v=3","gravatar_id":"","url":"https://api.github.com/users/mikerhodes","html_url":"https://github.com/mikerhodes","followers_url":"https://api.github.com/users/mikerhodes/followers","following_url":"https://api.github.com/users/mikerhodes/following{/other_user}","gists_url":"https://api.github.com/users/mikerhodes/gists{/gist_id}","starred_url":"https://api.github.com/users/mikerhodes/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mikerhodes/subscriptions","organizations_url":"https://api.github.com/users/mikerhodes/orgs","repos_url":"https://api.github.com/users/mikerhodes/repos","events_url":"https://api.github.com/users/mikerhodes/events{/privacy}","received_events_url":"https://api.github.com/users/mikerhodes/received_events","type":"User","site_admin":false},"body":"I want to avoid the user writing into the copy stored in the EncryptionKey object. It seems like that'd be a recipe for hard to debug issues.","created_at":"2015-05-22T12:21:35Z","updated_at":"2015-05-22T12:21:35Z","html_url":"https://github.com/cloudant/sync-android/pull/134#discussion_r30890054","pull_request_url":"https://api.github.com/repos/cloudant/sync-android/pulls/134","_links":{"self":{"href":"https://api.github.com/repos/cloudant/sync-android/pulls/comments/30890054"},"html":{"href":"https://github.com/cloudant/sync-android/pull/134#discussion_r30890054"},"pull_request":{"href":"https://api.github.com/repos/cloudant/sync-android/pulls/134"}}},"pull_request":{"url":"https://api.github.com/repos/cloudant/sync-android/pulls/134","id":35730604,"html_url":"https://github.com/cloudant/sync-android/pull/134","diff_url":"https://github.com/cloudant/sync-android/pull/134.diff","patch_url":"https://github.com/cloudant/sync-android/pull/134.patch","issue_url":"https://api.github.com/repos/cloudant/sync-android/issues/134","number":134,"state":"open","locked":false,"title":"Add encryption of JSON datastore to sync-android","user":{"login":"mikerhodes","id":453210,"avatar_url":"https://avatars.githubusercontent.com/u/453210?v=3","gravatar_id":"","url":"https://api.github.com/users/mikerhodes","html_url":"https://github.com/mikerhodes","followers_url":"https://api.github.com/users/mikerhodes/followers","following_url":"https://api.github.com/users/mikerhodes/following{/other_user}","gists_url":"https://api.github.com/users/mikerhodes/gists{/gist_id}","starred_url":"https://api.github.com/users/mikerhodes/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mikerhodes/subscriptions","organizations_url":"https://api.github.com/users/mikerhodes/orgs","repos_url":"https://api.github.com/users/mikerhodes/repos","events_url":"https://api.github.com/users/mikerhodes/events{/privacy}","received_events_url":"https://api.github.com/users/mikerhodes/received_events","type":"User","site_admin":false},"body":"_What:_\r\n\r\nThis is the first PR along the `feature-encryption` path to allow developers to encrypt data stored on Android devices. It implements encrypting the JSON data on the device (i.e., the JSON data's SQLite database).\r\n\r\n_Why:_\r\n\r\nThis feature is several PRs because it's a large feature, impossible to review all in one go. This PR shows including SQLCipher in sync-android along with encryption of one part of the data storage chain.\r\n\r\n_How:_\r\n\r\nSQLCipher .so, .jar files are now included in the sync-android build. At some point we might want to break these out into a separate sync-encryption class, but for now the jar is always there on Android. The final location of the .so files needs to be decided, as they are currently within the demo app file system tree.\r\n\r\nThe encryption follows the pattern of iOS. We have a KeyProvider interface which a developer uses to provide a 256-bit encryption key for use by SQLCipher to encrypt the database. We provide a SimpleKeyProvider which just stores the byte array the developer passes in, assuming that it's a securely generated key.\r\n\r\nThe developer passes the KeyProvider into the `openDatastore` method in `DatastoreManager`, which then passes it through to the datastore constructor, which passes it to the database queue, which passes it to the platform independent database wrapper class, which finally figures which concrete database implementation to use -- Android with or without encryption, or Java SE without encryption as we don't support encryption there yet.\r\n\r\nA NullKeyProvider class is used to signal use of non-encrypted databases, this avoids duplicated code and `null` checks.\r\n\r\n_Tests:_\r\n\r\nRight now, there's a simple switch which runs all the tests using a SQLCipher-based database on an Android device or simulator.\r\n\r\nFuture work in improving these tests:\r\n\r\n- Try to make sure test suite always tests both encrypted and non-encrypted code paths.\r\n- Have explicit checks that when a key is passed, database is encrypted.\r\n\r\nBugId: 43715\r\n\r\nreviewer @tomblench \r\nreviewer @indisoluble \r\nreviewer @rhyshort ","created_at":"2015-05-19T13:36:16Z","updated_at":"2015-05-22T12:21:35Z","closed_at":null,"merged_at":null,"merge_commit_sha":"0f6a4f231a56f6205cb31d331d5de38b579ffdfa","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/cloudant/sync-android/pulls/134/commits","review_comments_url":"https://api.github.com/repos/cloudant/sync-android/pulls/134/comments","review_comment_url":"https://api.github.com/repos/cloudant/sync-android/pulls/comments{/number}","comments_url":"https://api.github.com/repos/cloudant/sync-android/issues/134/comments","statuses_url":"https://api.github.com/repos/cloudant/sync-android/statuses/432b064848931acd3e040abc033d8f79bc339514","head":{"label":"cloudant:43715-sqlcipher-support-01","ref":"43715-sqlcipher-support-01","sha":"432b064848931acd3e040abc033d8f79bc339514","user":{"login":"cloudant","id":44642,"avatar_url":"https://avatars.githubusercontent.com/u/44642?v=3","gravatar_id":"","url":"https://api.github.com/users/cloudant","html_url":"https://github.com/cloudant","followers_url":"https://api.github.com/users/cloudant/followers","following_url":"https://api.github.com/users/cloudant/following{/other_user}","gists_url":"https://api.github.com/users/cloudant/gists{/gist_id}","starred_url":"https://api.github.com/users/cloudant/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cloudant/subscriptions","organizations_url":"https://api.github.com/users/cloudant/orgs","repos_url":"https://api.github.com/users/cloudant/repos","events_url":"https://api.github.com/users/cloudant/events{/privacy}","received_events_url":"https://api.github.com/users/cloudant/received_events","type":"Organization","site_admin":false},"repo":{"id":16811502,"name":"sync-android","full_name":"cloudant/sync-android","owner":{"login":"cloudant","id":44642,"avatar_url":"https://avatars.githubusercontent.com/u/44642?v=3","gravatar_id":"","url":"https://api.github.com/users/cloudant","html_url":"https://github.com/cloudant","followers_url":"https://api.github.com/users/cloudant/followers","following_url":"https://api.github.com/users/cloudant/following{/other_user}","gists_url":"https://api.github.com/users/cloudant/gists{/gist_id}","starred_url":"https://api.github.com/users/cloudant/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cloudant/subscriptions","organizations_url":"https://api.github.com/users/cloudant/orgs","repos_url":"https://api.github.com/users/cloudant/repos","events_url":"https://api.github.com/users/cloudant/events{/privacy}","received_events_url":"https://api.github.com/users/cloudant/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/cloudant/sync-android","description":"A JSON-based document datastore for Android applications","fork":false,"url":"https://api.github.com/repos/cloudant/sync-android","forks_url":"https://api.github.com/repos/cloudant/sync-android/forks","keys_url":"https://api.github.com/repos/cloudant/sync-android/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cloudant/sync-android/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cloudant/sync-android/teams","hooks_url":"https://api.github.com/repos/cloudant/sync-android/hooks","issue_events_url":"https://api.github.com/repos/cloudant/sync-android/issues/events{/number}","events_url":"https://api.github.com/repos/cloudant/sync-android/events","assignees_url":"https://api.github.com/repos/cloudant/sync-android/assignees{/user}","branches_url":"https://api.github.com/repos/cloudant/sync-android/branches{/branch}","tags_url":"https://api.github.com/repos/cloudant/sync-android/tags","blobs_url":"https://api.github.com/repos/cloudant/sync-android/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cloudant/sync-android/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cloudant/sync-android/git/refs{/sha}","trees_url":"https://api.github.com/repos/cloudant/sync-android/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cloudant/sync-android/statuses/{sha}","languages_url":"https://api.github.com/repos/cloudant/sync-android/languages","stargazers_url":"https://api.github.com/repos/cloudant/sync-android/stargazers","contributors_url":"https://api.github.com/repos/cloudant/sync-android/contributors","subscribers_url":"https://api.github.com/repos/cloudant/sync-android/subscribers","subscription_url":"https://api.github.com/repos/cloudant/sync-android/subscription","commits_url":"https://api.github.com/repos/cloudant/sync-android/commits{/sha}","git_commits_url":"https://api.github.com/repos/cloudant/sync-android/git/commits{/sha}","comments_url":"https://api.github.com/repos/cloudant/sync-android/comments{/number}","issue_comment_url":"https://api.github.com/repos/cloudant/sync-android/issues/comments{/number}","contents_url":"https://api.github.com/repos/cloudant/sync-android/contents/{+path}","compare_url":"https://api.github.com/repos/cloudant/sync-android/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cloudant/sync-android/merges","archive_url":"https://api.github.com/repos/cloudant/sync-android/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cloudant/sync-android/downloads","issues_url":"https://api.github.com/repos/cloudant/sync-android/issues{/number}","pulls_url":"https://api.github.com/repos/cloudant/sync-android/pulls{/number}","milestones_url":"https://api.github.com/repos/cloudant/sync-android/milestones{/number}","notifications_url":"https://api.github.com/repos/cloudant/sync-android/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cloudant/sync-android/labels{/name}","releases_url":"https://api.github.com/repos/cloudant/sync-android/releases{/id}","created_at":"2014-02-13T17:52:09Z","updated_at":"2015-05-21T20:12:36Z","pushed_at":"2015-05-21T17:02:59Z","git_url":"git://github.com/cloudant/sync-android.git","ssh_url":"git@github.com:cloudant/sync-android.git","clone_url":"https://github.com/cloudant/sync-android.git","svn_url":"https://github.com/cloudant/sync-android","homepage":"","size":27161,"stargazers_count":77,"watchers_count":77,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":24,"mirror_url":null,"open_issues_count":10,"forks":24,"open_issues":10,"watchers":77,"default_branch":"master"}},"base":{"label":"cloudant:feature-encryption","ref":"feature-encryption","sha":"b912c2bda3e64dbfac3bc088badedf535b3f5428","user":{"login":"cloudant","id":44642,"avatar_url":"https://avatars.githubusercontent.com/u/44642?v=3","gravatar_id":"","url":"https://api.github.com/users/cloudant","html_url":"https://github.com/cloudant","followers_url":"https://api.github.com/users/cloudant/followers","following_url":"https://api.github.com/users/cloudant/following{/other_user}","gists_url":"https://api.github.com/users/cloudant/gists{/gist_id}","starred_url":"https://api.github.com/users/cloudant/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cloudant/subscriptions","organizations_url":"https://api.github.com/users/cloudant/orgs","repos_url":"https://api.github.com/users/cloudant/repos","events_url":"https://api.github.com/users/cloudant/events{/privacy}","received_events_url":"https://api.github.com/users/cloudant/received_events","type":"Organization","site_admin":false},"repo":{"id":16811502,"name":"sync-android","full_name":"cloudant/sync-android","owner":{"login":"cloudant","id":44642,"avatar_url":"https://avatars.githubusercontent.com/u/44642?v=3","gravatar_id":"","url":"https://api.github.com/users/cloudant","html_url":"https://github.com/cloudant","followers_url":"https://api.github.com/users/cloudant/followers","following_url":"https://api.github.com/users/cloudant/following{/other_user}","gists_url":"https://api.github.com/users/cloudant/gists{/gist_id}","starred_url":"https://api.github.com/users/cloudant/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cloudant/subscriptions","organizations_url":"https://api.github.com/users/cloudant/orgs","repos_url":"https://api.github.com/users/cloudant/repos","events_url":"https://api.github.com/users/cloudant/events{/privacy}","received_events_url":"https://api.github.com/users/cloudant/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/cloudant/sync-android","description":"A JSON-based document datastore for Android applications","fork":false,"url":"https://api.github.com/repos/cloudant/sync-android","forks_url":"https://api.github.com/repos/cloudant/sync-android/forks","keys_url":"https://api.github.com/repos/cloudant/sync-android/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cloudant/sync-android/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cloudant/sync-android/teams","hooks_url":"https://api.github.com/repos/cloudant/sync-android/hooks","issue_events_url":"https://api.github.com/repos/cloudant/sync-android/issues/events{/number}","events_url":"https://api.github.com/repos/cloudant/sync-android/events","assignees_url":"https://api.github.com/repos/cloudant/sync-android/assignees{/user}","branches_url":"https://api.github.com/repos/cloudant/sync-android/branches{/branch}","tags_url":"https://api.github.com/repos/cloudant/sync-android/tags","blobs_url":"https://api.github.com/repos/cloudant/sync-android/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cloudant/sync-android/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cloudant/sync-android/git/refs{/sha}","trees_url":"https://api.github.com/repos/cloudant/sync-android/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cloudant/sync-android/statuses/{sha}","languages_url":"https://api.github.com/repos/cloudant/sync-android/languages","stargazers_url":"https://api.github.com/repos/cloudant/sync-android/stargazers","contributors_url":"https://api.github.com/repos/cloudant/sync-android/contributors","subscribers_url":"https://api.github.com/repos/cloudant/sync-android/subscribers","subscription_url":"https://api.github.com/repos/cloudant/sync-android/subscription","commits_url":"https://api.github.com/repos/cloudant/sync-android/commits{/sha}","git_commits_url":"https://api.github.com/repos/cloudant/sync-android/git/commits{/sha}","comments_url":"https://api.github.com/repos/cloudant/sync-android/comments{/number}","issue_comment_url":"https://api.github.com/repos/cloudant/sync-android/issues/comments{/number}","contents_url":"https://api.github.com/repos/cloudant/sync-android/contents/{+path}","compare_url":"https://api.github.com/repos/cloudant/sync-android/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cloudant/sync-android/merges","archive_url":"https://api.github.com/repos/cloudant/sync-android/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cloudant/sync-android/downloads","issues_url":"https://api.github.com/repos/cloudant/sync-android/issues{/number}","pulls_url":"https://api.github.com/repos/cloudant/sync-android/pulls{/number}","milestones_url":"https://api.github.com/repos/cloudant/sync-android/milestones{/number}","notifications_url":"https://api.github.com/repos/cloudant/sync-android/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cloudant/sync-android/labels{/name}","releases_url":"https://api.github.com/repos/cloudant/sync-android/releases{/id}","created_at":"2014-02-13T17:52:09Z","updated_at":"2015-05-21T20:12:36Z","pushed_at":"2015-05-21T17:02:59Z","git_url":"git://github.com/cloudant/sync-android.git","ssh_url":"git@github.com:cloudant/sync-android.git","clone_url":"https://github.com/cloudant/sync-android.git","svn_url":"https://github.com/cloudant/sync-android","homepage":"","size":27161,"stargazers_count":77,"watchers_count":77,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":24,"mirror_url":null,"open_issues_count":10,"forks":24,"open_issues":10,"watchers":77,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/cloudant/sync-android/pulls/134"},"html":{"href":"https://github.com/cloudant/sync-android/pull/134"},"issue":{"href":"https://api.github.com/repos/cloudant/sync-android/issues/134"},"comments":{"href":"https://api.github.com/repos/cloudant/sync-android/issues/134/comments"},"review_comments":{"href":"https://api.github.com/repos/cloudant/sync-android/pulls/134/comments"},"review_comment":{"href":"https://api.github.com/repos/cloudant/sync-android/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/cloudant/sync-android/pulls/134/commits"},"statuses":{"href":"https://api.github.com/repos/cloudant/sync-android/statuses/432b064848931acd3e040abc033d8f79bc339514"}}}}