//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
using System;
using System.IdentityModel.Selectors;
namespace System.IdentityModel.Tokens
{
    /// 
    /// SecurityKeyElement provides delayed resolution of security keys by resolving the SecurityKeyIdentifierClause or SecurityKeyIdentifier 
    /// only when cryptographic functions are needed.  This allows a key clause or identifier that is never used by an application
    /// to be serialized and deserialzied on and off the wire without issue.
    /// 
    public class SecurityKeyElement : SecurityKey
    {
        SecurityKey _securityKey;
        object _keyLock;
        SecurityTokenResolver _securityTokenResolver;
        SecurityKeyIdentifier _securityKeyIdentifier;
        /// 
        /// Constructor to use when working with SecurityKeyIdentifierClauses
        /// 
        /// SecurityKeyIdentifierClause that represents a SecuriytKey
        /// SecurityTokenResolver that can be resolved to a SecurityKey
        /// Thrown if the 'clause' is null
        public SecurityKeyElement(SecurityKeyIdentifierClause securityKeyIdentifierClause, SecurityTokenResolver securityTokenResolver)
        {
            if (securityKeyIdentifierClause == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("securityKeyIdentifierClause");
            }
            Initialize(new SecurityKeyIdentifier(securityKeyIdentifierClause), securityTokenResolver);
        }
        /// 
        /// Constructor to use when working with SecurityKeyIdentifiers
        /// 
        /// SecurityKeyIdentifier that represents a SecuriytKey
        /// SecurityTokenResolver that can be resolved to a SecurityKey
        /// Thrown if the 'securityKeyIdentifier' is null
        public SecurityKeyElement(SecurityKeyIdentifier securityKeyIdentifier, SecurityTokenResolver securityTokenResolver)
        {
            if (securityKeyIdentifier == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("securityKeyIdentifier");
            }
            Initialize(securityKeyIdentifier, securityTokenResolver);
        }
        void Initialize(SecurityKeyIdentifier securityKeyIdentifier, SecurityTokenResolver securityTokenResolver)
        {
            _keyLock = new object();
            _securityKeyIdentifier = securityKeyIdentifier;
            _securityTokenResolver = securityTokenResolver;
        }
        /// 
        /// Decrypts a key using the specified algorithm.
        /// 
        /// Algorithm to use when decrypting the key.
        /// Bytes representing the encrypted key.
        /// Decrypted bytes.
        public override byte[] DecryptKey(string algorithm, byte[] keyData)
        {
            if (_securityKey == null)
            {
                ResolveKey();
            }
            return _securityKey.DecryptKey(algorithm, keyData);
        }
        /// 
        /// Encrypts a key using the specified algorithm.
        /// 
        /// Algorithm to use when encrypting the key.
        /// Bytes representing the key.
        /// Encrypted bytes.
        public override byte[] EncryptKey(string algorithm, byte[] keyData)
        {
            if (_securityKey == null)
            {
                ResolveKey();
            }
            return _securityKey.EncryptKey(algorithm, keyData);
        }
        /// 
        /// Answers question: is the algorithm Asymmetric.
        /// 
        /// Algorithm to check.
        /// True if algorithm will be processed by runtime as Asymmetric.
        public override bool IsAsymmetricAlgorithm(string algorithm)
        {
            // Copied from System.IdentityModel.CryptoHelper
            // no need to ResolveKey
            switch (algorithm)
            {
                case SecurityAlgorithms.DsaSha1Signature:
                case SecurityAlgorithms.RsaSha1Signature:
                case SecurityAlgorithms.RsaSha256Signature:
                case SecurityAlgorithms.RsaOaepKeyWrap:
                case SecurityAlgorithms.RsaV15KeyWrap:
                    return true;
                default:
                    return false;
            }
        }
        /// 
        /// Answers question: is the algorithm is supported by this key.
        /// 
        /// Algorithm to check.
        /// True if algorithm is supported by this key.
        public override bool IsSupportedAlgorithm(string algorithm)
        {
            if (_securityKey == null)
            {
                ResolveKey();
            }
            return _securityKey.IsSupportedAlgorithm(algorithm);
        }
        /// 
        /// Answers question: is the algorithm Symmetric.
        /// 
        /// Algorithm to check.
        /// True if algorithm will be processed by runtime as Symmetric.
        public override bool IsSymmetricAlgorithm(string algorithm)
        {
            // Copied from System.IdentityModel.CryptoHelper
            // no need to ResolveKey.
            switch (algorithm)
            {
                case SecurityAlgorithms.DsaSha1Signature:
                case SecurityAlgorithms.RsaSha1Signature:
                case SecurityAlgorithms.RsaSha256Signature:
                case SecurityAlgorithms.RsaOaepKeyWrap:
                case SecurityAlgorithms.RsaV15KeyWrap:
                    return false;
                case SecurityAlgorithms.HmacSha1Signature:
                case SecurityAlgorithms.HmacSha256Signature:
                case SecurityAlgorithms.Aes128Encryption:
                case SecurityAlgorithms.Aes192Encryption:
                case SecurityAlgorithms.Aes256Encryption:
                case SecurityAlgorithms.TripleDesEncryption:
                case SecurityAlgorithms.Aes128KeyWrap:
                case SecurityAlgorithms.Aes192KeyWrap:
                case SecurityAlgorithms.Aes256KeyWrap:
                case SecurityAlgorithms.TripleDesKeyWrap:
                case SecurityAlgorithms.Psha1KeyDerivation:
                case SecurityAlgorithms.Psha1KeyDerivationDec2005:
                    return true;
                default:
                    return false;
            }
        }
        /// 
        /// Gets the key size in bits.
        /// 
        /// Key size in bits.
        public override int KeySize
        {
            get
            {
                if (_securityKey == null)
                {
                    ResolveKey();
                }
                return _securityKey.KeySize;
            }
        }
        /// 
        /// Attempts to resolve the _securityKeyIdentifier into a securityKey.  If successful, the private _securityKey is set.
        /// Uses the tokenresolver that was passed in, it may be the case a keyIdentifier can 
        /// generate a securityKey.  A RSA key can generate a key with just the public part.
        /// 
        /// void
        void ResolveKey()
        {
            if (_securityKeyIdentifier == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("ski");
            }
            if (_securityKey == null)
            {
                lock (_keyLock)
                {
                    if (_securityKey == null)
                    {
                        if (_securityTokenResolver != null)
                        {
                            for (int i = 0; i < _securityKeyIdentifier.Count; ++i)
                            {
                                if (_securityTokenResolver.TryResolveSecurityKey(_securityKeyIdentifier[i], out _securityKey))
                                {
                                    return;
                                }
                            }
                        }
                        // most likely a public key, do this last
                        if (_securityKeyIdentifier.CanCreateKey)
                        {
                            _securityKey = _securityKeyIdentifier.CreateKey();
                            return;
                        }
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
                            new SecurityTokenException(SR.GetString(SR.ID2080,
                                        _securityTokenResolver == null ? "null" : _securityTokenResolver.ToString(),
                                        _securityKeyIdentifier == null ? "null" : _securityKeyIdentifier.ToString())), System.Diagnostics.TraceEventType.Error);
                    }
                }
            }
        }
    }
}