{"action":"created","comment":{"url":"https://api.github.com/repos/cloudant/sync-android/pulls/comments/31570867","id":31570867,"diff_hunk":"@@ -0,0 +1,149 @@\n+package com.cloudant.sync.datastore.encryption;\n+\n+import org.junit.Test;\n+\n+import java.util.Arrays;\n+\n+import cloudant.com.androidtest.AndroidTestUtil;\n+\n+import static org.junit.Assert.assertFalse;\n+import static org.junit.Assert.assertNotNull;\n+import static org.junit.Assert.assertTrue;\n+import static org.junit.Assert.fail;\n+\n+public class AndroidKeyProviderTests {\n+\n+    @Test\n+    public void testCreateProviderWithIdentifier() {\n+        AndroidKeyProvider provider = new AndroidKeyProvider(AndroidTestUtil.context,\n+                ProviderTestUtil.password, ProviderTestUtil.getUniqueIdentifier());\n+\n+        EncryptionKey createdKey = provider.getEncryptionKey();\n+        assertNotNull(\"EncryptionKey was not created\", createdKey);\n+\n+        EncryptionKey loadedKey = provider.getEncryptionKey();\n+        assertNotNull(\"EncryptionKey was not loaded\", loadedKey);\n+\n+        assertTrue(\"loadedKey did not match createdKey\", Arrays.equals(createdKey.getKey(),\n+                loadedKey.getKey()));\n+\n+        provider.getManager().clearKey();\n+    }\n+\n+    @Test\n+    public void testCreateProviderWithManager() {\n+        KeyStorage storage = new KeyStorage(AndroidTestUtil.context, ProviderTestUtil\n+                .getUniqueIdentifier());\n+        KeyManager manager = new KeyManager(storage);\n+        AndroidKeyProvider provider = new AndroidKeyProvider(ProviderTestUtil.password, manager);\n+\n+        EncryptionKey createdKey = provider.getEncryptionKey();\n+        assertNotNull(\"EncryptionKey was not created\", createdKey);\n+\n+        EncryptionKey loadedKey = provider.getEncryptionKey();\n+        assertNotNull(\"EncryptionKey was not loaded\", loadedKey);","path":"sync-android/src/test/java/com/cloudant/sync/datastore/encryption/AndroidKeyProviderTests.java","position":null,"original_position":44,"commit_id":"a93b7fac5ddf8e596ba2df7108476a5b5b18e2c0","original_commit_id":"41c60e998f2267db9767fd652badacaa08e0dff2","user":{"login":"kholdaway","id":7144685,"avatar_url":"https://avatars.githubusercontent.com/u/7144685?v=3","gravatar_id":"","url":"https://api.github.com/users/kholdaway","html_url":"https://github.com/kholdaway","followers_url":"https://api.github.com/users/kholdaway/followers","following_url":"https://api.github.com/users/kholdaway/following{/other_user}","gists_url":"https://api.github.com/users/kholdaway/gists{/gist_id}","starred_url":"https://api.github.com/users/kholdaway/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kholdaway/subscriptions","organizations_url":"https://api.github.com/users/kholdaway/orgs","repos_url":"https://api.github.com/users/kholdaway/repos","events_url":"https://api.github.com/users/kholdaway/events{/privacy}","received_events_url":"https://api.github.com/users/kholdaway/received_events","type":"User","site_admin":false},"body":"I actually removed that test case as it used KeyManager for creating the AndroidKeyProvider.  The KeyManager class is not public API.  It has package level scope.  I removed the AndroidKeyProvider constructor that takes a KeyManager.  See commit https://github.com/cloudant/sync-android/commit/a93b7fac5ddf8e596ba2df7108476a5b5b18e2c0","created_at":"2015-06-02T21:03:25Z","updated_at":"2015-06-02T21:03:25Z","html_url":"https://github.com/cloudant/sync-android/pull/146#discussion_r31570867","pull_request_url":"https://api.github.com/repos/cloudant/sync-android/pulls/146","_links":{"self":{"href":"https://api.github.com/repos/cloudant/sync-android/pulls/comments/31570867"},"html":{"href":"https://github.com/cloudant/sync-android/pull/146#discussion_r31570867"},"pull_request":{"href":"https://api.github.com/repos/cloudant/sync-android/pulls/146"}}},"pull_request":{"url":"https://api.github.com/repos/cloudant/sync-android/pulls/146","id":36410511,"html_url":"https://github.com/cloudant/sync-android/pull/146","diff_url":"https://github.com/cloudant/sync-android/pull/146.diff","patch_url":"https://github.com/cloudant/sync-android/pull/146.patch","issue_url":"https://api.github.com/repos/cloudant/sync-android/issues/146","number":146,"state":"open","locked":false,"title":"47368 android keychainkeyprovider","user":{"login":"kholdaway","id":7144685,"avatar_url":"https://avatars.githubusercontent.com/u/7144685?v=3","gravatar_id":"","url":"https://api.github.com/users/kholdaway","html_url":"https://github.com/kholdaway","followers_url":"https://api.github.com/users/kholdaway/followers","following_url":"https://api.github.com/users/kholdaway/following{/other_user}","gists_url":"https://api.github.com/users/kholdaway/gists{/gist_id}","starred_url":"https://api.github.com/users/kholdaway/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kholdaway/subscriptions","organizations_url":"https://api.github.com/users/kholdaway/orgs","repos_url":"https://api.github.com/users/kholdaway/repos","events_url":"https://api.github.com/users/kholdaway/events{/privacy}","received_events_url":"https://api.github.com/users/kholdaway/received_events","type":"User","site_admin":false},"body":"What\r\nImplement a KeyProvider for Android.\r\n\r\nWhy\r\nfeature-encryption requires an Android KeyProvider which will generate a Data Protect Key (DPK) or strong password that can be used later for database encryption. \r\n\r\nHow\r\nGenerate a DPK using a user provided password.  The DPK is generate as follows:\r\n - Generate a DPK as a 32 bytes buffer with secure random values.\r\n - Generate a salt as a 32 bytes buffer with secure random values.\r\n - Use PBKDF2 to derive a key based on the user-provided password and the salt.\r\n - Generate an initialization vector (IV) as a 16 bytes buffer with secure random values.\r\n - Use AES to cipher the DPK with the key and the IV.\r\n - Return the DPK and save the encrypted version to the Android SharedPreferences.\r\n\r\nThis implementation is split between the AndroidKeyProvider, KeyManager, KeyStorage and KeyData.  The KeyManager is responsible for generation of the DPK and utilizes the KeyStorage to persist the DPK.  The KeyStorage operates on KeyData objects.  The KeyData objects are serialized to JSON for storage.\r\n\r\nTesting\r\nCreated JUnit Android test classes AndroidKeyProviderTests, KeyManagerTests, KeyStorageTests, and KeyDataTests.  These test cases test public API of the corresponding class.\r\n\r\nreviewer @mikerhodes\r\nreviewer @tomblench \r\nreviewr @indisoluble ","created_at":"2015-05-28T14:11:45Z","updated_at":"2015-06-02T21:03:25Z","closed_at":null,"merged_at":null,"merge_commit_sha":"e77492667f8d97e3889c49aa812062bd979d6544","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/cloudant/sync-android/pulls/146/commits","review_comments_url":"https://api.github.com/repos/cloudant/sync-android/pulls/146/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/146/comments","statuses_url":"https://api.github.com/repos/cloudant/sync-android/statuses/a93b7fac5ddf8e596ba2df7108476a5b5b18e2c0","head":{"label":"cloudant:47368-android-keychainkeyprovider","ref":"47368-android-keychainkeyprovider","sha":"a93b7fac5ddf8e596ba2df7108476a5b5b18e2c0","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-06-01T09:59:35Z","pushed_at":"2015-06-02T21:01:13Z","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":28713,"stargazers_count":79,"watchers_count":79,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":26,"mirror_url":null,"open_issues_count":10,"forks":26,"open_issues":10,"watchers":79,"default_branch":"master"}},"base":{"label":"cloudant:feature-encryption","ref":"feature-encryption","sha":"ab19756bf8f6c20a807a2dd08cb8bca945f90864","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-06-01T09:59:35Z","pushed_at":"2015-06-02T21:01:13Z","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":28713,"stargazers_count":79,"watchers_count":79,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":26,"mirror_url":null,"open_issues_count":10,"forks":26,"open_issues":10,"watchers":79,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/cloudant/sync-android/pulls/146"},"html":{"href":"https://github.com/cloudant/sync-android/pull/146"},"issue":{"href":"https://api.github.com/repos/cloudant/sync-android/issues/146"},"comments":{"href":"https://api.github.com/repos/cloudant/sync-android/issues/146/comments"},"review_comments":{"href":"https://api.github.com/repos/cloudant/sync-android/pulls/146/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/146/commits"},"statuses":{"href":"https://api.github.com/repos/cloudant/sync-android/statuses/a93b7fac5ddf8e596ba2df7108476a5b5b18e2c0"}}}}