18f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki/*
28f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki *  Licensed to the Apache Software Foundation (ASF) under one or more
38f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki *  contributor license agreements.  See the NOTICE file distributed with
48f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki *  this work for additional information regarding copyright ownership.
58f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki *  The ASF licenses this file to You under the Apache License, Version 2.0
68f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki *  (the "License"); you may not use this file except in compliance with
78f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki *  the License.  You may obtain a copy of the License at
88f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki *
98f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki *     http://www.apache.org/licenses/LICENSE-2.0
108f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki *
118f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki *  Unless required by applicable law or agreed to in writing, software
128f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki *  distributed under the License is distributed on an "AS IS" BASIS,
138f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
148f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki *  See the License for the specific language governing permissions and
158f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki *  limitations under the License.
168f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki */
178f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
182269d1572e5fcfb725ea55f5764d8c3280d69f6dDianne Hackbornpackage com.android.internal.net;
192269d1572e5fcfb725ea55f5764d8c3280d69f6dDianne Hackborn
208f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
218f028a94fc533e75077485a7d11a04e4de820335Makoto Onukiimport android.util.Log;
228f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
238f028a94fc533e75077485a7d11a04e4de820335Makoto Onukiimport java.io.IOException;
248f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
258f028a94fc533e75077485a7d11a04e4de820335Makoto Onukiimport javax.security.auth.x500.X500Principal;
268f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
278f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki/**
288f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * A simple distinguished name(DN) parser.
298f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki *
308f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * <p>This class is based on org.apache.harmony.security.x509.DNParser.  It's customized to remove
318f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * external references which are unnecessary for our requirements.
328f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki *
338f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * <p>This class is only meant for extracting a string value from a DN.  e.g. it doesn't support
348f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * values in the hex-string style.
358f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki *
368f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * <p>This class is used by {@link DomainNameValidator} only.  However, in order to make this
378f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki * class visible from unit tests, it's made public.
382269d1572e5fcfb725ea55f5764d8c3280d69f6dDianne Hackborn *
392269d1572e5fcfb725ea55f5764d8c3280d69f6dDianne Hackborn * @hide
408f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki */
418f028a94fc533e75077485a7d11a04e4de820335Makoto Onukipublic final class DNParser {
428f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    private static final String TAG = "DNParser";
438f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
448f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    /** DN to be parsed. */
458f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    private final String dn;
468f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
478f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    // length of distinguished name string
488f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    private final int length;
498f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
508f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    private int pos, beg, end;
518f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
528f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    // tmp vars to store positions of the currently parsed item
538f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    private int cur;
548f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
558f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    // distinguished name chars
568f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    private char[] chars;
578f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
588f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    /**
598f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki     * Exception message thrown when we failed to parse DN, which shouldn't happen because we
608f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki     * only handle DNs that {@link X500Principal#getName} returns, which shouldn't be malformed.
618f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki     */
628f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    private static final String ERROR_PARSE_ERROR = "Failed to parse DN";
638f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
648f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    /**
658f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki     * Constructor.
668f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki     *
678f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki     * @param principal - {@link X500Principal} to be parsed
688f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki     */
698f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    public DNParser(X500Principal principal) {
708f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        this.dn = principal.getName(X500Principal.RFC2253);
718f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        this.length = dn.length();
728f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    }
738f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
748f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    // gets next attribute type: (ALPHA 1*keychar) / oid
758f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    private String nextAT() throws IOException {
768f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
778f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        // skip preceding space chars, they can present after
788f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        // comma or semicolon (compatibility with RFC 1779)
798f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        for (; pos < length && chars[pos] == ' '; pos++) {
808f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        }
818f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        if (pos == length) {
828f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            return null; // reached the end of DN
838f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        }
848f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
858f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        // mark the beginning of attribute type
868f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        beg = pos;
878f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
888f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        // attribute type chars
898f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        pos++;
908f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        for (; pos < length && chars[pos] != '=' && chars[pos] != ' '; pos++) {
918f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            // we don't follow exact BNF syntax here:
928f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            // accept any char except space and '='
938f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        }
948f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        if (pos >= length) {
958f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            // unexpected end of DN
968f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            throw new IOException(ERROR_PARSE_ERROR);
978f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        }
988f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
998f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        // mark the end of attribute type
1008f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        end = pos;
1018f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
1028f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        // skip trailing space chars between attribute type and '='
1038f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        // (compatibility with RFC 1779)
1048f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        if (chars[pos] == ' ') {
1058f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            for (; pos < length && chars[pos] != '=' && chars[pos] == ' '; pos++) {
1068f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            }
1078f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
1088f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            if (chars[pos] != '=' || pos == length) {
1098f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                // unexpected end of DN
1108f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                throw new IOException(ERROR_PARSE_ERROR);
1118f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            }
1128f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        }
1138f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
1148f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        pos++; //skip '=' char
1158f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
1168f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        // skip space chars between '=' and attribute value
1178f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        // (compatibility with RFC 1779)
1188f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        for (; pos < length && chars[pos] == ' '; pos++) {
1198f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        }
1208f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
1218f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        // in case of oid attribute type skip its prefix: "oid." or "OID."
1228f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        // (compatibility with RFC 1779)
1238f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        if ((end - beg > 4) && (chars[beg + 3] == '.')
1248f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                && (chars[beg] == 'O' || chars[beg] == 'o')
1258f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                && (chars[beg + 1] == 'I' || chars[beg + 1] == 'i')
1268f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                && (chars[beg + 2] == 'D' || chars[beg + 2] == 'd')) {
1278f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            beg += 4;
1288f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        }
1298f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
1308f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        return new String(chars, beg, end - beg);
1318f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    }
1328f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
1338f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    // gets quoted attribute value: QUOTATION *( quotechar / pair ) QUOTATION
1348f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    private String quotedAV() throws IOException {
1358f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
1368f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        pos++;
1378f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        beg = pos;
1388f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        end = beg;
1398f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        while (true) {
1408f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
1418f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            if (pos == length) {
1428f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                // unexpected end of DN
1438f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                throw new IOException(ERROR_PARSE_ERROR);
1448f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            }
1458f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
1468f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            if (chars[pos] == '"') {
1478f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                // enclosing quotation was found
1488f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                pos++;
1498f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                break;
1508f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            } else if (chars[pos] == '\\') {
1518f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                chars[end] = getEscaped();
1528f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            } else {
1538f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                // shift char: required for string with escaped chars
1548f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                chars[end] = chars[pos];
1558f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            }
1568f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            pos++;
1578f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            end++;
1588f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        }
1598f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
1608f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        // skip trailing space chars before comma or semicolon.
1618f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        // (compatibility with RFC 1779)
1628f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        for (; pos < length && chars[pos] == ' '; pos++) {
1638f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        }
1648f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
1658f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        return new String(chars, beg, end - beg);
1668f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    }
1678f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
1688f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    // gets hex string attribute value: "#" hexstring
1698f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    private String hexAV() throws IOException {
1708f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
1718f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        if (pos + 4 >= length) {
1728f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            // encoded byte array  must be not less then 4 c
1738f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            throw new IOException(ERROR_PARSE_ERROR);
1748f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        }
1758f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
1768f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        beg = pos; // store '#' position
1778f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        pos++;
1788f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        while (true) {
1798f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
1808f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            // check for end of attribute value
1818f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            // looks for space and component separators
1828f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            if (pos == length || chars[pos] == '+' || chars[pos] == ','
1838f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                    || chars[pos] == ';') {
1848f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                end = pos;
1858f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                break;
1868f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            }
1878f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
1888f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            if (chars[pos] == ' ') {
1898f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                end = pos;
1908f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                pos++;
1918f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                // skip trailing space chars before comma or semicolon.
1928f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                // (compatibility with RFC 1779)
1938f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                for (; pos < length && chars[pos] == ' '; pos++) {
1948f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                }
1958f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                break;
1968f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            } else if (chars[pos] >= 'A' && chars[pos] <= 'F') {
1978f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                chars[pos] += 32; //to low case
1988f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            }
1998f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
2008f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            pos++;
2018f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        }
2028f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
2038f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        // verify length of hex string
2048f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        // encoded byte array  must be not less then 4 and must be even number
2058f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        int hexLen = end - beg; // skip first '#' char
2068f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        if (hexLen < 5 || (hexLen & 1) == 0) {
2078f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            throw new IOException(ERROR_PARSE_ERROR);
2088f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        }
2098f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
2108f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        // get byte encoding from string representation
2118f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        byte[] encoded = new byte[hexLen / 2];
2128f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        for (int i = 0, p = beg + 1; i < encoded.length; p += 2, i++) {
2138f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            encoded[i] = (byte) getByte(p);
2148f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        }
2158f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
2168f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        return new String(chars, beg, hexLen);
2178f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    }
2188f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
2198f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    // gets string attribute value: *( stringchar / pair )
2208f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    private String escapedAV() throws IOException {
2218f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
2228f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        beg = pos;
2238f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        end = pos;
2248f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        while (true) {
2258f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
2268f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            if (pos >= length) {
2278f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                // the end of DN has been found
2288f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                return new String(chars, beg, end - beg);
2298f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            }
2308f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
2318f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            switch (chars[pos]) {
2328f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            case '+':
2338f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            case ',':
2348f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            case ';':
2358f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                // separator char has beed found
2368f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                return new String(chars, beg, end - beg);
2378f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            case '\\':
2388f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                // escaped char
2398f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                chars[end++] = getEscaped();
2408f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                pos++;
2418f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                break;
2428f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            case ' ':
2438f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                // need to figure out whether space defines
2448f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                // the end of attribute value or not
2458f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                cur = end;
2468f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
2478f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                pos++;
2488f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                chars[end++] = ' ';
2498f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
2508f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                for (; pos < length && chars[pos] == ' '; pos++) {
2518f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                    chars[end++] = ' ';
2528f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                }
2538f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                if (pos == length || chars[pos] == ',' || chars[pos] == '+'
2548f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                        || chars[pos] == ';') {
2558f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                    // separator char or the end of DN has beed found
2568f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                    return new String(chars, beg, cur - beg);
2578f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                }
2588f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                break;
2598f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            default:
2608f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                chars[end++] = chars[pos];
2618f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                pos++;
2628f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            }
2638f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        }
2648f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    }
2658f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
2668f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    // returns escaped char
2678f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    private char getEscaped() throws IOException {
2688f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
2698f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        pos++;
2708f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        if (pos == length) {
2718f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            throw new IOException(ERROR_PARSE_ERROR);
2728f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        }
2738f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
2748f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        switch (chars[pos]) {
2758f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        case '"':
2768f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        case '\\':
2778f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        case ',':
2788f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        case '=':
2798f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        case '+':
2808f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        case '<':
2818f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        case '>':
2828f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        case '#':
2838f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        case ';':
2848f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        case ' ':
2858f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        case '*':
2868f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        case '%':
2878f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        case '_':
2888f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            //FIXME: escaping is allowed only for leading or trailing space char
2898f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            return chars[pos];
2908f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        default:
2918f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            // RFC doesn't explicitly say that escaped hex pair is
2928f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            // interpreted as UTF-8 char. It only contains an example of such DN.
2938f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            return getUTF8();
2948f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        }
2958f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    }
2968f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
2978f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    // decodes UTF-8 char
2988f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    // see http://www.unicode.org for UTF-8 bit distribution table
2998f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    private char getUTF8() throws IOException {
3008f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
3018f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        int res = getByte(pos);
3028f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        pos++; //FIXME tmp
3038f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
3048f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        if (res < 128) { // one byte: 0-7F
3058f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            return (char) res;
3068f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        } else if (res >= 192 && res <= 247) {
3078f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
3088f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            int count;
3098f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            if (res <= 223) { // two bytes: C0-DF
3108f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                count = 1;
3118f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                res = res & 0x1F;
3128f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            } else if (res <= 239) { // three bytes: E0-EF
3138f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                count = 2;
3148f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                res = res & 0x0F;
3158f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            } else { // four bytes: F0-F7
3168f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                count = 3;
3178f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                res = res & 0x07;
3188f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            }
3198f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
3208f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            int b;
3218f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            for (int i = 0; i < count; i++) {
3228f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                pos++;
3238f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                if (pos == length || chars[pos] != '\\') {
3248f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                    return 0x3F; //FIXME failed to decode UTF-8 char - return '?'
3258f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                }
3268f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                pos++;
3278f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
3288f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                b = getByte(pos);
3298f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                pos++; //FIXME tmp
3308f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                if ((b & 0xC0) != 0x80) {
3318f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                    return 0x3F; //FIXME failed to decode UTF-8 char - return '?'
3328f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                }
3338f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
3348f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                res = (res << 6) + (b & 0x3F);
3358f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            }
3368f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            return (char) res;
3378f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        } else {
3388f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            return 0x3F; //FIXME failed to decode UTF-8 char - return '?'
3398f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        }
3408f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    }
3418f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
3428f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    // Returns byte representation of a char pair
3438f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    // The char pair is composed of DN char in
3448f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    // specified 'position' and the next char
3458f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    // According to BNF syntax:
3468f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    // hexchar    = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
3478f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    //                    / "a" / "b" / "c" / "d" / "e" / "f"
3488f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    private int getByte(int position) throws IOException {
3498f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
3508f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        if ((position + 1) >= length) {
3518f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            // to avoid ArrayIndexOutOfBoundsException
3528f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            throw new IOException(ERROR_PARSE_ERROR);
3538f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        }
3548f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
3558f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        int b1, b2;
3568f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
3578f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        b1 = chars[position];
3588f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        if (b1 >= '0' && b1 <= '9') {
3598f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            b1 = b1 - '0';
3608f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        } else if (b1 >= 'a' && b1 <= 'f') {
3618f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            b1 = b1 - 87; // 87 = 'a' - 10
3628f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        } else if (b1 >= 'A' && b1 <= 'F') {
3638f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            b1 = b1 - 55; // 55 = 'A' - 10
3648f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        } else {
3658f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            throw new IOException(ERROR_PARSE_ERROR);
3668f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        }
3678f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
3688f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        b2 = chars[position + 1];
3698f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        if (b2 >= '0' && b2 <= '9') {
3708f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            b2 = b2 - '0';
3718f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        } else if (b2 >= 'a' && b2 <= 'f') {
3728f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            b2 = b2 - 87; // 87 = 'a' - 10
3738f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        } else if (b2 >= 'A' && b2 <= 'F') {
3748f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            b2 = b2 - 55; // 55 = 'A' - 10
3758f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        } else {
3768f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            throw new IOException(ERROR_PARSE_ERROR);
3778f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        }
3788f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
3798f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        return (b1 << 4) + b2;
3808f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    }
3818f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
3828f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    /**
3838f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki     * Parses the DN and returns the attribute value for an attribute type.
3848f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki     *
3858f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki     * @param attributeType attribute type to look for (e.g. "ca")
3868f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki     * @return value of the attribute that first found, or null if none found
3878f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki     */
3888f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    public String find(String attributeType) {
3898f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        try {
3908f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            // Initialize internal state.
3918f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            pos = 0;
3928f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            beg = 0;
3938f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            end = 0;
3948f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            cur = 0;
3958f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            chars = dn.toCharArray();
3968f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
3978f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            String attType = nextAT();
3988f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            if (attType == null) {
3998f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                return null;
4008f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            }
4018f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            while (true) {
4028f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                String attValue = "";
4038f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
4048f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                if (pos == length) {
4058f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                    return null;
4068f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                }
4078f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
4088f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                switch (chars[pos]) {
4098f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                case '"':
4108f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                    attValue = quotedAV();
4118f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                    break;
4128f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                case '#':
4138f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                    attValue = hexAV();
4148f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                    break;
4158f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                case '+':
4168f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                case ',':
4178f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                case ';': // compatibility with RFC 1779: semicolon can separate RDNs
4188f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                    //empty attribute value
4198f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                    break;
4208f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                default:
4218f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                    attValue = escapedAV();
4228f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                }
4238f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
4248f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                if (attributeType.equalsIgnoreCase(attType)) {
4258f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                    return attValue;
4268f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                }
4278f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
4288f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                if (pos >= length) {
4298f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                    return null;
4308f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                }
4318f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
4328f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                if (chars[pos] == ',' || chars[pos] == ';') {
4338f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                } else if (chars[pos] != '+') {
4348f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                    throw new IOException(ERROR_PARSE_ERROR);
4358f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                }
4368f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki
4378f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                pos++;
4388f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                attType = nextAT();
4398f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                if (attType == null) {
4408f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                    throw new IOException(ERROR_PARSE_ERROR);
4418f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki                }
4428f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            }
4438f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        } catch (IOException e) {
4448f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            // Parse error shouldn't happen, because we only handle DNs that
4458f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            // X500Principal.getName() returns, which shouldn't be malformed.
4468f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            Log.e(TAG, "Failed to parse DN: " + dn);
4478f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki            return null;
4488f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki        }
4498f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki    }
4508f028a94fc533e75077485a7d11a04e4de820335Makoto Onuki}
451