diff options
-rw-r--r-- | services/core/java/com/android/server/signedconfig/SignatureVerifier.java | 109 | ||||
-rw-r--r-- | services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java | 11 | ||||
-rw-r--r-- | tools/signedconfig/debug_key.pem | 5 | ||||
-rw-r--r-- | tools/signedconfig/debug_public.pem | 4 | ||||
-rwxr-xr-x | tools/signedconfig/debug_sign.sh | 6 | ||||
-rwxr-xr-x | tools/signedconfig/gen_priv_key.sh | 7 | ||||
-rwxr-xr-x | tools/signedconfig/verify_b64.sh | 10 |
7 files changed, 150 insertions, 2 deletions
diff --git a/services/core/java/com/android/server/signedconfig/SignatureVerifier.java b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java new file mode 100644 index 000000000000..944db84acc71 --- /dev/null +++ b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * 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 com.android.server.signedconfig; + +import android.os.Build; +import android.util.Slog; + +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.spec.EncodedKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +/** + * Helper class for verifying config signatures. + */ +public class SignatureVerifier { + + private static final String TAG = "SignedConfig"; + private static final boolean DBG = false; + + private static final String DEBUG_KEY = + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaAn2XVifsLTHg616nTsOMVmlhBoECGbTEBTKKvdd2hO60" + + "pj1pnU8SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ=="; + + private final PublicKey mDebugKey; + + public SignatureVerifier() { + mDebugKey = createKey(DEBUG_KEY); + } + + private static PublicKey createKey(String base64) { + EncodedKeySpec keySpec; + try { + byte[] key = Base64.getDecoder().decode(base64); + keySpec = new X509EncodedKeySpec(key); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Failed to base64 decode public key", e); + return null; + } + try { + KeyFactory factory = KeyFactory.getInstance("EC"); + return factory.generatePublic(keySpec); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + Slog.e(TAG, "Failed to construct public key", e); + return null; + } + } + + /** + * Verify a signature for signed config. + * + * @param config Config as read from APK meta-data. + * @param base64Signature Signature as read from APK meta-data. + * @return {@code true} iff the signature was successfully verified. + */ + public boolean verifySignature(String config, String base64Signature) + throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { + byte[] signature; + try { + signature = Base64.getDecoder().decode(base64Signature); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Failed to base64 decode signature"); + return false; + } + byte[] data = config.getBytes(StandardCharsets.UTF_8); + if (DBG) Slog.i(TAG, "Data: " + Base64.getEncoder().encodeToString(data)); + + if (Build.IS_DEBUGGABLE) { + if (mDebugKey != null) { + if (DBG) Slog.w(TAG, "Trying to verify signature using debug key"); + Signature verifier = Signature.getInstance("SHA256withECDSA"); + verifier.initVerify(mDebugKey); + verifier.update(data); + if (verifier.verify(signature)) { + Slog.i(TAG, "Verified config using debug key"); + return true; + } else { + if (DBG) Slog.i(TAG, "Config verification failed using debug key"); + } + } else { + Slog.w(TAG, "Debuggable build, but have no debug key"); + } + } + // TODO verify production key. + Slog.w(TAG, "NO PRODUCTION KEY YET, FAILING VERIFICATION"); + return false; + } +} diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java b/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java index e4d799a2e3b7..4908964109ad 100644 --- a/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java +++ b/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java @@ -24,6 +24,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; +import java.security.GeneralSecurityException; import java.util.Arrays; import java.util.Collections; import java.util.Map; @@ -65,15 +66,21 @@ class SignedConfigApplicator { private final Context mContext; private final String mSourcePackage; + private final SignatureVerifier mVerifier; SignedConfigApplicator(Context context, String sourcePackage) { mContext = context; mSourcePackage = sourcePackage; + mVerifier = new SignatureVerifier(); } private boolean checkSignature(String data, String signature) { - Slog.w(TAG, "SIGNATURE CHECK NOT IMPLEMENTED YET!"); - return false; + try { + return mVerifier.verifySignature(data, signature); + } catch (GeneralSecurityException e) { + Slog.e(TAG, "Failed to verify signature", e); + return false; + } } private int getCurrentConfigVersion() { diff --git a/tools/signedconfig/debug_key.pem b/tools/signedconfig/debug_key.pem new file mode 100644 index 000000000000..0af577bf81e1 --- /dev/null +++ b/tools/signedconfig/debug_key.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIEfgtO+KPOoqJqTnqkDDKkAcOzyvtovsUO/ShLE6y4XRoAoGCCqGSM49 +AwEHoUQDQgAEaAn2XVifsLTHg616nTsOMVmlhBoECGbTEBTKKvdd2hO60pj1pnU8 +SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ== +-----END EC PRIVATE KEY----- diff --git a/tools/signedconfig/debug_public.pem b/tools/signedconfig/debug_public.pem new file mode 100644 index 000000000000..f61f81322b94 --- /dev/null +++ b/tools/signedconfig/debug_public.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaAn2XVifsLTHg616nTsOMVmlhBoE +CGbTEBTKKvdd2hO60pj1pnU8SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ== +-----END PUBLIC KEY----- diff --git a/tools/signedconfig/debug_sign.sh b/tools/signedconfig/debug_sign.sh new file mode 100755 index 000000000000..28e54289f8f8 --- /dev/null +++ b/tools/signedconfig/debug_sign.sh @@ -0,0 +1,6 @@ +#!/bin/bash +# Script to sign data with the debug keys. Outputs base64 for embedding into +# APK metadata. + +openssl dgst -sha256 -sign $(dirname $0)/debug_key.pem $1 | base64 -w 0 +echo diff --git a/tools/signedconfig/gen_priv_key.sh b/tools/signedconfig/gen_priv_key.sh new file mode 100755 index 000000000000..834c86bc8c12 --- /dev/null +++ b/tools/signedconfig/gen_priv_key.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# This script acts as a record of how the debug key was generated. There should +# be no need to run it again. + +openssl ecparam -name prime256v1 -genkey -noout -out debug_key.pem +openssl ec -in debug_key.pem -pubout -out debug_public.pem diff --git a/tools/signedconfig/verify_b64.sh b/tools/signedconfig/verify_b64.sh new file mode 100755 index 000000000000..8e1f58ce7b45 --- /dev/null +++ b/tools/signedconfig/verify_b64.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Script to verify signatures, with both signature & data given in b64 +# Args: +# 1. data (base64 encoded) +# 2. signature (base64 encoded) +# The arg values can be taken from the debug log for SignedConfigService when verbose logging is +# enabled. + +openssl dgst -sha256 -verify $(dirname $0)/debug_public.pem -signature <(echo $2 | base64 -d) <(echo $1 | base64 -d) |