151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski/*
26582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski *
551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * This code is free software; you can redistribute it and/or modify it
651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * under the terms of the GNU General Public License version 2 only, as
751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * published by the Free Software Foundation.  Oracle designates this
851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * particular file as subject to the "Classpath" exception as provided
951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * by Oracle in the LICENSE file that accompanied this code.
1051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski *
1151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * This code is distributed in the hope that it will be useful, but WITHOUT
1251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * version 2 for more details (a copy is included in the LICENSE file that
1551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * accompanied this code).
1651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski *
1751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * You should have received a copy of the GNU General Public License version
1851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * 2 along with this work; if not, write to the Free Software Foundation,
1951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski *
2151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * or visit www.oracle.com if you need additional information or have any
2351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * questions.
2451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski */
2551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
2651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskipackage javax.crypto;
2751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
2851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.util.*;
2951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.util.jar.*;
3051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.io.*;
3151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.net.URL;
3251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.security.*;
3351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
3451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.security.Provider.Service;
3551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
3651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport sun.security.jca.*;
3751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport sun.security.jca.GetInstance.Instance;
3851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
3951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski/**
4051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * This class instantiates implementations of JCE engine classes from
4151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * providers registered with the java.security.Security object.
4251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski *
4351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * @author Jan Luehe
4451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * @author Sharon Liu
4551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * @since 1.4
4651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski */
4751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
4851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskifinal class JceSecurity {
4951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
5051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    static final SecureRandom RANDOM = new SecureRandom();
5151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
5251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // The defaultPolicy and exemptPolicy will be set up
5351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // in the static initializer.
5451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static CryptoPermissions defaultPolicy = null;
5551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static CryptoPermissions exemptPolicy = null;
5651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
5751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // Map<Provider,?> of the providers we already have verified
5851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // value == PROVIDER_VERIFIED is successfully verified
5951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // value is failure cause Exception in error case
601798254f19bacffb635e740c868e81d42371db4bSergio Giro    private final static Map<Provider, Object> verificationResults =
611798254f19bacffb635e740c868e81d42371db4bSergio Giro            new IdentityHashMap<>();
6251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
6351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // Map<Provider,?> of the providers currently being verified
641798254f19bacffb635e740c868e81d42371db4bSergio Giro    private final static Map<Provider, Object> verifyingProviders =
651798254f19bacffb635e740c868e81d42371db4bSergio Giro            new IdentityHashMap<>();
6651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
676582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian    // Android-removed: JCE crypto strength restrictions are never in place on Android.
686582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian    // private static final boolean isRestricted = true;
696582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian
706582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian    // Android-removed: This debugging mechanism is not used in Android.
716582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian    /*
726582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian    private static final Debug debug =
736582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian                        Debug.getInstance("jca", "Cipher");
746582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian    */
7551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
7651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /*
7751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Don't let anyone instantiate this.
7851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
7951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private JceSecurity() {
8051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
8151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
826582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian    // BEGIN Android-removed: JCE crypto strength restrictions are never in place on Android.
834087011821a84f2a4e2b827f304bb00481d3d139Tobias Thierer    /*
8451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    static {
8551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        try {
861798254f19bacffb635e740c868e81d42371db4bSergio Giro            AccessController.doPrivileged(
871798254f19bacffb635e740c868e81d42371db4bSergio Giro                new PrivilegedExceptionAction<Object>() {
881798254f19bacffb635e740c868e81d42371db4bSergio Giro                    public Object run() throws Exception {
891798254f19bacffb635e740c868e81d42371db4bSergio Giro                        setupJurisdictionPolicies();
901798254f19bacffb635e740c868e81d42371db4bSergio Giro                        return null;
911798254f19bacffb635e740c868e81d42371db4bSergio Giro                    }
921798254f19bacffb635e740c868e81d42371db4bSergio Giro                });
9351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
9451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            isRestricted = defaultPolicy.implies(
9551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                CryptoAllPermission.INSTANCE) ? false : true;
9651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        } catch (Exception e) {
971798254f19bacffb635e740c868e81d42371db4bSergio Giro            throw new SecurityException(
981798254f19bacffb635e740c868e81d42371db4bSergio Giro                    "Can not initialize cryptographic mechanism", e);
9951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
10051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
1014087011821a84f2a4e2b827f304bb00481d3d139Tobias Thierer    */
1026582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian    // END Android-removed: JCE crypto strength restrictions are never in place on Android.
10351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
1041798254f19bacffb635e740c868e81d42371db4bSergio Giro    static Instance getInstance(String type, Class<?> clazz, String algorithm,
10551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            String provider) throws NoSuchAlgorithmException,
10651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            NoSuchProviderException {
10751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        Service s = GetInstance.getService(type, algorithm, provider);
10851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        Exception ve = getVerificationResult(s.getProvider());
10951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (ve != null) {
11051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            String msg = "JCE cannot authenticate the provider " + provider;
11151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            throw (NoSuchProviderException)
11251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                new NoSuchProviderException(msg).initCause(ve);
11351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
11451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return GetInstance.getInstance(s, clazz);
11551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
11651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
1171798254f19bacffb635e740c868e81d42371db4bSergio Giro    static Instance getInstance(String type, Class<?> clazz, String algorithm,
11851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            Provider provider) throws NoSuchAlgorithmException {
11951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        Service s = GetInstance.getService(type, algorithm, provider);
12051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        Exception ve = JceSecurity.getVerificationResult(provider);
12151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (ve != null) {
12251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            String msg = "JCE cannot authenticate the provider "
12351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                + provider.getName();
12451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            throw new SecurityException(msg, ve);
12551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
12651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return GetInstance.getInstance(s, clazz);
12751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
12851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
1291798254f19bacffb635e740c868e81d42371db4bSergio Giro    static Instance getInstance(String type, Class<?> clazz, String algorithm)
13051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            throws NoSuchAlgorithmException {
1311798254f19bacffb635e740c868e81d42371db4bSergio Giro        List<Service> services = GetInstance.getServices(type, algorithm);
13251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        NoSuchAlgorithmException failure = null;
1331798254f19bacffb635e740c868e81d42371db4bSergio Giro        for (Service s : services) {
13451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if (canUseProvider(s.getProvider()) == false) {
13551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                // allow only signed providers
13651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                continue;
13751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
13851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            try {
13951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                Instance instance = GetInstance.getInstance(s, clazz);
14051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                return instance;
14151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            } catch (NoSuchAlgorithmException e) {
14251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                failure = e;
14351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
14451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
14551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        throw new NoSuchAlgorithmException("Algorithm " + algorithm
14651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                + " not available", failure);
14751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
14851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
14951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
15051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Verify if the JAR at URL codeBase is a signed exempt application
15151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * JAR file and returns the permissions bundled with the JAR.
15251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *
15351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @throws Exception on error
15451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
15551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    static CryptoPermissions verifyExemptJar(URL codeBase) throws Exception {
15651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        JarVerifier jv = new JarVerifier(codeBase, true);
15751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        jv.verify();
15851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return jv.getPermissions();
15951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
16051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
16151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
16251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Verify if the JAR at URL codeBase is a signed provider JAR file.
16351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *
16451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * @throws Exception on error
16551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
16651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    static void verifyProviderJar(URL codeBase) throws Exception {
16751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // Verify the provider JAR file and all
16851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // supporting JAR files if there are any.
16951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        JarVerifier jv = new JarVerifier(codeBase, false);
17051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        jv.verify();
17151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
17251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
17351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private final static Object PROVIDER_VERIFIED = Boolean.TRUE;
17451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
17551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /*
17651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Verify that the provider JAR files are signed properly, which
17751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * means the signer's certificate can be traced back to a
17851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * JCE trusted CA.
17951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Return null if ok, failure Exception if verification failed.
18051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
18151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    static synchronized Exception getVerificationResult(Provider p) {
18251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        Object o = verificationResults.get(p);
18351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (o == PROVIDER_VERIFIED) {
18451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            return null;
18551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        } else if (o != null) {
18651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            return (Exception)o;
18751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
18851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (verifyingProviders.get(p) != null) {
18951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            // this method is static synchronized, must be recursion
19051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            // return failure now but do not save the result
19151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            return new NoSuchProviderException("Recursion during verification");
19251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
19351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        try {
19451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            verifyingProviders.put(p, Boolean.FALSE);
19551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            URL providerURL = getCodeBase(p.getClass());
19651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            verifyProviderJar(providerURL);
19751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            // Verified ok, cache result
19851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            verificationResults.put(p, PROVIDER_VERIFIED);
19951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            return null;
20051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        } catch (Exception e) {
20151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            verificationResults.put(p, e);
20251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            return e;
20351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        } finally {
20451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            verifyingProviders.remove(p);
20551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
20651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
20751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
20851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // return whether this provider is properly signed and can be used by JCE
20951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    static boolean canUseProvider(Provider p) {
2106582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian        // BEGIN Android-changed: All providers are available.
2114087011821a84f2a4e2b827f304bb00481d3d139Tobias Thierer        // return getVerificationResult(p) == null;
2129af804680e8f3a299991816aa5fe36e290297775Piotr Jastrzebski        return true;
2136582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian        // END Android-changed: All providers are available.
21451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
21551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
21651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // dummy object to represent null
21751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final URL NULL_URL;
21851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
21951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    static {
22051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        try {
2216582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian            NULL_URL = new URL("http://null.oracle.com/");
22251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        } catch (Exception e) {
22351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            throw new RuntimeException(e);
22451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
22551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
22651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
22751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // reference to a Map we use as a cache for codebases
2281798254f19bacffb635e740c868e81d42371db4bSergio Giro    private static final Map<Class<?>, URL> codeBaseCacheRef =
2291798254f19bacffb635e740c868e81d42371db4bSergio Giro            new WeakHashMap<>();
23051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
23151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /*
2321798254f19bacffb635e740c868e81d42371db4bSergio Giro     * Returns the CodeBase for the given class.
23351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
2341798254f19bacffb635e740c868e81d42371db4bSergio Giro    static URL getCodeBase(final Class<?> clazz) {
2351798254f19bacffb635e740c868e81d42371db4bSergio Giro        synchronized (codeBaseCacheRef) {
2361798254f19bacffb635e740c868e81d42371db4bSergio Giro            URL url = codeBaseCacheRef.get(clazz);
2371798254f19bacffb635e740c868e81d42371db4bSergio Giro            if (url == null) {
2381798254f19bacffb635e740c868e81d42371db4bSergio Giro                url = AccessController.doPrivileged(new PrivilegedAction<URL>() {
2391798254f19bacffb635e740c868e81d42371db4bSergio Giro                    public URL run() {
2401798254f19bacffb635e740c868e81d42371db4bSergio Giro                        ProtectionDomain pd = clazz.getProtectionDomain();
2411798254f19bacffb635e740c868e81d42371db4bSergio Giro                        if (pd != null) {
2421798254f19bacffb635e740c868e81d42371db4bSergio Giro                            CodeSource cs = pd.getCodeSource();
2431798254f19bacffb635e740c868e81d42371db4bSergio Giro                            if (cs != null) {
2441798254f19bacffb635e740c868e81d42371db4bSergio Giro                                return cs.getLocation();
2451798254f19bacffb635e740c868e81d42371db4bSergio Giro                            }
24651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        }
2471798254f19bacffb635e740c868e81d42371db4bSergio Giro                        return NULL_URL;
24851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    }
2491798254f19bacffb635e740c868e81d42371db4bSergio Giro                });
2501798254f19bacffb635e740c868e81d42371db4bSergio Giro                codeBaseCacheRef.put(clazz, url);
2511798254f19bacffb635e740c868e81d42371db4bSergio Giro            }
2521798254f19bacffb635e740c868e81d42371db4bSergio Giro            return (url == NULL_URL) ? null : url;
25351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
25451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
25551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
2566582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian    // BEGIN Android-removed: JCE crypto strength restrictions are never in place on Android.
2576582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian    /*
2586582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian     * This is called from within an doPrivileged block.
2596582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian     *
2606582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian     * Following logic is used to decide what policy files are selected.
2616582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian     *
2626582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian     * If the new Security property (crypto.policy) is set in the
2636582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian     * java.security file, or has been set dynamically using the
2646582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian     * Security.setProperty() call before the JCE framework has
2656582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian     * been initialized, that setting will be used.
2666582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian     * Remember - this property is not defined by default. A conscious
2676582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian     * user edit or an application call is required.
2686582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian     *
2696582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian     * Otherwise, if user has policy jar files installed in the legacy
2706582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian     * jre/lib/security/ directory, the JDK will honor whatever
2716582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian     * setting is set by those policy files. (legacy/current behavior)
2726582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian     *
2736582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian     * If none of the above 2 conditions are met, the JDK will default
2746582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian     * to using the limited crypto policy files found in the
2756582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian     * jre/lib/security/policy/limited/ directory
2766582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian     *
27751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static void setupJurisdictionPolicies() throws Exception {
2786582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian        // Sanity check the crypto.policy Security property.  Single
2796582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian        // directory entry, no pseudo-directories (".", "..", leading/trailing
2806582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian        // path separators). normalize()/getParent() will help later.
2816582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian        String javaHomeProperty = System.getProperty("java.home");
2826582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian        String cryptoPolicyProperty = Security.getProperty("crypto.policy");
2836582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian        Path cpPath = (cryptoPolicyProperty == null) ? null :
2846582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian                Paths.get(cryptoPolicyProperty);
2856582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian
2866582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian        if ((cpPath != null) && ((cpPath.getNameCount() != 1) ||
2876582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian                (cpPath.compareTo(cpPath.getFileName())) != 0)) {
2886582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian            throw new SecurityException(
2896582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian                    "Invalid policy directory name format: " +
2906582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian                            cryptoPolicyProperty);
2916582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian        }
2926582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian
2936582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian        if (cpPath == null) {
2946582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian            // Security property is not set, use default path
2956582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian            cpPath = Paths.get(javaHomeProperty, "lib", "security");
2966582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian        } else {
2976582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian            // populate with java.home
2986582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian            cpPath = Paths.get(javaHomeProperty, "lib", "security",
2996582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian                    "policy", cryptoPolicyProperty);
3006582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian        }
3016582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian
3026582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian        if (debug != null) {
3036582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian            debug.println("crypto policy directory: " + cpPath);
3046582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian        }
3056582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian
3066582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian        File exportJar = new File(cpPath.toFile(),"US_export_policy.jar");
3076582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian        File importJar = new File(cpPath.toFile(),"local_policy.jar");
3086582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian
3096582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian        if (cryptoPolicyProperty == null && (!exportJar.exists() ||
3106582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian                !importJar.exists())) {
3116582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian            // Compatibility set up. If crypto.policy is not defined.
3126582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian            // check to see if legacy jars exist in lib directory. If
3136582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian            // they don't exist, we default to limited policy mode.
3146582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian            cpPath = Paths.get(
3156582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian                    javaHomeProperty, "lib", "security", "policy", "limited");
3166582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian            // point to the new jar files in limited directory
3176582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian            exportJar = new File(cpPath.toFile(),"US_export_policy.jar");
3186582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian            importJar = new File(cpPath.toFile(),"local_policy.jar");
3196582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian        }
32051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
32151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        URL jceCipherURL = ClassLoader.getSystemResource
32251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                ("javax/crypto/Cipher.class");
32351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
32451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if ((jceCipherURL == null) ||
32551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                !exportJar.exists() || !importJar.exists()) {
32651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            throw new SecurityException
32751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                ("Cannot locate policy or framework files!");
32851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
32951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
33051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // Read jurisdiction policies.
33151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        CryptoPermissions defaultExport = new CryptoPermissions();
33251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        CryptoPermissions exemptExport = new CryptoPermissions();
33351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        loadPolicies(exportJar, defaultExport, exemptExport);
33451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
33551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        CryptoPermissions defaultImport = new CryptoPermissions();
33651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        CryptoPermissions exemptImport = new CryptoPermissions();
33751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        loadPolicies(importJar, defaultImport, exemptImport);
33851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
33951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // Merge the export and import policies for default applications.
34051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (defaultExport.isEmpty() || defaultImport.isEmpty()) {
34151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            throw new SecurityException("Missing mandatory jurisdiction " +
34251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                        "policy files");
34351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
34451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        defaultPolicy = defaultExport.getMinimum(defaultImport);
34551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
34651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // Merge the export and import policies for exempt applications.
34751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (exemptExport.isEmpty())  {
34851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            exemptPolicy = exemptImport.isEmpty() ? null : exemptImport;
34951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        } else {
35051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            exemptPolicy = exemptExport.getMinimum(exemptImport);
35151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
35251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
3536582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian    */
3546582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian    // END Android-removed: JCE crypto strength restrictions are never in place on Android.
35551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
35651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
35751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Load the policies from the specified file. Also checks that the
35851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * policies are correctly signed.
35951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
36051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static void loadPolicies(File jarPathName,
36151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                     CryptoPermissions defaultPolicy,
36251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                     CryptoPermissions exemptPolicy)
36351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        throws Exception {
36451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
36551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        JarFile jf = new JarFile(jarPathName);
36651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
3671798254f19bacffb635e740c868e81d42371db4bSergio Giro        Enumeration<JarEntry> entries = jf.entries();
36851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        while (entries.hasMoreElements()) {
3691798254f19bacffb635e740c868e81d42371db4bSergio Giro            JarEntry je = entries.nextElement();
37051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            InputStream is = null;
37151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            try {
37251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                if (je.getName().startsWith("default_")) {
37351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    is = jf.getInputStream(je);
37451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    defaultPolicy.load(is);
37551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                } else if (je.getName().startsWith("exempt_")) {
37651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    is = jf.getInputStream(je);
37751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    exemptPolicy.load(is);
37851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                } else {
37951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    continue;
38051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                }
38151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            } finally {
38251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                if (is != null) {
38351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    is.close();
38451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                }
38551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
38651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
38751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            // Enforce the signer restraint, i.e. signer of JCE framework
38851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            // jar should also be the signer of the two jurisdiction policy
38951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            // jar files.
39051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            JarVerifier.verifyPolicySigned(je.getCertificates());
39151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
39251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // Close and nullify the JarFile reference to help GC.
39351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        jf.close();
39451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        jf = null;
39551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
39651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
39751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    static CryptoPermissions getDefaultPolicy() {
39851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return defaultPolicy;
39951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
40051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
40151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    static CryptoPermissions getExemptPolicy() {
40251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return exemptPolicy;
40351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
40451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
4056582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian    // Android-removed: JCE crypto strength restrictions are never in place on Android.
4066582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian    // static boolean isRestricted() {
4076582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian    //     return isRestricted;
4086582f180e3431415346e8a7e910e13ebfa668b53Adam Vartanian    // }
40951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski}
410