1adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/*
2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  Licensed to the Apache Software Foundation (ASF) under one or more
3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  contributor license agreements.  See the NOTICE file distributed with
4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  this work for additional information regarding copyright ownership.
5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  The ASF licenses this file to You under the Apache License, Version 2.0
6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  (the "License"); you may not use this file except in compliance with
7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  the License.  You may obtain a copy of the License at
8adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
9adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *     http://www.apache.org/licenses/LICENSE-2.0
10adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  Unless required by applicable law or agreed to in writing, software
12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  distributed under the License is distributed on an "AS IS" BASIS,
13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  See the License for the specific language governing permissions and
15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  limitations under the License.
16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
17adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
18adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/**
19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project* @author Boris V. Kuznetsov
20adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project* @version $Revision$
21adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project*/
22adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
23adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpackage org.apache.harmony.security.fortress;
24adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
25adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.security.NoSuchAlgorithmException;
26adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.security.Provider;
27a695e8fafadd2591cd148e78f19bc6d7c15121bbJesse Wilsonimport java.util.Locale;
28adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
29adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
30adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/**
310a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom * This class implements common functionality for Provider supplied
320a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom * classes. The usage pattern is to allocate static Engine instance
330a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom * per service type and synchronize on that instance during calls to
340a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom * {@code getInstance} and retreival of the selected {@code Provider}
350a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom * and Service Provider Interface (SPI) results. Retreiving the
360a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom * results with {@code getProvider} and {@code getSpi} sets the
370a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom * internal {@code Engine} values to null to prevent memory leaks.
38f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes *
390a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom * <p>
40f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes *
410a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom * For example: <pre>   {@code
420a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *   public class Foo {
430a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *
440a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *       private static final Engine ENGINE = new Engine("Foo");
450a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *
460a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *       private final FooSpi spi;
470a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *       private final Provider provider;
480a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *       private final String algorithm;
490a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *
500a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *       protected Foo(FooSpi spi,
510a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *                     Provider provider,
520a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *                     String algorithm) {
530a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *           this.spi = spi;
540a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *           this.provider = provider;
550a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *           this.algorithm = algorithm;
560a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *       }
570a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *
580a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *       public static Foo getInstance(String algorithm) {
596cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom *           Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
606cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom *           return new Foo((FooSpi) sap.spi, sap.provider, algorithm);
610a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *       }
620a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *
630a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *       public static Foo getInstance(String algorithm, Provider provider) {
646cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom *           Object spi = ENGINE.getInstance(algorithm, provider, null);
656cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom *           return new Foo((FooSpi) spi, provider, algorithm);
660a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *       }
670a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *
680a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *       ...
690a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom *
700a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom * }</pre>
71adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
72adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpublic class Engine {
73adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
740a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom    /**
750a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom     * Access to package visible api in java.security
760a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom     */
770a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom    public static SecurityAccess door;
780a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom
79adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
806cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom     * Service name such as Cipher or SSLContext
81adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
826cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom    private final String serviceName;
83adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
84adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
856cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom     * Previous result for getInstance(String, Object) optimization.
866cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom     * Only this non-Provider version of getInstance is optimized
876cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom     * since the the Provider version does not require an expensive
886cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom     * Services.getService call.
89adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
906cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom    private volatile ServiceCacheEntry serviceCache;
916cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom
926cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom    private static final class ServiceCacheEntry {
936cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom        /** used to test for cache hit */
946cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom        private final String algorithm;
956cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom        /** used to test for cache validity */
96c7a56c94bdea0e20123ae6fba5eca5b8d984c670Brian Carlstrom        private final int cacheVersion;
976cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom        /** cached result */
986cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom        private final Provider.Service service;
996cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom
1006cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom        private ServiceCacheEntry(String algorithm,
101c7a56c94bdea0e20123ae6fba5eca5b8d984c670Brian Carlstrom                                  int cacheVersion,
1026cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom                                  Provider.Service service) {
1036cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom            this.algorithm = algorithm;
104c7a56c94bdea0e20123ae6fba5eca5b8d984c670Brian Carlstrom            this.cacheVersion = cacheVersion;
1056cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom            this.service = service;
1066cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom        }
1076cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom    }
1086cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom
1096cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom    public static final class SpiAndProvider {
1106cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom        public final Object spi;
1116cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom        public final Provider provider;
1126cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom        private SpiAndProvider(Object spi, Provider provider) {
1136cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom            this.spi = spi;
1146cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom            this.provider = provider;
1156cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom        }
1166cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom    }
117adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
118adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
119adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Creates a Engine object
120f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
121adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param service
122adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
123adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Engine(String service) {
124adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        this.serviceName = service;
125adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
126adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
127adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
1286cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom     * Finds the appropriate service implementation and returns an
1296cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom     * {@code SpiAndProvider} instance containing a reference to SPI
1306cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom     * and its {@code Provider}
131adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
1326cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom    public SpiAndProvider getInstance(String algorithm, Object param)
133adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            throws NoSuchAlgorithmException {
134adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (algorithm == null) {
135897538a36c18f4db8f9f68ee566aec0bda842e9fElliott Hughes            throw new NoSuchAlgorithmException("Null algorithm name");
136adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
137c7a56c94bdea0e20123ae6fba5eca5b8d984c670Brian Carlstrom        int newCacheVersion = Services.getCacheVersion();
1386cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom        Provider.Service service;
1396cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom        ServiceCacheEntry cacheEntry = this.serviceCache;
1406cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom        if (cacheEntry != null
141a695e8fafadd2591cd148e78f19bc6d7c15121bbJesse Wilson                && cacheEntry.algorithm.equalsIgnoreCase(algorithm)
142c7a56c94bdea0e20123ae6fba5eca5b8d984c670Brian Carlstrom                && newCacheVersion == cacheEntry.cacheVersion) {
1436cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom            service = cacheEntry.service;
144adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } else {
145adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (Services.isEmpty()) {
146897538a36c18f4db8f9f68ee566aec0bda842e9fElliott Hughes                throw notFound(serviceName, algorithm);
147adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
148a695e8fafadd2591cd148e78f19bc6d7c15121bbJesse Wilson            String name = this.serviceName + "." + algorithm.toUpperCase(Locale.US);
1496cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom            service = Services.getService(name);
1506cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom            if (service == null) {
151897538a36c18f4db8f9f68ee566aec0bda842e9fElliott Hughes                throw notFound(serviceName, algorithm);
152adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
153c7a56c94bdea0e20123ae6fba5eca5b8d984c670Brian Carlstrom            this.serviceCache = new ServiceCacheEntry(algorithm, newCacheVersion, service);
154adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1556cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom        return new SpiAndProvider(service.newInstance(param), service.getProvider());
156897538a36c18f4db8f9f68ee566aec0bda842e9fElliott Hughes    }
157897538a36c18f4db8f9f68ee566aec0bda842e9fElliott Hughes
158adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
1596cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom     * Finds the appropriate service implementation and returns and
1606cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom     * instance of the class that implements corresponding Service
1616cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom     * Provider Interface.
162adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
1636cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom    public Object getInstance(String algorithm, Provider provider, Object param)
1646cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom            throws NoSuchAlgorithmException {
165adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (algorithm == null) {
166897538a36c18f4db8f9f68ee566aec0bda842e9fElliott Hughes            throw new NoSuchAlgorithmException("algorithm == null");
167adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1686cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom        Provider.Service service = provider.getService(serviceName, algorithm);
1696cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom        if (service == null) {
170897538a36c18f4db8f9f68ee566aec0bda842e9fElliott Hughes            throw notFound(serviceName, algorithm);
171adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1726cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom        return service.newInstance(param);
1730a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom    }
1740a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom
1756cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom    private NoSuchAlgorithmException notFound(String serviceName, String algorithm)
1766cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom            throws NoSuchAlgorithmException {
1776cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom        throw new NoSuchAlgorithmException(serviceName + " " + algorithm
1786cdb6b7e6939270ccd21790ec95e42197cefc0c3Brian Carlstrom                                           + " implementation not found");
1790a480846a9798c763b088a122ab0dcd3dc3a17b6Brian Carlstrom    }
180897538a36c18f4db8f9f68ee566aec0bda842e9fElliott Hughes}
181