1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18/**
19* @author Vladimir N. Molotkov
20* @version $Revision$
21*/
22
23package org.apache.harmony.security.utils;
24
25import java.security.Provider;
26import java.security.Security;
27import java.util.HashMap;
28import java.util.Locale;
29import java.util.Map;
30import java.util.Map.Entry;
31import java.util.Set;
32import org.apache.harmony.security.asn1.ObjectIdentifier;
33import org.apache.harmony.security.fortress.Services;
34
35/**
36 * Provides Algorithm Name to OID and OID to Algorithm Name mappings. Some known
37 * mappings are hardcoded. Tries to obtain additional mappings from installed
38 * providers during initialization.
39 */
40public class AlgNameMapper {
41    private static AlgNameMapperSource source = null;
42
43    private static volatile int cacheVersion = -1;
44
45    // Will search OID mappings for these services
46    private static final String[] serviceName = {
47            "Cipher",
48            "AlgorithmParameters",
49            "Signature"
50    };
51
52    // These mappings CAN NOT be overridden
53    // by the ones from available providers
54    // during maps initialization
55    // (source: http://asn1.elibel.tm.fr):
56    private static final String[][] knownAlgMappings = {
57        {"1.2.840.10040.4.1",       "DSA"},
58        {"1.2.840.10040.4.3",       "SHA1withDSA"},
59        {"1.2.840.113549.1.1.1",    "RSA"},
60        // BEGIN android-removed
61        // Dropping MD2
62        // {"1.2.840.113549.1.1.2",    "MD2withRSA"},
63        // END android-removed
64        {"1.2.840.113549.1.1.4",    "MD5withRSA"},
65        {"1.2.840.113549.1.1.5",    "SHA1withRSA"},
66        {"1.2.840.113549.1.3.1",    "DiffieHellman"},
67        {"1.2.840.113549.1.5.3",    "pbeWithMD5AndDES-CBC"},
68        {"1.2.840.113549.1.12.1.3", "pbeWithSHAAnd3-KeyTripleDES-CBC"},
69        {"1.2.840.113549.1.12.1.6", "pbeWithSHAAnd40BitRC2-CBC"},
70        {"1.2.840.113549.3.2",      "RC2-CBC"},
71        {"1.2.840.113549.3.3",      "RC2-EBC"},
72        {"1.2.840.113549.3.4",      "RC4"},
73        {"1.2.840.113549.3.5",      "RC4WithMAC"},
74        {"1.2.840.113549.3.6",      "DESx-CBC"},
75        {"1.2.840.113549.3.7",      "TripleDES-CBC"},
76        {"1.2.840.113549.3.8",      "rc5CBC"},
77        {"1.2.840.113549.3.9",      "RC5-CBC"},
78        {"1.2.840.113549.3.10",     "DESCDMF"},
79        {"2.23.42.9.11.4.1",        "ECDSA"},
80    };
81    // Maps alg name to OID
82    private static final Map<String, String> alg2OidMap = new HashMap<String, String>();
83    // Maps OID to alg name
84    private static final Map<String, String> oid2AlgMap = new HashMap<String, String>();
85    // Maps aliases to alg names
86    private static final Map<String, String> algAliasesMap = new HashMap<String, String>();
87
88    static {
89        for (String[] element : knownAlgMappings) {
90            String algUC = element[1].toUpperCase(Locale.US);
91            alg2OidMap.put(algUC, element[0]);
92            oid2AlgMap.put(element[0], algUC);
93            // map upper case alg name to its original name
94            algAliasesMap.put(algUC, element[1]);
95        }
96    }
97
98    // No instances
99    private AlgNameMapper() {
100    }
101
102    private static synchronized void checkCacheVersion() {
103        int newCacheVersion = Services.getCacheVersion();
104        if (newCacheVersion != cacheVersion) {
105            //
106            // Now search providers for mappings like
107            // Alg.Alias.<service>.<OID-INTS-DOT-SEPARATED>=<alg-name>
108            //  or
109            // Alg.Alias.<service>.OID.<OID-INTS-DOT-SEPARATED>=<alg-name>
110            //
111            Provider[] pl = Security.getProviders();
112            for (Provider element : pl) {
113                selectEntries(element);
114            }
115            cacheVersion = newCacheVersion;
116        }
117    }
118
119    /**
120     * Returns OID for algName
121     *
122     * @param algName algorithm name to be mapped
123     * @return OID as String
124     */
125    public static String map2OID(String algName) {
126        checkCacheVersion();
127
128        // alg2OidMap map contains upper case keys
129        String result = alg2OidMap.get(algName.toUpperCase(Locale.US));
130        if (result != null) {
131            return result;
132        }
133
134        // Check our external source.
135        AlgNameMapperSource s = source;
136        if (s != null) {
137            return s.mapNameToOid(algName);
138        }
139
140        return null;
141    }
142
143    /**
144     * Returns algName for OID
145     *
146     * @param oid OID to be mapped
147     * @return algorithm name
148     */
149    public static String map2AlgName(String oid) {
150        checkCacheVersion();
151
152        // oid2AlgMap map contains upper case values
153        String algUC = oid2AlgMap.get(oid);
154        // if not null there is always map UC->Orig
155        if (algUC != null) {
156            return algAliasesMap.get(algUC);
157        }
158
159        // Check our external source.
160        AlgNameMapperSource s = source;
161        if (s != null) {
162            return s.mapOidToName(oid);
163        }
164
165        return null;
166    }
167
168    /**
169     * Returns Algorithm name for given algorithm alias
170     *
171     * @param algName - alias
172     * @return algorithm name
173     */
174    public static String getStandardName(String algName) {
175        return algAliasesMap.get(algName.toUpperCase(Locale.US));
176    }
177
178    // Searches given provider for mappings like
179    // Alg.Alias.<service>.<OID-INTS-DOT-SEPARATED>=<alg-name>
180    //  or
181    // Alg.Alias.<service>.OID.<OID-INTS-DOT-SEPARATED>=<alg-name>
182    // Puts mappings found into appropriate internal maps
183    private static void selectEntries(Provider p) {
184        Set<Map.Entry<Object, Object>> entrySet = p.entrySet();
185        for (String service : serviceName) {
186            String keyPrfix2find = "Alg.Alias." + service + ".";
187            for (Entry<Object, Object> me : entrySet) {
188                String key = (String)me.getKey();
189                if (key.startsWith(keyPrfix2find)) {
190                    String alias = key.substring(keyPrfix2find.length());
191                    String alg = (String)me.getValue();
192                    String algUC = alg.toUpperCase(Locale.US);
193                    if (isOID(alias)) {
194                        if (alias.startsWith("OID.")) {
195                            alias = alias.substring(4);
196                        }
197                        // Do not overwrite already known mappings
198                        boolean oid2AlgContains = oid2AlgMap.containsKey(alias);
199                        boolean alg2OidContains = alg2OidMap.containsKey(algUC);
200                        if (!oid2AlgContains || !alg2OidContains) {
201                            if (!oid2AlgContains) {
202                                oid2AlgMap.put(alias, algUC);
203                            }
204                            if (!alg2OidContains) {
205                                alg2OidMap.put(algUC, alias);
206                            }
207                            // map upper case alg name to its original name
208                            algAliasesMap.put(algUC, alg);
209                        }
210                           // Do not override known standard names
211                    } else if (!algAliasesMap.containsKey(alias.toUpperCase(Locale.US))) {
212                        algAliasesMap.put(alias.toUpperCase(Locale.US), alg);
213                    }
214                }
215            }
216        }
217    }
218
219    /**
220     * Checks if parameter represents OID
221     *
222     * @param alias alias to be checked
223     * @return 'true' if parameter represents OID
224     */
225    public static boolean isOID(String alias) {
226        return ObjectIdentifier.isOID(normalize(alias));
227    }
228
229    /**
230     * Removes leading "OID." from oid String passed
231     *
232     * @param oid string that may contain leading "OID."
233     * @return string passed without leading "OID."
234     */
235    public static String normalize(String oid) {
236        return oid.startsWith("OID.")
237            ? oid.substring(4)
238            : oid;
239    }
240
241    public static void setSource(AlgNameMapperSource source) {
242        AlgNameMapper.source = source;
243    }
244}
245