/*
 * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 * @test
 * @bug 6383195
 * @summary javax.crypto.spec.PBEKeySpec is not thread safe
 */

/*
 * Use one thread to clear the password, another to check the password state.
 *
 * If a partially cleared password is ever obtained, fail the test.
 */
import java.util.Arrays;
import javax.crypto.spec.PBEKeySpec;

public class PBEKeySpecRacing {
    private static final int NUMTESTS = 1000;
    private static final int PASSWORD_LEN = 25;

    private static PBEKeySpec keySpec;

    private static final char[] password;

    static {
        password = new char[PASSWORD_LEN];
        Arrays.fill(password, 'A');
    }

    // flag for failed test case.

    private static volatile char[] failed = null;

    public static void main(String[] args) {
        System.out.println("Testing: ");
        for (int i = 0; i < NUMTESTS; i++) {
            keySpec = new PBEKeySpec(password);
            Thread reader = new Thread(() -> {
                try {
                    // Repeat until state changes or failure seen
                    while (true) {
                        char[] pbePass = keySpec.getPassword();
                        if (!Arrays.equals(password, pbePass)) {
                            failed = pbePass;
                            return;
                        }
                    }
                } catch (IllegalStateException e) {
                    System.out.print(".");
                }
            });
            Thread clearer = new Thread(() -> keySpec.clearPassword());
            reader.start();
            clearer.start();
            try {
                reader.join();
                clearer.join();
            } catch (InterruptedException e) {
                // Swallow
            }

            if (failed != null) {
                throw new RuntimeException(
                        "Inconsistent Password: " + Arrays.toString(failed));
            }

            // avoid long output lines.
            if ((i % 80) == 79) {
                System.out.println();
            }
        }
        System.out.println("Test PASSED");
    }
}