/*
 * Copyright (c) 2021, 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 8255410
 * @summary test the general SecretKeyFactory functionality
 * @library /test/lib ..
 * @modules jdk.crypto.cryptoki
 * @run main/othervm TestGeneral
 */

import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.SecureRandom;
import java.util.Arrays;
import javax.crypto.SecretKeyFactory;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

public class TestGeneral extends PKCS11Test {

    private enum TestResult {
        PASS,
        FAIL,
        TBD // unused for now
    }

    public static void main(String[] args) throws Exception {
        main(new TestGeneral(), args);
    }

    private void test(String algorithm, SecretKey key, Provider p,
            TestResult expected) throws RuntimeException {
        System.out.println("Testing " + algorithm + " SKF from " + p.getName());
        SecretKeyFactory skf;
        try {
            skf = SecretKeyFactory.getInstance(algorithm, p);
        } catch (NoSuchAlgorithmException e) {
            System.out.println("Not supported, skipping: " + e);
            return;
        }
        try {
            SecretKey key2 = skf.translateKey(key);
            if (expected == TestResult.FAIL) {
                throw new RuntimeException("translateKey() should FAIL");
            }
            System.out.println("=> translated key");
            if (!key2.getAlgorithm().equalsIgnoreCase(algorithm)) {
                throw new RuntimeException("translated key algorithm mismatch");
            }
            System.out.println("=> checked key algorithm");

            // proceed to check encodings if format match
            if (key2.getFormat().equalsIgnoreCase(key.getFormat())) {
                if (key2.getEncoded() != null &&
                        !Arrays.equals(key.getEncoded(), key2.getEncoded())) {
                    throw new RuntimeException(
                            "translated key encoding mismatch");
                }
                System.out.println("=> checked key format and encoding");
            }
        } catch (Exception e) {
            if (expected == TestResult.PASS) {
                e.printStackTrace();
                throw new RuntimeException("translateKey() should pass", e);
            }
        }
    }

    @Override
    public void main(Provider p) throws Exception {

        byte[] rawBytes = new byte[32];
        new SecureRandom().nextBytes(rawBytes);

        SecretKey aes_128Key = new SecretKeySpec(rawBytes, 0, 16, "AES");
        SecretKey aes_256Key = new SecretKeySpec(rawBytes, 0, 32, "AES");
        SecretKey bf_128Key = new SecretKeySpec(rawBytes, 0, 16, "Blowfish");
        SecretKey cc20Key = new SecretKeySpec(rawBytes, 0, 32, "ChaCha20");

        // fixed key length
        test("AES", aes_128Key, p, TestResult.PASS);
        test("AES", aes_256Key, p, TestResult.PASS);
        test("AES", cc20Key, p, TestResult.FAIL);

        test("ChaCha20", aes_128Key, p, TestResult.FAIL);
        test("ChaCha20", aes_256Key, p, TestResult.FAIL);
        test("ChaCha20", cc20Key, p, TestResult.PASS);

        // variable key length
        // Different PKCS11 impls may have different ranges
        // of supported key sizes for variable-key-length
        // algorithms.
        test("Blowfish", aes_128Key, p, TestResult.FAIL);
        test("Blowfish", cc20Key, p, TestResult.FAIL);
        test("Blowfish", bf_128Key, p, TestResult.PASS);
    }
}