Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 96c419f9 authored by Mathew Inwood's avatar Mathew Inwood
Browse files

Implement signature check.

Currently, we just have debug keys, and always fail verification on
user builds. Production keys will be added later.

This CL also includes some helper scripts:
- Used to generate debug keys, for the record
- To sign data using the debug keys
- To verify base64 encoded data, used for debugging

Test: atest CtsSignedConfigHostTestCases
Note: The test also relies on some other changes going in too; it has
been verified with all relevant change in place, but will not pass at
HEAD quite yet.

Bug: 110509075
Change-Id: I8bd420c44a0a523cbefb21f90c49550c25beb0a6
parent 9a7fdeb3
Loading
Loading
Loading
Loading
+109 −0
Original line number Diff line number Diff line
/*
 * 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;
    }
}
+9 −2
Original line number Diff line number Diff line
@@ -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,16 +66,22 @@ 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!");
        try {
            return mVerifier.verifySignature(data, signature);
        } catch (GeneralSecurityException e) {
            Slog.e(TAG, "Failed to verify signature", e);
            return false;
        }
    }

    private int getCurrentConfigVersion() {
        return Settings.Global.getInt(mContext.getContentResolver(),
+5 −0
Original line number Diff line number Diff line
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIEfgtO+KPOoqJqTnqkDDKkAcOzyvtovsUO/ShLE6y4XRoAoGCCqGSM49
AwEHoUQDQgAEaAn2XVifsLTHg616nTsOMVmlhBoECGbTEBTKKvdd2hO60pj1pnU8
SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ==
-----END EC PRIVATE KEY-----
+4 −0
Original line number Diff line number Diff line
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaAn2XVifsLTHg616nTsOMVmlhBoE
CGbTEBTKKvdd2hO60pj1pnU8SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ==
-----END PUBLIC KEY-----
+6 −0
Original line number Diff line number Diff line
#!/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
Loading