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