1ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistpackage com.android.hotspot2.osu; 2ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 3ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport android.util.Log; 4ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 5ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport com.android.anqp.HSIconFileElement; 6ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport com.android.anqp.I18Name; 7ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport com.android.anqp.IconInfo; 8ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport com.android.hotspot2.Utils; 9ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport com.android.hotspot2.asn1.Asn1Class; 10ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport com.android.hotspot2.asn1.Asn1Constructed; 11ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport com.android.hotspot2.asn1.Asn1Decoder; 12ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport com.android.hotspot2.asn1.Asn1Integer; 13ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport com.android.hotspot2.asn1.Asn1Object; 14ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport com.android.hotspot2.asn1.Asn1Octets; 15ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport com.android.hotspot2.asn1.Asn1Oid; 16ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport com.android.hotspot2.asn1.Asn1String; 17ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport com.android.hotspot2.asn1.OidMappings; 180701952aaa17dcef461d3a538048243d9fe690f1Jan Nordqvistimport com.android.hotspot2.flow.OSUInfo; 19ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 20ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.io.IOException; 21ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.nio.ByteBuffer; 22ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.nio.charset.StandardCharsets; 23ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.security.GeneralSecurityException; 24ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.security.MessageDigest; 25ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.security.cert.X509Certificate; 26ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.ArrayList; 27ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.Arrays; 28ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.HashMap; 29ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.Iterator; 30ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.List; 31ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.Map; 32ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 33ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistpublic class SPVerifier { 34ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public static final int OtherName = 0; 35ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public static final int DNSName = 2; 36ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 37ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final OSUInfo mOSUInfo; 38ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 39ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public SPVerifier(OSUInfo osuInfo) { 40ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mOSUInfo = osuInfo; 41ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 42ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 43ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist /* 44ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist SEQUENCE: 45ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist [Context 0]: 46ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist SEQUENCE: 47ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist [Context 0]: -- LogotypeData 48ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist SEQUENCE: 49ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist SEQUENCE: 50ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist SEQUENCE: 51ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist IA5String='image/png' 52ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist SEQUENCE: 53ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist SEQUENCE: 54ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist SEQUENCE: 55ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist OID=2.16.840.1.101.3.4.2.1 56ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist NULL 57ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist OCTET_STRING= cf aa 74 a8 ad af 85 82 06 c8 f5 b5 bf ee 45 72 8a ee ea bd 47 ab 50 d3 62 0c 92 c1 53 c3 4c 6b 58ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist SEQUENCE: 59ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist IA5String='http://www.r2-testbed.wi-fi.org/icon_orange_zxx.png' 60ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist SEQUENCE: 61ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist INTEGER=4184 62ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist INTEGER=-128 63ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist INTEGER=61 64ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist [Context 4]= 7a 78 78 65ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist [Context 0]: -- LogotypeData 66ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist SEQUENCE: 67ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist SEQUENCE: -- LogotypeImage 68ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist SEQUENCE: -- LogoTypeDetails 69ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist IA5String='image/png' 70ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist SEQUENCE: 71ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist SEQUENCE: -- HashAlgAndValue 72ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist SEQUENCE: 73ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist OID=2.16.840.1.101.3.4.2.1 74ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist NULL 75ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist OCTET_STRING= cb 35 5c ba 7a 21 59 df 8e 0a e1 d8 9f a4 81 9e 41 8f af 58 0c 08 d6 28 7f 66 22 98 13 57 95 8d 76ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist SEQUENCE: 77ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist IA5String='http://www.r2-testbed.wi-fi.org/icon_orange_eng.png' 78ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist SEQUENCE: -- LogotypeImageInfo 79ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist INTEGER=11635 80ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist INTEGER=-96 81ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist INTEGER=76 82ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist [Context 4]= 65 6e 67 83ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist */ 84ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 85ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private static class LogoTypeImage { 86ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final String mMimeType; 87ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final List<HashAlgAndValue> mHashes = new ArrayList<>(); 88ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final List<String> mURIs = new ArrayList<>(); 89ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final int mFileSize; 90ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final int mXsize; 91ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final int mYsize; 92ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final String mLanguage; 93ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 94ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private LogoTypeImage(Asn1Constructed sequence) throws IOException { 95ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Iterator<Asn1Object> children = sequence.getChildren().iterator(); 96ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 97ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Iterator<Asn1Object> logoTypeDetails = 98ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist castObject(children.next(), Asn1Constructed.class).getChildren().iterator(); 99ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mMimeType = castObject(logoTypeDetails.next(), Asn1String.class).getString(); 100ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 101ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Asn1Constructed hashes = castObject(logoTypeDetails.next(), Asn1Constructed.class); 102ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist for (Asn1Object hash : hashes.getChildren()) { 103ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mHashes.add(new HashAlgAndValue(castObject(hash, Asn1Constructed.class))); 104ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 105ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Asn1Constructed urls = castObject(logoTypeDetails.next(), Asn1Constructed.class); 106ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist for (Asn1Object url : urls.getChildren()) { 107ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mURIs.add(castObject(url, Asn1String.class).getString()); 108ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 109ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 110ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist boolean imageInfoSet = false; 111ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist int fileSize = -1; 112ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist int xSize = -1; 113ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist int ySize = -1; 114ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist String language = null; 115ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 116ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (children.hasNext()) { 117ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Iterator<Asn1Object> imageInfo = 118ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist castObject(children.next(), Asn1Constructed.class).getChildren().iterator(); 119ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 120ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Asn1Object first = imageInfo.next(); 121ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (first.getTag() == 0) { 122ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist first = imageInfo.next(); // Ignore optional LogotypeImageType 123ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 124ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 125ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist fileSize = (int) castObject(first, Asn1Integer.class).getValue(); 126ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist xSize = (int) castObject(imageInfo.next(), Asn1Integer.class).getValue(); 127ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist ySize = (int) castObject(imageInfo.next(), Asn1Integer.class).getValue(); 128ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist imageInfoSet = true; 129ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 130ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (imageInfo.hasNext()) { 131ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Asn1Object next = imageInfo.next(); 132ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (next.getTag() != 4) { 133ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist next = imageInfo.hasNext() ? imageInfo.next() : null; // Skip resolution 134ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 135ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (next != null && next.getTag() == 4) { 136ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist language = new String(castObject(next, Asn1Octets.class).getOctets(), 137ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist StandardCharsets.US_ASCII); 138ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 139ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 140ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 141ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 142ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (imageInfoSet) { 143ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mFileSize = complement(fileSize); 144ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mXsize = complement(xSize); 145ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mYsize = complement(ySize); 146ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } else { 147ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mFileSize = mXsize = mYsize = -1; 148ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 149ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mLanguage = language; 150ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 151ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 152ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private boolean verify(OSUInfo osuInfo) throws GeneralSecurityException, IOException { 153ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist IconInfo iconInfo = osuInfo.getIconInfo(); 154ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist HSIconFileElement iconData = osuInfo.getIconFileElement(); 155ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (!iconInfo.getIconType().equals(mMimeType) || 156ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist !iconInfo.getLanguage().equals(mLanguage) || 157ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist iconData.getIconData().length != mFileSize) { 158ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return false; 159ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 160ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist for (HashAlgAndValue hash : mHashes) { 161ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (hash.getJCEName() != null) { 162ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist MessageDigest digest = MessageDigest.getInstance(hash.getJCEName()); 163ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist byte[] computed = digest.digest(iconData.getIconData()); 164ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (!Arrays.equals(computed, hash.getHash())) { 165ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist throw new IOException("Icon hash mismatch"); 166ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } else { 167ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Log.d(OSUManager.TAG, "Icon verified with " + hash.getJCEName()); 168ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return true; 169ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 170ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 171ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 172ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return false; 173ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 174ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 175ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist @Override 176ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public String toString() { 177ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return "LogoTypeImage{" + 178ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist "MimeType='" + mMimeType + '\'' + 179ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist ", hashes=" + mHashes + 180ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist ", URIs=" + mURIs + 181ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist ", fileSize=" + mFileSize + 182ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist ", xSize=" + mXsize + 183ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist ", ySize=" + mYsize + 184ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist ", language='" + mLanguage + '\'' + 185ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist '}'; 186ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 187ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 188ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 189ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private static class HashAlgAndValue { 190ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final String mJCEName; 191ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final byte[] mHash; 192ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 193ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private HashAlgAndValue(Asn1Constructed sequence) throws IOException { 194ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (sequence.getChildren().size() != 2) { 195ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist throw new IOException("Bad HashAlgAndValue"); 196ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 197ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Iterator<Asn1Object> children = sequence.getChildren().iterator(); 198ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mJCEName = OidMappings.getJCEName(getFirstInner(children.next(), Asn1Oid.class)); 199ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mHash = castObject(children.next(), Asn1Octets.class).getOctets(); 200ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 201ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 202ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public String getJCEName() { 203ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return mJCEName; 204ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 205ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 206ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public byte[] getHash() { 207ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return mHash; 208ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 209ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 210ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist @Override 211ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public String toString() { 212ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return "HashAlgAndValue{" + 213ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist "JCEName='" + mJCEName + '\'' + 214ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist ", hash=" + Utils.toHex(mHash) + 215ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist '}'; 216ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 217ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 218ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 219ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private static int complement(int value) { 220ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return value >= 0 ? value : (~value) + 1; 221ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 222ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 223ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private static <T extends Asn1Object> T castObject(Asn1Object object, Class<T> klass) 224ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist throws IOException { 225ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (object.getClass() != klass) { 226ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist throw new IOException("Object is an " + object.getClass().getSimpleName() + 227ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist " expected an " + klass.getSimpleName()); 228ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 229ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return klass.cast(object); 230ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 231ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 232ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private static <T extends Asn1Object> T getFirstInner(Asn1Object container, Class<T> klass) 233ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist throws IOException { 234ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (container.getClass() != Asn1Constructed.class) { 235ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist throw new IOException("Not a container"); 236ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 237ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Iterator<Asn1Object> children = container.getChildren().iterator(); 238ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (!children.hasNext()) { 239ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist throw new IOException("No content"); 240ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 241ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return castObject(children.next(), klass); 242ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 243ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 244ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public void verify(X509Certificate osuCert) throws IOException, GeneralSecurityException { 245ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (osuCert == null) { 246ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist throw new IOException("No OSU cert found"); 247ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 248ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 249ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist checkName(castObject(getExtension(osuCert, OidMappings.IdCeSubjectAltName), 250ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Asn1Constructed.class)); 251ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 252ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist List<LogoTypeImage> logos = getImageData(getExtension(osuCert, OidMappings.IdPeLogotype)); 253ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Log.d(OSUManager.TAG, "Logos: " + logos); 254ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist for (LogoTypeImage logoTypeImage : logos) { 255ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (logoTypeImage.verify(mOSUInfo)) { 256ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return; 257ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 258ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 259ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist throw new IOException("Failed to match icon against any cert logo"); 260ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 261ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 262ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private static List<LogoTypeImage> getImageData(Asn1Object logoExtension) throws IOException { 263ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Asn1Constructed logo = castObject(logoExtension, Asn1Constructed.class); 264ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Asn1Constructed communityLogo = castObject(logo.getChildren().iterator().next(), 265ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Asn1Constructed.class); 266ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (communityLogo.getTag() != 0) { 267ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist throw new IOException("Expected tag [0] for communityLogos"); 268ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 269ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 270ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist List<LogoTypeImage> images = new ArrayList<>(); 271ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Asn1Constructed communityLogoSeq = castObject(communityLogo.getChildren().iterator().next(), 272ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Asn1Constructed.class); 273ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist for (Asn1Object logoTypeData : communityLogoSeq.getChildren()) { 274ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (logoTypeData.getTag() != 0) { 275ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist throw new IOException("Expected tag [0] for LogotypeData"); 276ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 277ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist for (Asn1Object logoTypeImage : castObject(logoTypeData.getChildren().iterator().next(), 278ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Asn1Constructed.class).getChildren()) { 279ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist // only read the image SEQUENCE and skip any audio [1] tags 280ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (logoTypeImage.getAsn1Class() == Asn1Class.Universal) { 281ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist images.add(new LogoTypeImage(castObject(logoTypeImage, Asn1Constructed.class))); 282ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 283ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 284ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 285ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return images; 286ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 287ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 288ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private void checkName(Asn1Constructed altName) throws IOException { 289ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Map<String, I18Name> friendlyNames = new HashMap<>(); 290ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist for (Asn1Object name : altName.getChildren()) { 291ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (name.getAsn1Class() == Asn1Class.Context && name.getTag() == OtherName) { 292ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Asn1Constructed otherName = (Asn1Constructed) name; 293ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Iterator<Asn1Object> children = otherName.getChildren().iterator(); 294ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (children.hasNext()) { 295ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Asn1Object oidObject = children.next(); 296ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (OidMappings.sIdWfaHotspotFriendlyName.equals(oidObject) && 297ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist children.hasNext()) { 298ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Asn1Constructed value = castObject(children.next(), Asn1Constructed.class); 299ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist String text = castObject(value.getChildren().iterator().next(), 300ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Asn1String.class).getString(); 301ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist I18Name friendlyName = new I18Name(text); 302ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist friendlyNames.put(friendlyName.getLanguage(), friendlyName); 303ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 304ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 305ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 306ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 307ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Log.d(OSUManager.TAG, "Friendly names: " + friendlyNames.values()); 308ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist for (I18Name osuName : mOSUInfo.getOSUProvider().getNames()) { 309ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist I18Name friendlyName = friendlyNames.get(osuName.getLanguage()); 310ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (!osuName.equals(friendlyName)) { 311ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist throw new IOException("Friendly name '" + osuName + " not in certificate"); 312ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 313ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 314ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 315ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 316ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private static Asn1Object getExtension(X509Certificate certificate, String extension) 317ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist throws GeneralSecurityException, IOException { 318ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist byte[] data = certificate.getExtensionValue(extension); 319ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (data == null) { 320ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return null; 321ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 322ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Asn1Octets octetString = (Asn1Octets) Asn1Decoder.decode(ByteBuffer.wrap(data)). 323ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist iterator().next(); 324ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Asn1Constructed sequence = castObject(Asn1Decoder.decode( 325ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist ByteBuffer.wrap(octetString.getOctets())).iterator().next(), 326ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Asn1Constructed.class); 327ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Log.d(OSUManager.TAG, "Extension " + extension + ": " + sequence); 328ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return sequence; 329ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 330ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist} 331