summaryrefslogtreecommitdiffstats
path: root/luni/src/test/java/libcore/java/security/ProviderTest.java
blob: 78608d0055159e7b2cfc4422f0f991c141352de4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package libcore.java.security;

import java.security.Provider;
import java.security.SecureRandom;
import java.security.SecureRandomSpi;
import java.security.Security;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.SecureRandomSpi;
import java.security.Security;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import junit.framework.TestCase;

public class ProviderTest extends TestCase {

    /**
     * Makes sure all all expected implementations (but not aliases)
     * and that there are no extras, according to what we expect from
     * StandardNames
     */
    public void test_Provider_getServices() throws Exception {

        // build set of expected algorithms
        Map<String,Set<String>> remaining
                = new HashMap<String,Set<String>>(StandardNames.PROVIDER_ALGORITHMS);
        for (Entry<String,Set<String>> entry : remaining.entrySet()) {
            entry.setValue(new HashSet<String>(entry.getValue()));
        }

        List<String> extra = new ArrayList();
        List<String> missing = new ArrayList();

        Provider[] providers = Security.getProviders();
        for (Provider provider : providers) {
            String providerName = provider.getName();
            // ignore BouncyCastle provider if it is installed on the RI
            if (StandardNames.IS_RI && providerName.equals("BC")) {
                continue;
            }
            Set<Provider.Service> services = provider.getServices();
            assertNotNull(services);
            assertFalse(services.isEmpty());

            for (Provider.Service service : services) {
                String type = service.getType();
                String algorithm = service.getAlgorithm().toUpperCase();
                String className = service.getClassName();
                if (false) {
                    System.out.println(providerName
                                       + " " + type
                                       + " " + algorithm
                                       + " " + className);
                }

                // remove from remaining, assert unknown if missing
                Set<String> algorithms = remaining.get(type);
                if (algorithms == null || !algorithms.remove(algorithm)) {
                    // seems to be missing, but sometimes the same
                    // algorithm is available from multiple providers
                    // (e.g. KeyFactory RSA is available from
                    // SunRsaSign and SunJSSE), so double check in
                    // original source before giving error
                    if (!(StandardNames.PROVIDER_ALGORITHMS.containsKey(type)
                            && StandardNames.PROVIDER_ALGORITHMS.get(type).contains(algorithm))) {
                        extra.add("Unknown " + type + " " + algorithm + " " + providerName + "\n");
                    }
                }
                if (algorithms != null && algorithms.isEmpty()) {
                    remaining.remove(type);
                }

                // make sure class exists and can be initialized
                try {
                    assertNotNull(Class.forName(className,
                                                true,
                                                provider.getClass().getClassLoader()));
                } catch (ClassNotFoundException e) {
                    // Sun forgot their own class
                    if (!className.equals("sun.security.pkcs11.P11MAC")) {
                        missing.add(className);
                    }
                }
            }
        }

        // assert that we don't have any extra in the implementation
        Collections.sort(extra); // sort so that its grouped by type
        assertEquals("Extra algorithms", Collections.EMPTY_LIST, extra);

        // assert that we don't have any missing in the implementation
        assertEquals("Missing algorithms", Collections.EMPTY_MAP, remaining);

        // assert that we don't have any missing classes
        Collections.sort(missing); // sort it for readability
        assertEquals("Missing classes", Collections.EMPTY_LIST, missing);
    }

    private static final Pattern alias = Pattern.compile("Alg\\.Alias\\.([^.]*)\\.(.*)");

    /**
     * Makes sure all provider properties either point to a class
     * implementation that exists or are aliases to known algorithms.
     */
    public void test_Provider_Properties() throws Exception {
        /*
         * A useful reference on Provider properties
         * <a href="http://java.sun.com/javase/6/docs/technotes/guides/security/crypto/HowToImplAProvider.html>
         * How to Implement a Provider in the Java &trade; Cryptography Architecture
         * </a>
         */

        Provider[] providers = Security.getProviders();
        for (Provider provider : providers) {
            // check Provider.id proprieties
            assertEquals(provider.getName(),
                         provider.get("Provider.id name"));
            assertEquals(String.valueOf(provider.getVersion()),
                         provider.get("Provider.id version"));
            assertEquals(provider.getInfo(),
                         provider.get("Provider.id info"));
            assertEquals(provider.getClass().getName(),
                         provider.get("Provider.id className"));

            // build map of all known aliases and implementations
            Map<String,String> aliases = new HashMap<String,String>();
            Map<String,String> implementations = new HashMap<String,String>();
            for (Entry<Object,Object> entry : provider.entrySet()) {
                Object k = entry.getKey();
                Object v = entry.getValue();
                assertEquals(String.class, k.getClass());
                assertEquals(String.class, v.getClass());
                String key = (String)k;
                String value = (String)v;

                // skip Provider.id keys, we check well known ones values above
                if (key.startsWith("Provider.id ")) {
                    continue;
                }

                // skip property settings such as: "Signature.SHA1withDSA ImplementedIn" "Software"
                if (key.indexOf(' ') != -1) {
                    continue;
                }

                Matcher m = alias.matcher(key);
                if (m.find()) {
                    String type = m.group(1);
                    aliases.put(key, type + "." + value);
                } else {
                    implementations.put(key, value);
                }
            }

            // verify implementation classes are available
            for (Entry<String,String> entry : implementations.entrySet()) {
                String typeAndAlgorithm = entry.getKey();
                String className = entry.getValue();
                try {
                    assertNotNull(Class.forName(className,
                                                true,
                                                provider.getClass().getClassLoader()));
                } catch (ClassNotFoundException e) {
                    // Sun forgot their own class
                    if (!className.equals("sun.security.pkcs11.P11MAC")) {
                        fail("Could not find class " + className + " for " + typeAndAlgorithm);
                    }
                }
            }

            // make sure all aliases point to some known implementation
            for (Entry<String,String> entry : aliases.entrySet()) {
                String alias  = entry.getKey();
                String actual = entry.getValue();
                assertTrue("Could not find implementation " + actual + " for alias " + alias,
                           implementations.containsKey(actual));
            }
        }
    }

    /**
     * http://code.google.com/p/android/issues/detail?id=21449
     */
    public void testSecureRandomImplementationOrder() {
        Provider srp = new SRProvider();
        try {
            int position = Security.insertProviderAt(srp, 1); // first is one, not zero
            assertEquals(1, position);
            SecureRandom sr = new SecureRandom();
            if (!sr.getAlgorithm().equals("SecureRandom1")) {
                throw new IllegalStateException("Expected SecureRandom1");
            }
        } finally {
            Security.removeProvider(srp.getName());
        }
    }

    public static class SRProvider extends Provider {

        SRProvider() {
            super("SRProvider", 1.42, "SecureRandom Provider");
            put("SecureRandom.SecureRandom1", SecureRandom1.class.getName());
            put("SecureRandom.SecureRandom2", SecureRandom2.class.getName());
            put("SecureRandom.SecureRandom3", SecureRandom3.class.getName());
        }
    }

    public static abstract class AbstractSecureRandom extends SecureRandomSpi {
        protected void engineSetSeed(byte[] seed) {
            throw new UnsupportedOperationException();
        }
        protected void engineNextBytes(byte[] bytes) {
            throw new UnsupportedOperationException();
        }
        protected byte[] engineGenerateSeed(int numBytes) {
            throw new UnsupportedOperationException();
        }
    }

    public static class SecureRandom1 extends AbstractSecureRandom {}
    public static class SecureRandom2 extends AbstractSecureRandom {}
    public static class SecureRandom3 extends AbstractSecureRandom {}

}