Just An Application

August 21, 2014

And Another One: Part Nineteen — Surely That Can’t Be The Only Thing The PackageManagerService Does With Signatures ?

It ought to be but then there is this which is just horrid

1.0 PackageManagerService.getUidForVerifier

File

    $(ANDROID_SRC)/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java

Source

    ...
    
    private int getUidForVerifier(VerifierInfo verifierInfo) {
        synchronized (mPackages) {
            final PackageParser.Package pkg = mPackages.get(verifierInfo.packageName);
            if (pkg == null) {
                return -1;
            } else if (pkg.mSignatures.length != 1) {
                Slog.i(TAG, "Verifier package " + verifierInfo.packageName
                        + " has more than one signature; ignoring");
                return -1;
            }
    
            /*
             * If the public key of the package's signature does not match
             * our expected public key, then this is a different package and
             * we should skip.
             */
    
            final byte[] expectedPublicKey;
            try {
                final Signature verifierSig = pkg.mSignatures[0];
                final PublicKey publicKey = verifierSig.getPublicKey();
                expectedPublicKey = publicKey.getEncoded();
            } catch (CertificateException e) {
                return -1;
            }
    
            final byte[] actualPublicKey = verifierInfo.publicKey.getEncoded();
    
            if (!Arrays.equals(actualPublicKey, expectedPublicKey)) {
                Slog.i(TAG, "Verifier package " + verifierInfo.packageName
                        + " does not have the expected public key; ignoring");
                return -1;
            }
    
            return pkg.applicationInfo.uid;
        }
    }
    
    ... 

This method single-handedly drives a coach and horses through the idea that a Signature is a, to quote the class documentation

Opaque, immutable representation of a signature associated with an application package.

Immutable yes, opaque not so much.

1.1 Signature.getPublicKey

The Signature class did not have a getPublicKey method originally although it has always had a toByteArray method so it has never been as opaque as it ought to have been but know you can ask it to do this !

File

    $(ANDROID_SRC)/frameworks/base/core/java/android/content/pm/Signature.java

Source

    ...
    
    public PublicKey getPublicKey() throws CertificateException {
        final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
        final ByteArrayInputStream bais = new ByteArrayInputStream(mSignature);
        final Certificate cert = certFactory.generateCertificate(bais);
        return cert.getPublicKey();
    } ...

In fact you cannot ask it to do that because despite being a public method it has been hidden which is bizarre because you can always do the whole thing yourself.

Even more bizarrely it looks as though getUidForVerifier is the only method in the system that calls it.

Why add and a then hide a method that is only used by a single method in the PackageManagerService class ?

Why not do it locally ? The PackageManagerService class is almost 11000 lines long, 5 more aren’t going to make much difference.

1.2 What Does getUidForVerifier Do ?

What the method does is to look up the Package specified by the VerifierInfo‘s packageName instance variable.

If the Package exists the method then checks the length of the Package’s array of Signatures.

If the length is not one the method returns -1.

The length of an Application’s Signature array is going to be greater than one, if either

  • the Application has been signed more than once, or

  • there was more than one certificate in the signed signature file

No idea what is significant about either of these cases except that by excluding them it does mean that this method is unaffected by the failure to verify certificate chains.

If the length of an Application’s signature array is one then it necessarily contains the Signature constructed from the certificate which specifies the public key corresponding to the private key with which it was signed.

If the public key specified by the VerifierInfo‘s publicKey instance variable matches the public key extracted from the Signature then …

Then what exactly ?

Then you know you have the named Application and that it was signed by the private key corresponding to the specified public key.

1.4 Why A Public Key ?

If the VerifierInfo specified a name and an array of Signatures getUidForVerifier could have used the compareSignatures method rather than levering open a Signature, so why the public key ?

The VerifierInfo is obtained from the package-verifier element of an Android Application Manifest by this method

File

    $(ANDROID_SRC)/frameworks/base/core/java/android/content/pm/Signature.java

Source

    ...
    
    private static VerifierInfo parseVerifier(Resources res, XmlPullParser parser,
            AttributeSet attrs, int flags, String[] outError) throws XmlPullParserException,
            IOException {
        final TypedArray sa = res.obtainAttributes(attrs,
                com.android.internal.R.styleable.AndroidManifestPackageVerifier);
    
        final String packageName = sa.getNonResourceString(
                com.android.internal.R.styleable.AndroidManifestPackageVerifier_name);
    
        final String encodedPublicKey = sa.getNonResourceString(
                com.android.internal.R.styleable.AndroidManifestPackageVerifier_publicKey);
    
        sa.recycle();
    
        if (packageName == null || packageName.length() == 0) {
            Slog.i(TAG, "verifier package name was null; skipping");
            return null;
        } else if (encodedPublicKey == null) {
            Slog.i(TAG, "verifier " + packageName + " public key was null; skipping");
        }
    
        EncodedKeySpec keySpec;
        try {
            final byte[] encoded = Base64.decode(encodedPublicKey, Base64.DEFAULT);
            keySpec = new X509EncodedKeySpec(encoded);
        } catch (IllegalArgumentException e) {
            Slog.i(TAG, "Could not parse verifier " + packageName + " public key; invalid Base64");
            return null;
        }
    
        /* First try the key as an RSA key. */
        try {
            final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            final PublicKey publicKey = keyFactory.generatePublic(keySpec);
            return new VerifierInfo(packageName, publicKey);
        } catch (NoSuchAlgorithmException e) {
            Log.wtf(TAG, "Could not parse public key because RSA isn't included in build");
            return null;
        } catch (InvalidKeySpecException e) {
            // Not a RSA public key.
        }
    
        /* Now try it as a DSA key. */
        try {
            final KeyFactory keyFactory = KeyFactory.getInstance("DSA");
            final PublicKey publicKey = keyFactory.generatePublic(keySpec);
            return new VerifierInfo(packageName, publicKey);
        } catch (NoSuchAlgorithmException e) {
            Log.wtf(TAG, "Could not parse public key because DSA isn't included in build");
            return null;
        } catch (InvalidKeySpecException e) {
            // Not a DSA public key.
        }
    
        return null;
    }

    ...

As you can see the public key is created by Base64 decoding a String and then constructing an instance of

    java.security.spec.X509EncodedKeySpec

with the resulting bytes.

This is then turned into an instance of

    java.security.PublicKey

using brute force, which is interesting.

Given that the Android Application Manifest is an XML file this is perhaps not the best way to specify a public key.

Its not as though it hasn't been done before. See here for a different approach that even manages to include the information about what type of key it is.

There is also the question of the format of the data that needs to be specified.

The X509EncodedKeySpec constructor takes an encoded instance of one of these.

    SubjectPublicKeyInfo ::= SEQUENCE {
    algorithm AlgorithmIdentifier,
    subjectPublicKey BIT STRING }

which is defined in X.509

Now where would you get one of those ?

Given that

  1. it is defined in X.509 and

  2. that specifying a public keys is what a certificates is for

its not unreasonable to suppose that you might find one in an X.509 certificate and in fact you would, its actually the payload of the certificate.

So why a bit of a certificate rather than all of the certificate ?

If it was as certificate at least you could maintain the pseudo-opacity of the Signature class and you could use the compareSignatures method so no special-case code required.

The obvious advantages of specifying a public key are that it is smaller and it works with Signatures constructed with any certificate that specifies the public key rather than a specific certificate.

Given that Android package verifiers are something of an enigma there could be other reasons why it is necessary to specify a public key rather than a certificate or a certificate chain but they would need to be very compelling to justify the getUidForVerifier method in its current form.


Copyright (c) 2014 By Simon Lewis. All Rights Reserved.

Unauthorized use and/or duplication of this material without express and written permission from this blog's author and owner Simon Lewis is strictly prohibited.

Excerpts and links may be used, provided that full and clear credit is given to Simon Lewis and justanapplication.wordpress.com with appropriate and specific direction to the original content.

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: