diff --git a/F-Droid/build.gradle b/F-Droid/build.gradle index d3d334330..499a3d509 100644 --- a/F-Droid/build.gradle +++ b/F-Droid/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.application' +apply plugin: 'witness' if (!hasProperty('sourceDeps')) { repositories { @@ -84,6 +85,26 @@ if (!hasProperty('sourceDeps')) { } } +// Only do the libraries imported from maven repositories. Our own libraries +// (like privileged-api-lib) and the prebuilt jars already checked into the +// source code don't need to be here. +dependencyVerification { + verify = [ + 'com.android.support:support-v4:c62f0d025dafa86f423f48df9185b0d89496adbc5f6a9be5a7c394d84cf91423', + 'com.android.support:appcompat-v7:9a2355537c2f01cf0b95523605c18606b8d824017e6e94a05c77b0cfc8f21c96', + 'com.android.support:support-annotations:104f353b53d5dd8d64b2f77eece4b37f6b961de9732eb6b706395e91033ec70a', + 'org.thoughtcrime.ssl.pinning:AndroidPinning:afa1d74e699257fa75cb109ff29bac50726ef269c6e306bdeffe8223cee06ef4', + 'com.nostra13.universalimageloader:universal-image-loader:b99382c5536c7325ef8dc0a0fe9a6cad803cf3488942bea7e1cca4db3e5dec43', + 'com.google.zxing:core:b4d82452e7a6bf6ec2698904b332431717ed8f9a850224f295aec89de80f2259', + 'eu.chainfire:libsuperuser:507f5f9703a7578406e672a96ff038fd8aeefd6e2fcb14dd0daba796239d6eaf', + 'cc.mvdan.accesspoint:library:dc89a085d6bc40381078b8dd7776b12bde0dbaf8ffbcddb17ec4ebc3edecc7ba', + 'info.guardianproject.netcipher:netcipher:a8eef6c3bf190e360c44c9364044b8050f0d387418acdae8d7ec78bd105a32a6', + 'com.madgag.spongycastle:pkix:0d9cca6991f68eb373cfad309d5268c9fc38db5efb5fe00dcccf5c973af1eca1', + 'com.madgag.spongycastle:prov:b8c3fec3a59aac1aa04ccf4dad7179351e54ef7672f53f508151b614c131398a', + 'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f', + 'commons-net:commons-net:b35ad597f17a6f221575df2f729a9de8f70390509e047680771e713bad713fb9', + ] +} task binaryDeps(type: Copy, dependsOn: ':F-Droid:prepareReleaseDependencies') { enabled = project.hasProperty('sourceDeps') diff --git a/build.gradle b/build.gradle index 42e0c832d..583db1576 100644 --- a/build.gradle +++ b/build.gradle @@ -4,5 +4,6 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:1.3.1' + classpath files('libs/gradle-witness.jar') } } diff --git a/extern/gradle-witness/LICENSE b/extern/gradle-witness/LICENSE new file mode 100644 index 000000000..9323adadf --- /dev/null +++ b/extern/gradle-witness/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 Open Whisper Systems + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/extern/gradle-witness/README.md b/extern/gradle-witness/README.md new file mode 100644 index 000000000..3fd82675d --- /dev/null +++ b/extern/gradle-witness/README.md @@ -0,0 +1,127 @@ +# Gradle Witness + +A gradle plugin that enables static verification for remote dependencies. + +Build systems like gradle and maven allow one to specify dependencies for versioned artifacts. An +Android project might list dependencies like this: + + dependency { + compile 'com.actionbarsherlock:actionbarsherlock:4.4.0@aar' + compile 'com.android.support:support-v4:19.0.1' + compile 'com.google.android.gcm:gcm-client:1.0.2' + compile 'se.emilsjolander:stickylistheaders:2.2.0' + } + +This allows the sample Android project to very easily make use of versioned third party libraries like +[ActionBarSherlock](http://actionbarsherlock.com/), or [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders). +During the build process, gradle will automatically retrieve the libraries from the configured +maven repositories and incorporate them into the build. This makes it easy to manage dependencies +without having to check jars into a project's source tree. + +## Dependency Problems + +A "published" maven/gradle artifact [looks like this](https://github.com/WhisperSystems/maven/tree/master/gson/releases/org/whispersystems/gson/2.2.4): + + gson-2.2.4.jar + gson-2.2.4.jar.md5 + gson-2.2.4.jar.sha1 + gson-2.2.4.pom + gson-2.2.4.pom.md5 + gson-2.2.4.pom.sha1 + +In the remote directory, the artifact consists of a POM file and a jar or aar, along with md5sum and +sha1sum hash values for those files. + +When gradle retrieves the artifact, it will also retrieve the md5sum and sha1sums to verify that +they match the calculated md5sum and sha1sum of the retrieved files. The problem, obviously, is +that if someone is able to compromise the remote maven repository and change the jar/aar for a +dependency to include some malicious functionality, they could just as easily change the md5sum +and sha1sum values the repository advertises as well. + +## The Witness Solution + +This gradle plugin simply allows the author of a project to statically specify the sha256sum of +the dependencies that it uses. For our dependency example above, `gradle-witness` would allow +the project to specify: + + dependency { + compile 'com.actionbarsherlock:actionbarsherlock:4.4.0@aar' + compile 'com.android.support:support-v4:19.0.1' + compile 'com.google.android.gcm:gcm-client:1.0.2' + compile 'se.emilsjolander:stickylistheaders:2.2.0' + } + + dependencyVerification { + verify = [ + 'com.actionbarsherlock:actionbarsherlock:5ab04d74101f70024b222e3ff9c87bee151ec43331b4a2134b6cc08cf8565819', + 'com.android.support:support-v4:a4268abd6370c3fd3f94d2a7f9e6e755f5ddd62450cf8bbc62ba789e1274d585', + 'com.google.android.gcm:gcm-client:5ff578202f93dcba1c210d015deb4241c7cdad9b7867bd1b32e0a5f4c16986ca', + 'se.emilsjolander:stickylistheaders:89146b46c96fea0e40200474a2625cda10fe94891e4128f53cdb42375091b9b6', + ] + } + +The `dependency` definition is the same, but `gradle-witness` allows one to also specify a +`dependencyVerification` definition as well. That definition should include a single list called +`verify` with elements in the format of `group_id:name:sha256sum`. + +At this point, running `gradle build` will first verify that all of the listed dependencies have +the specified sha256sums. If there's a mismatch, the build is aborted. If the remote repository +is later compromised, an attacker won't be able to undetectably modify these artifacts. + +## Using Witness + +Unfortunately, it doesn't make sense to publish `gradle-witness` as an artifact, since that +creates a bootstrapping problem. To use `gradle-witness`, the jar needs to be built and included +in your project: + + $ git clone https://github.com/WhisperSystems/gradle-witness.git + $ cd gradle-witness + $ gradle build + $ cp build/libs/gradle-witness.jar /path/to/your/project/libs/gradle-witness.jar + +Then in your project's `build.gradle`, the buildscript needs to add a `gradle-witness` dependency. +It might look something like: + + buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:0.9.+' + classpath files('libs/gradle-witness.jar') + } + } + + apply plugin: 'witness' + +At this point you can use `gradle-witness` in your project. If you're feeling "trusting on first +use," you can have `gradle-witness` calculate the sha256sum for all your project's dependencies +(and transitive dependencies!) for you: + + $ gradle -q calculateChecksums + +This will print the full `dependencyVerification` definition to include in the project's `build.gradle`. +For a project that has a dependency definition like: + + dependency { + compile 'com.actionbarsherlock:actionbarsherlock:4.4.0@aar' + compile 'com.android.support:support-v4:19.0.1' + compile 'com.google.android.gcm:gcm-client:1.0.2' + compile 'se.emilsjolander:stickylistheaders:2.2.0' + } + +Running `gradle -q calculateChecksums` will print: + + dependencyVerification { + verify = [ + 'com.actionbarsherlock:actionbarsherlock:5ab04d74101f70024b222e3ff9c87bee151ec43331b4a2134b6cc08cf8565819', + 'com.android.support:support-v4:a4268abd6370c3fd3f94d2a7f9e6e755f5ddd62450cf8bbc62ba789e1274d585', + 'com.google.android.gcm:gcm-client:5ff578202f93dcba1c210d015deb4241c7cdad9b7867bd1b32e0a5f4c16986ca', + 'se.emilsjolander:stickylistheaders:89146b46c96fea0e40200474a2625cda10fe94891e4128f53cdb42375091b9b6', + ] + } + +...which you can then include directly below the `dependency` definition in the project's `build.gradle`. + +And that's it! From then on, running a standard `gradle build` will verify the integrity of +the project's dependencies. diff --git a/extern/gradle-witness/build.gradle b/extern/gradle-witness/build.gradle new file mode 100644 index 000000000..988a3bfcb --- /dev/null +++ b/extern/gradle-witness/build.gradle @@ -0,0 +1,10 @@ +apply plugin: 'groovy' + +dependencies { + compile gradleApi() + compile localGroovy() +} + +sourceCompatibility = '1.7' +targetCompatibility = '1.7' + diff --git a/extern/gradle-witness/src/main/groovy/org/whispersystems/witness/WitnessPlugin.groovy b/extern/gradle-witness/src/main/groovy/org/whispersystems/witness/WitnessPlugin.groovy new file mode 100644 index 000000000..eb9123d7c --- /dev/null +++ b/extern/gradle-witness/src/main/groovy/org/whispersystems/witness/WitnessPlugin.groovy @@ -0,0 +1,64 @@ +package org.whispersystems.witness + +import org.gradle.api.InvalidUserDataException +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.ResolvedArtifact + +import java.security.MessageDigest + +class WitnessPluginExtension { + List verify +} + +class WitnessPlugin implements Plugin { + + static String calculateSha256(file) { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + file.eachByte 4096, {bytes, size -> + md.update(bytes, 0, size); + } + return md.digest().collect {String.format "%02x", it}.join(); + } + + void apply(Project project) { + project.extensions.create("dependencyVerification", WitnessPluginExtension) + project.afterEvaluate { + project.dependencyVerification.verify.each { + assertion -> + List parts = assertion.tokenize(":") + String group = parts.get(0) + String name = parts.get(1) + String hash = parts.get(2) + + ResolvedArtifact dependency = project.configurations.compile.resolvedConfiguration.resolvedArtifacts.find { + return it.name.equals(name) && it.moduleVersion.id.group.equals(group) + } + + println "Verifying " + group + ":" + name + + if (dependency == null) { + throw new InvalidUserDataException("No dependency for integrity assertion found: " + group + ":" + name) + } + + if (!hash.equals(calculateSha256(dependency.file))) { + throw new InvalidUserDataException("Checksum failed for " + assertion) + } + } + } + + project.task('calculateChecksums') << { + println "dependencyVerification {" + println " verify = [" + + project.configurations.compile.resolvedConfiguration.resolvedArtifacts.each { + dep -> + println " '" + dep.moduleVersion.id.group+ ":" + dep.name + ":" + calculateSha256(dep.file) + "'," + } + + println " ]" + println "}" + } + } +} + diff --git a/extern/gradle-witness/src/main/resources/META-INF/gradle-plugins/witness.properties b/extern/gradle-witness/src/main/resources/META-INF/gradle-plugins/witness.properties new file mode 100644 index 000000000..dae767f67 --- /dev/null +++ b/extern/gradle-witness/src/main/resources/META-INF/gradle-plugins/witness.properties @@ -0,0 +1 @@ +implementation-class=org.whispersystems.witness.WitnessPlugin diff --git a/libs/gradle-witness.jar b/libs/gradle-witness.jar new file mode 100644 index 000000000..f68e2338f Binary files /dev/null and b/libs/gradle-witness.jar differ diff --git a/libs/gradle-witness.txt b/libs/gradle-witness.txt new file mode 100644 index 000000000..d21345450 --- /dev/null +++ b/libs/gradle-witness.txt @@ -0,0 +1,6 @@ +gradle-witness.jar was obtained by running `gradle build` inside the directory +extern/gradle-witness/ in this repository. The source code for the groovy +plugin and its license can be found there. + +We must prebuild a jar for this plugin since gradle plugins can't be used +directly from source.