/*
 * Copyright (c) 2019 the Eclipse Milo Authors
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 */

package org.eclipse.milo.opcua.stack.core.security;

import java.security.MessageDigest;
import java.security.Signature;
import javax.crypto.Cipher;
import javax.crypto.Mac;

import org.eclipse.milo.opcua.stack.core.StatusCodes;
import org.eclipse.milo.opcua.stack.core.UaException;

public enum SecurityAlgorithm {

    None("", ""),

    /**
     * Symmetric Signature; transformation to be used with {@link Mac#getInstance(String)}.
     */
    HmacSha1("http://www.w3.org/2000/09/xmldsig#hmac-sha1", "HmacSHA1"),

    /**
     * Symmetric Signature; transformation to be used with {@link Mac#getInstance(String)}.
     */
    HmacSha256("http://www.w3.org/2000/09/xmldsig#hmac-sha256", "HmacSHA256"),

    /**
     * Symmetric Encryption; transformation to be used with {@link Cipher#getInstance(String)}.
     */
    Aes128("http://www.w3.org/2001/04/xmlenc#aes128-cbc", "AES/CBC/NoPadding"),

    /**
     * Symmetric Encryption; transformation to be used with {@link Cipher#getInstance(String)}.
     */
    Aes256("http://www.w3.org/2001/04/xmlenc#aes256-cbc", "AES/CBC/NoPadding"),

    /**
     * Asymmetric Signature; transformation to be used with {@link Signature#getInstance(String)}.
     */
    RsaSha1("http://www.w3.org/2000/09/xmldsig#rsa-sha1", "SHA1withRSA"),

    /**
     * Asymmetric Signature; transformation to be used with {@link Signature#getInstance(String)}.
     */
    RsaSha256("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", "SHA256withRSA"),

    /**
     * Asymmetric Signature; transformation to be used with {@link Signature#getInstance(String)}.
     * <p>
     * Requires Bouncy Castle installed as a Security Provider.
     */
    RsaSha256Pss("http://opcfoundation.org/UA/security/rsa-pss-sha2-256", "SHA256withRSA/PSS"),

    /**
     * Asymmetric Encryption; transformation to be used with {@link Cipher#getInstance(String)}.
     */
    Rsa15("http://www.w3.org/2001/04/xmlenc#rsa-1_5", "RSA/ECB/PKCS1Padding"),

    /**
     * Asymmetric Encryption; transformation to be used with {@link Cipher#getInstance(String)}.
     */
    RsaOaepSha1("http://www.w3.org/2001/04/xmlenc#rsa-oaep", "RSA/ECB/OAEPWithSHA-1AndMGF1Padding"),

    /**
     * Asymmetric Encryption; transformation to be used with {@link Cipher#getInstance(String)}.
     * <p>
     * Important note: the transformation used is "RSA/ECB/OAEPWithSHA256AndMGF1Padding" as opposed to
     * "RSA/ECB/OAEPWithSHA-256AndMGF1Padding".
     * <p>
     * While similar, the former is provided by Bouncy Castle whereas the latter is provided by SunJCE.
     * <p>
     * This is important because the BC version uses SHA256 in the padding while the SunJCE version uses Sha1.
     */
    RsaOaepSha256("http://opcfoundation.org/UA/security/rsa-oaep-sha2-256", "RSA/ECB/OAEPWithSHA256AndMGF1Padding"),

    /**
     * Asymmetric Key Wrap
     */
    KwRsa15("http://www.w3.org/2001/04/xmlenc#rsa-1_5", ""),

    /**
     * Asymmetric Key Wrap
     */
    KwRsaOaep("http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p", ""),

    /**
     * Key Derivation
     */
    PSha1("http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512/dk/p_sha1", ""),

    /**
     * Key Derivation
     */
    PSha256("http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512/dk/p_sha256", ""),

    /**
     * Cryptographic Hash; transformation to be used with {@link MessageDigest#getInstance(String)}.
     */
    Sha1("http://www.w3.org/2000/09/xmldsig#sha1", "SHA-1"),

    /**
     * Cryptographic Hash; transformation to be used with {@link MessageDigest#getInstance(String)}.
     */
    Sha256("http://www.w3.org/2001/04/xmlenc#sha256", "SHA-256");

    private final String uri;
    private final String transformation;

    SecurityAlgorithm(String uri, String transformation) {
        this.uri = uri;
        this.transformation = transformation;
    }

    /**
     * @return The URI identifying this security algorithm.
     */
    public String getUri() {
        return uri;
    }

    /**
     * @return The transformation string to use with the appropriate provider SPI.
     */
    public String getTransformation() {
        return transformation;
    }

    public static SecurityAlgorithm fromUri(String securityAlgorithmUri) throws UaException {
        for (SecurityAlgorithm algorithm : values()) {
            if (algorithm.getUri().equals(securityAlgorithmUri)) {
                return algorithm;
            }
        }

        throw new UaException(StatusCodes.Bad_SecurityChecksFailed,
            "unknown securityAlgorithmUri: " + securityAlgorithmUri);
    }

}
