1 /* Copyright 2019, The Android Open Source Project, Inc. 2 * 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package com.google.android.attestation; 17 18 import static com.google.android.attestation.Constants.ATTESTATION_APPLICATION_ID_PACKAGE_INFOS_INDEX; 19 import static com.google.android.attestation.Constants.ATTESTATION_APPLICATION_ID_SIGNATURE_DIGESTS_INDEX; 20 import static com.google.android.attestation.Constants.ATTESTATION_PACKAGE_INFO_PACKAGE_NAME_INDEX; 21 import static com.google.android.attestation.Constants.ATTESTATION_PACKAGE_INFO_VERSION_INDEX; 22 import static java.nio.charset.StandardCharsets.UTF_8; 23 24 import java.io.IOException; 25 import java.util.ArrayList; 26 import java.util.Arrays; 27 import java.util.List; 28 import java.util.Objects; 29 import org.bouncycastle.asn1.ASN1Encodable; 30 import org.bouncycastle.asn1.ASN1Integer; 31 import org.bouncycastle.asn1.ASN1OctetString; 32 import org.bouncycastle.asn1.ASN1Sequence; 33 import org.bouncycastle.asn1.ASN1Set; 34 import org.bouncycastle.asn1.DEROctetString; 35 36 /** 37 * This data structure reflects the Android platform's belief as to which apps are allowed to use 38 * the secret key material under attestation. The ID can comprise multiple packages if and only if 39 * multiple packages share the same UID. 40 */ 41 public class AttestationApplicationId implements Comparable<AttestationApplicationId> { 42 public final List<AttestationPackageInfo> packageInfos; 43 public final List<byte[]> signatureDigests; 44 AttestationApplicationId(DEROctetString attestationApplicationId)45 private AttestationApplicationId(DEROctetString attestationApplicationId) throws IOException { 46 ASN1Sequence attestationApplicationIdSequence = 47 (ASN1Sequence) ASN1Sequence.fromByteArray(attestationApplicationId.getOctets()); 48 ASN1Set attestationPackageInfos = 49 (ASN1Set) 50 attestationApplicationIdSequence.getObjectAt( 51 ATTESTATION_APPLICATION_ID_PACKAGE_INFOS_INDEX); 52 this.packageInfos = new ArrayList<>(); 53 for (ASN1Encodable packageInfo : attestationPackageInfos) { 54 this.packageInfos.add(new AttestationPackageInfo((ASN1Sequence) packageInfo)); 55 } 56 57 ASN1Set digests = 58 (ASN1Set) 59 attestationApplicationIdSequence.getObjectAt( 60 ATTESTATION_APPLICATION_ID_SIGNATURE_DIGESTS_INDEX); 61 this.signatureDigests = new ArrayList<>(); 62 for (ASN1Encodable digest : digests) { 63 this.signatureDigests.add(((ASN1OctetString) digest).getOctets()); 64 } 65 } 66 AttestationApplicationId( List<AttestationPackageInfo> packageInfos, List<byte[]> signatureDigests)67 AttestationApplicationId( 68 List<AttestationPackageInfo> packageInfos, List<byte[]> signatureDigests) { 69 this.packageInfos = packageInfos; 70 this.signatureDigests = signatureDigests; 71 } 72 createAttestationApplicationId( DEROctetString attestationApplicationId)73 static AttestationApplicationId createAttestationApplicationId( 74 DEROctetString attestationApplicationId) { 75 if (attestationApplicationId == null) { 76 return null; 77 } 78 try { 79 return new AttestationApplicationId(attestationApplicationId); 80 } catch (IOException e) { 81 return null; 82 } 83 } 84 85 @Override compareTo(AttestationApplicationId other)86 public int compareTo(AttestationApplicationId other) { 87 int res = Integer.compare(packageInfos.size(), other.packageInfos.size()); 88 if (res != 0) { 89 return res; 90 } 91 for (int i = 0; i < packageInfos.size(); ++i) { 92 res = packageInfos.get(i).compareTo(other.packageInfos.get(i)); 93 if (res != 0) { 94 return res; 95 } 96 } 97 res = Integer.compare(signatureDigests.size(), other.signatureDigests.size()); 98 if (res != 0) { 99 return res; 100 } 101 ByteArrayComparator cmp = new ByteArrayComparator(); 102 for (int i = 0; i < signatureDigests.size(); ++i) { 103 res = cmp.compare(signatureDigests.get(i), other.signatureDigests.get(i)); 104 if (res != 0) { 105 return res; 106 } 107 } 108 return res; 109 } 110 111 @Override equals(Object o)112 public boolean equals(Object o) { 113 return (o instanceof AttestationApplicationId) 114 && (compareTo((AttestationApplicationId) o) == 0); 115 } 116 117 @Override hashCode()118 public int hashCode() { 119 return Objects.hash(packageInfos, Arrays.deepHashCode(signatureDigests.toArray())); 120 } 121 122 /** Provides package's name and version number. */ 123 public static class AttestationPackageInfo implements Comparable<AttestationPackageInfo> { 124 public final String packageName; 125 public final long version; 126 AttestationPackageInfo(ASN1Sequence packageInfo)127 private AttestationPackageInfo(ASN1Sequence packageInfo) { 128 this.packageName = 129 new String( 130 ((ASN1OctetString) 131 packageInfo.getObjectAt(ATTESTATION_PACKAGE_INFO_PACKAGE_NAME_INDEX)) 132 .getOctets(), 133 UTF_8); 134 this.version = 135 ((ASN1Integer) packageInfo.getObjectAt(ATTESTATION_PACKAGE_INFO_VERSION_INDEX)) 136 .getValue() 137 .longValue(); 138 } 139 AttestationPackageInfo(String packageName, long version)140 AttestationPackageInfo(String packageName, long version) { 141 this.packageName = packageName; 142 this.version = version; 143 } 144 145 @Override compareTo(AttestationPackageInfo other)146 public int compareTo(AttestationPackageInfo other) { 147 int res = packageName.compareTo(other.packageName); 148 if (res != 0) { 149 return res; 150 } 151 res = Long.compare(version, other.version); 152 if (res != 0) { 153 return res; 154 } 155 return res; 156 } 157 158 @Override equals(Object o)159 public boolean equals(Object o) { 160 return (o instanceof AttestationPackageInfo) && (compareTo((AttestationPackageInfo) o) == 0); 161 } 162 163 @Override hashCode()164 public int hashCode() { 165 return Objects.hash(packageName, version); 166 } 167 } 168 169 private static class ByteArrayComparator implements java.util.Comparator<byte[]> { 170 @Override compare(byte[] a, byte[] b)171 public int compare(byte[] a, byte[] b) { 172 int res = Integer.compare(a.length, b.length); 173 if (res != 0) { 174 return res; 175 } 176 for (int i = 0; i < a.length; ++i) { 177 res = Byte.compare(a[i], b[i]); 178 if (res != 0) { 179 return res; 180 } 181 } 182 return res; 183 } 184 } 185 } 186