/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <util/crypto/DrmCrypto.h>
#include <ustring.h>

using namespace ustl;

void AesAgent::discardPaddingByte( unsigned char* decryptedBuf,unsigned long* decryptedBufLen)
{
    if(!decryptedBuf)
    {
        return;
    }

    int i;
    unsigned long tmpLen = *decryptedBufLen;

    // Check whether the last several bytes are padding or not
    for ( i = 1; i < decryptedBuf[tmpLen - 1]; i++)
    {
        if (decryptedBuf[tmpLen - 1 - i] != decryptedBuf[tmpLen - 1])
            break;
    }

    // They are padding bytes
    if (i == decryptedBuf[tmpLen - 1])
    {
        *decryptedBufLen = tmpLen - i;
    }

    return;
}

int32_t AesAgent::decContent( unsigned char* iv,
                              const unsigned char* encData,
                              unsigned long encLen,
                              unsigned char* decData)
{
    if(AES_128_CBC == mode)
    {
        AES_KEY key;
        AES_set_decrypt_key(AesKey,AES_KEY_BITS,&key);

        uint8_t *tmpBuf = new uint8_t[encLen];

        AES_cbc_encrypt( encData,
                         tmpBuf,
                         encLen,
                         &key,
                         iv,
                         AES_DECRYPT);

        unsigned long tempLen = encLen;
        discardPaddingByte(tmpBuf,&tempLen);

        memcpy(decData, tmpBuf, tempLen);

        delete []tmpBuf;
        return encLen - tempLen;
    }
    else
    {
        return AES_DEC_FAILED;
    }
}

void Sha1Agent::computeHash( const unsigned char* inData,
                             unsigned long inLen,
                             unsigned char* outHash) const
{
    EVP_Digest(inData,inLen,outHash,NULL,EVP_sha1(),NULL);
    return;
}

void HmacSha1Agent::computeMac( const unsigned char* inData,
                                unsigned long inLen,
                                unsigned char* outData) const
{
    HMAC(EVP_sha1(),macKey,keyLen,inData,inLen,outData,NULL);
    return;
}

bool RsaAgent::signature( const unsigned char* rawData,
                          unsigned long rawLen,
                          unsigned char* sigData,
                          RsaAlg sigAlg)
{
    switch(sigAlg)
    {
        case RSA_PSS:
            {
                unsigned char mHash[SHA_DIGEST_LENGTH];
                Sha1Agent sha1;
                sha1.computeHash(rawData,rawLen,mHash);

                unsigned char EM[rsaSize];
                if( 0 == RSA_padding_add_PKCS1_PSS( &rsaKey,
                                                    EM,
                                                    mHash,
                                                    EVP_sha1(),
                                                    SHA_DIGEST_LENGTH))
                {
                    return false;
                }

                if(0 > RSA_private_encrypt( SHA_DIGEST_LENGTH,
                                            EM,
                                            sigData,
                                            &rsaKey,
                                            RSA_PKCS1_PADDING))
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }
            break;
        case RSA_SHA1:
            {
                unsigned char mHash[SHA_DIGEST_LENGTH];
                Sha1Agent sha1;
                sha1.computeHash(rawData,rawLen,mHash);

                if(0 != RSA_sign( NID_sha1WithRSA,
                                  mHash,
                                  SHA_DIGEST_LENGTH,
                                  sigData,
                                  &rsaSize,
                                  &rsaKey))
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
           break;
        default:
            return false;
    }

    return false;
}

bool RsaAgent::sigVerify( unsigned char* sigData,
                          unsigned long sigLen,
                          const unsigned char* rawData,
                          unsigned long rawLen,
                          RsaAlg sigAlg)
{
    if( sigAlg == RSA_PSS)
    {
        unsigned char decSigData[rsaSize];

        if(0 > RSA_public_decrypt(sigLen,
                                  sigData,
                                  decSigData,
                                  &rsaKey,
                                  RSA_PKCS1_PADDING))
        {
            return false;
        }
        else
        {
            unsigned char mHash[SHA_DIGEST_LENGTH];
            Sha1Agent sha1;
            sha1.computeHash(rawData,rawLen,mHash);

            if( 0 == RSA_verify_PKCS1_PSS( &rsaKey,
                                           mHash,
                                           EVP_sha1(),
                                           decSigData,
                                           -1))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
    else if(sigAlg == RSA_SHA1)
    {
        unsigned char mHash[SHA_DIGEST_LENGTH];
        Sha1Agent sha1;
        sha1.computeHash(rawData,rawLen,mHash);

        if(0 != RSA_verify( NID_sha1WithRSA,
                            mHash,
                            SHA_DIGEST_LENGTH,
                            sigData,
                            sigLen,
                            &rsaKey))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    else
    {
        return false;
    }
}

int RsaAgent::decrypt( const unsigned char* encData,
                       unsigned long encLen,
                       unsigned char* decData)
{
    return RSA_private_decrypt( encLen,
                                encData,
                                decData,
                                &rsaKey,
                                RSA_PKCS1_PADDING);
}