2 * Copyright (C) 2013 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.server.pm;
19 import android.content.pm.PackageParser;
20 import android.util.ArrayMap;
21 import android.util.ArraySet;
22 import android.util.Base64;
23 import android.util.Slog;
24 import android.util.LongSparseArray;
26 import java.io.IOException;
27 import java.io.PrintWriter;
28 import java.security.PublicKey;
31 import org.xmlpull.v1.XmlPullParser;
32 import org.xmlpull.v1.XmlPullParserException;
33 import org.xmlpull.v1.XmlSerializer;
36 * Manages system-wide KeySet state.
38 public class KeySetManagerService {
40 static final String TAG = "KeySetManagerService";
42 /* original keysets implementation had no versioning info, so this is the first */
43 public static final int FIRST_VERSION = 1;
45 public static final int CURRENT_VERSION = FIRST_VERSION;
47 /** Sentinel value returned when a {@code KeySet} is not found. */
48 public static final long KEYSET_NOT_FOUND = -1;
50 /** Sentinel value returned when public key is not found. */
51 protected static final long PUBLIC_KEY_NOT_FOUND = -1;
53 private final LongSparseArray<KeySetHandle> mKeySets;
55 private final LongSparseArray<PublicKeyHandle> mPublicKeys;
57 protected final LongSparseArray<ArraySet<Long>> mKeySetMapping;
59 private final ArrayMap<String, PackageSetting> mPackages;
61 private long lastIssuedKeySetId = 0;
63 private long lastIssuedKeyId = 0;
65 class PublicKeyHandle {
66 private final PublicKey mKey;
67 private final long mId;
68 private int mRefCount;
70 public PublicKeyHandle(long id, PublicKey key) {
77 * Only used when reading state from packages.xml
79 private PublicKeyHandle(long id, int refCount, PublicKey key) {
89 public PublicKey getKey() {
93 public int getRefCountLPr() {
97 public void incrRefCountLPw() {
101 public long decrRefCountLPw() {
107 public KeySetManagerService(ArrayMap<String, PackageSetting> packages) {
108 mKeySets = new LongSparseArray<KeySetHandle>();
109 mPublicKeys = new LongSparseArray<PublicKeyHandle>();
110 mKeySetMapping = new LongSparseArray<ArraySet<Long>>();
111 mPackages = packages;
115 * Determine if a package is signed by the given KeySet.
117 * Returns false if the package was not signed by all the
118 * keys in the KeySet.
120 * Returns true if the package was signed by at least the
121 * keys in the given KeySet.
123 * Note that this can return true for multiple KeySets.
125 public boolean packageIsSignedByLPr(String packageName, KeySetHandle ks) {
126 PackageSetting pkg = mPackages.get(packageName);
128 throw new NullPointerException("Invalid package name");
130 if (pkg.keySetData == null) {
131 throw new NullPointerException("Package has no KeySet data");
133 long id = getIdByKeySetLPr(ks);
134 if (id == KEYSET_NOT_FOUND) {
137 ArraySet<Long> pkgKeys = mKeySetMapping.get(pkg.keySetData.getProperSigningKeySet());
138 ArraySet<Long> testKeys = mKeySetMapping.get(id);
139 return pkgKeys.containsAll(testKeys);
143 * Determine if a package is signed by the given KeySet.
145 * Returns false if the package was not signed by all the
146 * keys in the KeySet, or if the package was signed by keys
149 * Note that this can return only for one KeySet.
151 public boolean packageIsSignedByExactlyLPr(String packageName, KeySetHandle ks) {
152 PackageSetting pkg = mPackages.get(packageName);
154 throw new NullPointerException("Invalid package name");
156 if (pkg.keySetData == null
157 || pkg.keySetData.getProperSigningKeySet()
158 == PackageKeySetData.KEYSET_UNASSIGNED) {
159 throw new NullPointerException("Package has no KeySet data");
161 long id = getIdByKeySetLPr(ks);
162 if (id == KEYSET_NOT_FOUND) {
165 ArraySet<Long> pkgKeys = mKeySetMapping.get(pkg.keySetData.getProperSigningKeySet());
166 ArraySet<Long> testKeys = mKeySetMapping.get(id);
167 return pkgKeys.equals(testKeys);
171 * Informs the system that the given package was signed by the provided KeySet.
173 public void addSigningKeySetToPackageLPw(String packageName,
174 ArraySet<PublicKey> signingKeys) {
176 /* check existing keyset for reuse or removal */
177 PackageSetting pkg = mPackages.get(packageName);
178 long signingKeySetId = pkg.keySetData.getProperSigningKeySet();
180 if (signingKeySetId != PackageKeySetData.KEYSET_UNASSIGNED) {
181 ArraySet<PublicKey> existingKeys = getPublicKeysFromKeySetLPr(signingKeySetId);
182 if (existingKeys.equals(signingKeys)) {
184 /* no change in signing keys, leave PackageSetting alone */
188 /* old keyset no longer valid, remove ref */
189 KeySetHandle ksh = mKeySets.get(signingKeySetId);
190 decrementKeySetLPw(signingKeySetId);
194 /* create and add a new keyset */
195 KeySetHandle ks = addKeySetLPw(signingKeys);
196 long id = ks.getId();
197 pkg.keySetData.setProperSigningKeySet(id);
202 * Fetches the stable identifier associated with the given KeySet. Returns
203 * {@link #KEYSET_NOT_FOUND} if the KeySet... wasn't found.
205 private long getIdByKeySetLPr(KeySetHandle ks) {
206 for (int keySetIndex = 0; keySetIndex < mKeySets.size(); keySetIndex++) {
207 KeySetHandle value = mKeySets.valueAt(keySetIndex);
208 if (ks.equals(value)) {
209 return mKeySets.keyAt(keySetIndex);
212 return KEYSET_NOT_FOUND;
216 * Inform the system that the given package defines the given KeySets.
217 * Remove any KeySets the package no longer defines.
219 public void addDefinedKeySetsToPackageLPw(String packageName,
220 ArrayMap<String, ArraySet<PublicKey>> definedMapping) {
221 PackageSetting pkg = mPackages.get(packageName);
222 ArrayMap<String, Long> prevDefinedKeySets = pkg.keySetData.getAliases();
224 /* add all of the newly defined KeySets */
225 ArrayMap<String, Long> newKeySetAliases = new ArrayMap<String, Long>();
226 final int defMapSize = definedMapping.size();
227 for (int i = 0; i < defMapSize; i++) {
228 String alias = definedMapping.keyAt(i);
229 ArraySet<PublicKey> pubKeys = definedMapping.valueAt(i);
230 if (alias != null && pubKeys != null && pubKeys.size() > 0) {
231 KeySetHandle ks = addKeySetLPw(pubKeys);
232 newKeySetAliases.put(alias, ks.getId());
236 /* remove each of the old references */
237 final int prevDefSize = prevDefinedKeySets.size();
238 for (int i = 0; i < prevDefSize; i++) {
239 decrementKeySetLPw(prevDefinedKeySets.valueAt(i));
241 pkg.keySetData.removeAllUpgradeKeySets();
243 /* switch to the just-added */
244 pkg.keySetData.setAliases(newKeySetAliases);
249 * This informs the system that the given package has defined a KeySet
250 * alias in its manifest to be an upgradeKeySet. This must be called
251 * after all of the defined KeySets have been added.
253 public void addUpgradeKeySetsToPackageLPw(String packageName,
254 ArraySet<String> upgradeAliases) {
255 PackageSetting pkg = mPackages.get(packageName);
256 final int uaSize = upgradeAliases.size();
257 for (int i = 0; i < uaSize; i++) {
258 pkg.keySetData.addUpgradeKeySet(upgradeAliases.valueAt(i));
264 * Fetched the {@link KeySetHandle} that a given package refers to by the
265 * provided alias. Returns null if the package is unknown or does not have a
266 * KeySet corresponding to that alias.
268 public KeySetHandle getKeySetByAliasAndPackageNameLPr(String packageName, String alias) {
269 PackageSetting p = mPackages.get(packageName);
270 if (p == null || p.keySetData == null) {
273 Long keySetId = p.keySetData.getAliases().get(alias);
274 if (keySetId == null) {
275 throw new IllegalArgumentException("Unknown KeySet alias: " + alias);
277 return mKeySets.get(keySetId);
281 * Fetches the {@link PublicKey public keys} which belong to the specified
284 * Returns {@code null} if the identifier doesn't
285 * identify a {@link KeySetHandle}.
287 public ArraySet<PublicKey> getPublicKeysFromKeySetLPr(long id) {
288 if(mKeySetMapping.get(id) == null) {
291 ArraySet<PublicKey> mPubKeys = new ArraySet<PublicKey>();
292 ArraySet<Long> pkIds = mKeySetMapping.get(id);
293 final int pkSize = pkIds.size();
294 for (int i = 0; i < pkSize; i++) {
295 mPubKeys.add(mPublicKeys.get(pkIds.valueAt(i)).getKey());
301 * Fetches the proper {@link KeySetHandle KeySet} that signed the given
304 * @throws IllegalArgumentException if the package has no keyset data.
305 * @throws NullPointerException if the packgae is unknown.
307 public KeySetHandle getSigningKeySetByPackageNameLPr(String packageName) {
308 PackageSetting p = mPackages.get(packageName);
310 || p.keySetData == null
311 || p.keySetData.getProperSigningKeySet()
312 == PackageKeySetData.KEYSET_UNASSIGNED) {
315 return mKeySets.get(p.keySetData.getProperSigningKeySet());
319 * Creates a new KeySet corresponding to the given keys.
321 * If the {@link PublicKey PublicKeys} aren't known to the system, this
322 * adds them. Otherwise, they're deduped and the reference count
325 * If the KeySet isn't known to the system, this adds that and creates the
326 * mapping to the PublicKeys. If it is known, then it's deduped and the
327 * reference count is incremented.
329 * Throws if the provided set is {@code null}.
331 private KeySetHandle addKeySetLPw(ArraySet<PublicKey> keys) {
332 if (keys == null || keys.size() == 0) {
333 throw new IllegalArgumentException("Cannot add an empty set of keys!");
336 /* add each of the keys in the provided set */
337 ArraySet<Long> addedKeyIds = new ArraySet<Long>(keys.size());
338 final int kSize = keys.size();
339 for (int i = 0; i < kSize; i++) {
340 long id = addPublicKeyLPw(keys.valueAt(i));
344 /* check to see if the resulting keyset is new */
345 long existingKeySetId = getIdFromKeyIdsLPr(addedKeyIds);
346 if (existingKeySetId != KEYSET_NOT_FOUND) {
348 /* public keys were incremented, but we aren't adding a new keyset: undo */
349 for (int i = 0; i < kSize; i++) {
350 decrementPublicKeyLPw(addedKeyIds.valueAt(i));
352 KeySetHandle ks = mKeySets.get(existingKeySetId);
353 ks.incrRefCountLPw();
357 // get the next keyset id
358 long id = getFreeKeySetIDLPw();
360 // create the KeySet object and add to mKeySets and mapping
361 KeySetHandle ks = new KeySetHandle(id);
362 mKeySets.put(id, ks);
363 mKeySetMapping.put(id, addedKeyIds);
368 * Decrements the reference to KeySet represented by the given id. If this
369 * drops to zero, then also decrement the reference to each public key it
370 * contains and remove the KeySet.
372 private void decrementKeySetLPw(long id) {
373 KeySetHandle ks = mKeySets.get(id);
374 if (ks.decrRefCountLPw() <= 0) {
375 ArraySet<Long> pubKeys = mKeySetMapping.get(id);
376 final int pkSize = pubKeys.size();
377 for (int i = 0; i < pkSize; i++) {
378 decrementPublicKeyLPw(pubKeys.valueAt(i));
381 mKeySetMapping.delete(id);
387 * Decrements the reference to PublicKey represented by the given id. If
388 * this drops to zero, then remove it.
390 private void decrementPublicKeyLPw(long id) {
391 PublicKeyHandle pk = mPublicKeys.get(id);
392 if (pk.decrRefCountLPw() <= 0) {
393 mPublicKeys.delete(id);
399 * Adds the given PublicKey to the system, deduping as it goes.
401 private long addPublicKeyLPw(PublicKey key) {
402 long id = getIdForPublicKeyLPr(key);
403 if (id != PUBLIC_KEY_NOT_FOUND) {
405 /* We already know about this key, increment its ref count and ret */
406 mPublicKeys.get(id).incrRefCountLPw();
410 /* if it's new find the first unoccupied slot in the public keys */
411 id = getFreePublicKeyIdLPw();
412 mPublicKeys.put(id, new PublicKeyHandle(id, key));
417 * Finds the stable identifier for a KeySet based on a set of PublicKey stable IDs.
419 * Returns KEYSET_NOT_FOUND if there isn't one.
421 private long getIdFromKeyIdsLPr(Set<Long> publicKeyIds) {
422 for (int keyMapIndex = 0; keyMapIndex < mKeySetMapping.size(); keyMapIndex++) {
423 ArraySet<Long> value = mKeySetMapping.valueAt(keyMapIndex);
424 if (value.equals(publicKeyIds)) {
425 return mKeySetMapping.keyAt(keyMapIndex);
428 return KEYSET_NOT_FOUND;
432 * Finds the stable identifier for a PublicKey or PUBLIC_KEY_NOT_FOUND.
434 private long getIdForPublicKeyLPr(PublicKey k) {
435 String encodedPublicKey = new String(k.getEncoded());
436 for (int publicKeyIndex = 0; publicKeyIndex < mPublicKeys.size(); publicKeyIndex++) {
437 PublicKey value = mPublicKeys.valueAt(publicKeyIndex).getKey();
438 String encodedExistingKey = new String(value.getEncoded());
439 if (encodedPublicKey.equals(encodedExistingKey)) {
440 return mPublicKeys.keyAt(publicKeyIndex);
443 return PUBLIC_KEY_NOT_FOUND;
447 * Gets an unused stable identifier for a KeySet.
449 private long getFreeKeySetIDLPw() {
450 lastIssuedKeySetId += 1;
451 return lastIssuedKeySetId;
455 * Same as above, but for public keys.
457 private long getFreePublicKeyIdLPw() {
458 lastIssuedKeyId += 1;
459 return lastIssuedKeyId;
463 * This package is being removed from the system, so we need to
464 * remove its keyset and public key references, then remove its
467 public void removeAppKeySetDataLPw(String packageName) {
469 /* remove refs from common keysets and public keys */
470 PackageSetting pkg = mPackages.get(packageName);
471 long signingKeySetId = pkg.keySetData.getProperSigningKeySet();
472 decrementKeySetLPw(signingKeySetId);
473 ArrayMap<String, Long> definedKeySets = pkg.keySetData.getAliases();
474 for (int i = 0; i < definedKeySets.size(); i++) {
475 decrementKeySetLPw(definedKeySets.valueAt(i));
478 /* remove from package */
479 clearPackageKeySetDataLPw(pkg);
483 private void clearPackageKeySetDataLPw(PackageSetting pkg) {
484 pkg.keySetData.setProperSigningKeySet(PackageKeySetData.KEYSET_UNASSIGNED);
485 pkg.keySetData.removeAllDefinedKeySets();
486 pkg.keySetData.removeAllUpgradeKeySets();
490 public String encodePublicKey(PublicKey k) throws IOException {
491 return new String(Base64.encode(k.getEncoded(), Base64.NO_WRAP));
494 public void dumpLPr(PrintWriter pw, String packageName,
495 PackageManagerService.DumpState dumpState) {
496 boolean printedHeader = false;
497 for (ArrayMap.Entry<String, PackageSetting> e : mPackages.entrySet()) {
498 String keySetPackage = e.getKey();
499 if (packageName != null && !packageName.equals(keySetPackage)) {
502 if (!printedHeader) {
503 if (dumpState.onTitlePrinted())
505 pw.println("Key Set Manager:");
506 printedHeader = true;
508 PackageSetting pkg = e.getValue();
509 pw.print(" ["); pw.print(keySetPackage); pw.println("]");
510 if (pkg.keySetData != null) {
511 boolean printedLabel = false;
512 for (ArrayMap.Entry<String, Long> entry : pkg.keySetData.getAliases().entrySet()) {
514 pw.print(" KeySets Aliases: ");
519 pw.print(entry.getKey());
521 pw.print(Long.toString(entry.getValue()));
526 printedLabel = false;
527 if (pkg.keySetData.isUsingDefinedKeySets()) {
528 ArrayMap<String, Long> definedKeySets = pkg.keySetData.getAliases();
529 final int dksSize = definedKeySets.size();
530 for (int i = 0; i < dksSize; i++) {
532 pw.print(" Defined KeySets: ");
537 pw.print(Long.toString(definedKeySets.valueAt(i)));
543 printedLabel = false;
544 final long signingKeySet = pkg.keySetData.getProperSigningKeySet();
545 pw.print(" Signing KeySets: ");
546 pw.print(Long.toString(signingKeySet));
548 if (pkg.keySetData.isUsingUpgradeKeySets()) {
549 for (long keySetId : pkg.keySetData.getUpgradeKeySets()) {
551 pw.print(" Upgrade KeySets: ");
556 pw.print(Long.toString(keySetId));
566 void writeKeySetManagerServiceLPr(XmlSerializer serializer) throws IOException {
567 serializer.startTag(null, "keyset-settings");
568 serializer.attribute(null, "version", Integer.toString(CURRENT_VERSION));
569 writePublicKeysLPr(serializer);
570 writeKeySetsLPr(serializer);
571 serializer.startTag(null, "lastIssuedKeyId");
572 serializer.attribute(null, "value", Long.toString(lastIssuedKeyId));
573 serializer.endTag(null, "lastIssuedKeyId");
574 serializer.startTag(null, "lastIssuedKeySetId");
575 serializer.attribute(null, "value", Long.toString(lastIssuedKeySetId));
576 serializer.endTag(null, "lastIssuedKeySetId");
577 serializer.endTag(null, "keyset-settings");
580 void writePublicKeysLPr(XmlSerializer serializer) throws IOException {
581 serializer.startTag(null, "keys");
582 for (int pKeyIndex = 0; pKeyIndex < mPublicKeys.size(); pKeyIndex++) {
583 long id = mPublicKeys.keyAt(pKeyIndex);
584 PublicKeyHandle pkh = mPublicKeys.valueAt(pKeyIndex);
585 String encodedKey = encodePublicKey(pkh.getKey());
586 serializer.startTag(null, "public-key");
587 serializer.attribute(null, "identifier", Long.toString(id));
588 serializer.attribute(null, "value", encodedKey);
589 serializer.endTag(null, "public-key");
591 serializer.endTag(null, "keys");
594 void writeKeySetsLPr(XmlSerializer serializer) throws IOException {
595 serializer.startTag(null, "keysets");
596 for (int keySetIndex = 0; keySetIndex < mKeySetMapping.size(); keySetIndex++) {
597 long id = mKeySetMapping.keyAt(keySetIndex);
598 ArraySet<Long> keys = mKeySetMapping.valueAt(keySetIndex);
599 serializer.startTag(null, "keyset");
600 serializer.attribute(null, "identifier", Long.toString(id));
601 for (long keyId : keys) {
602 serializer.startTag(null, "key-id");
603 serializer.attribute(null, "identifier", Long.toString(keyId));
604 serializer.endTag(null, "key-id");
606 serializer.endTag(null, "keyset");
608 serializer.endTag(null, "keysets");
611 void readKeySetsLPw(XmlPullParser parser, ArrayMap<Long, Integer> keySetRefCounts)
612 throws XmlPullParserException, IOException {
614 long currentKeySetId = 0;
615 int outerDepth = parser.getDepth();
616 String recordedVersionStr = parser.getAttributeValue(null, "version");
617 if (recordedVersionStr == null) {
618 // The keyset information comes from pre-versioned devices, and
619 // is inaccurate, don't collect any of it.
620 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
621 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
624 // The KeySet information read previously from packages.xml is invalid.
626 for (PackageSetting p : mPackages.values()) {
627 clearPackageKeySetDataLPw(p);
631 int recordedVersion = Integer.parseInt(recordedVersionStr);
632 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
633 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
634 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
637 final String tagName = parser.getName();
638 if (tagName.equals("keys")) {
640 } else if (tagName.equals("keysets")) {
641 readKeySetListLPw(parser);
642 } else if (tagName.equals("lastIssuedKeyId")) {
643 lastIssuedKeyId = Long.parseLong(parser.getAttributeValue(null, "value"));
644 } else if (tagName.equals("lastIssuedKeySetId")) {
645 lastIssuedKeySetId = Long.parseLong(parser.getAttributeValue(null, "value"));
649 addRefCountsFromSavedPackagesLPw(keySetRefCounts);
652 void readKeysLPw(XmlPullParser parser)
653 throws XmlPullParserException, IOException {
654 int outerDepth = parser.getDepth();
656 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
657 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
658 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
661 final String tagName = parser.getName();
662 if (tagName.equals("public-key")) {
663 readPublicKeyLPw(parser);
668 void readKeySetListLPw(XmlPullParser parser)
669 throws XmlPullParserException, IOException {
670 int outerDepth = parser.getDepth();
672 long currentKeySetId = 0;
673 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
674 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
675 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
678 final String tagName = parser.getName();
679 if (tagName.equals("keyset")) {
680 String encodedID = parser.getAttributeValue(null, "identifier");
681 currentKeySetId = Long.parseLong(encodedID);
683 mKeySets.put(currentKeySetId, new KeySetHandle(currentKeySetId, refCount));
684 mKeySetMapping.put(currentKeySetId, new ArraySet<Long>());
685 } else if (tagName.equals("key-id")) {
686 String encodedID = parser.getAttributeValue(null, "identifier");
687 long id = Long.parseLong(encodedID);
688 mKeySetMapping.get(currentKeySetId).add(id);
693 void readPublicKeyLPw(XmlPullParser parser)
694 throws XmlPullParserException {
695 String encodedID = parser.getAttributeValue(null, "identifier");
696 long identifier = Long.parseLong(encodedID);
698 String encodedPublicKey = parser.getAttributeValue(null, "value");
699 PublicKey pub = PackageParser.parsePublicKey(encodedPublicKey);
701 PublicKeyHandle pkh = new PublicKeyHandle(identifier, refCount, pub);
702 mPublicKeys.put(identifier, pkh);
707 * Set each KeySet ref count. Also increment all public keys in each keyset.
709 private void addRefCountsFromSavedPackagesLPw(ArrayMap<Long, Integer> keySetRefCounts) {
710 final int numRefCounts = keySetRefCounts.size();
711 for (int i = 0; i < numRefCounts; i++) {
712 KeySetHandle ks = mKeySets.get(keySetRefCounts.keyAt(i));
713 ks.setRefCountLPw(keySetRefCounts.valueAt(i));
716 final int numKeySets = mKeySets.size();
717 for (int i = 0; i < numKeySets; i++) {
718 ArraySet<Long> pubKeys = mKeySetMapping.valueAt(i);
719 final int pkSize = pubKeys.size();
720 for (int j = 0; j < pkSize; j++) {
721 mPublicKeys.get(pubKeys.valueAt(j)).incrRefCountLPw();