omadrm/drmengine/drmcrypto/src/OmaCrypto.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 17 Dec 2009 08:52:27 +0200
changeset 0 95b198f216e5
permissions -rw-r--r--
Revision: 200949 Kit: 200951

/*
* Copyright (c) 2002-2008 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  OMA DRM 2.x Crypto Algorithms
*
*/



// INCLUDE FILES
#include <e32std.h>
#include <asymmetric.h>
#include <symmetric.h>
#include <hash.h>
#include "OmaCrypto.h"
#include "DrmKeyStorage.h"

#ifdef _DEBUG
#define LOGGING
#endif

#ifdef LOGGING
_LIT(KLogDir, "DRM");
_LIT(KLogName, "Crypto.log");
#include "flogger.h"
#define LOG(string) \
    RFileLogger::Write(KLogDir, KLogName, \
        EFileLoggingModeAppend, string);
#define LOGHEX(buffer) \
    RFileLogger::HexDump(KLogDir, KLogName, \
        EFileLoggingModeAppend, _S(""), _S(""), \
        buffer.Ptr(), buffer.Length());
#define LOGFMT(string, value) \
    RFileLogger::WriteFormat(KLogDir, KLogName, \
        EFileLoggingModeAppend, string, value);
#else
#define LOG
#define LOGHEX
#define LOGFMT
#endif

// LOCAL CONSTANTS AND MACROS

_LIT8(KWrapIv, "\xA6\xA6\xA6\xA6\xA6\xA6\xA6\xA6");

// LOCAL FUNCTION PROTOTYPES
LOCAL_C void DeleteObject( TAny* aObject );

// FORWARD DECLARATIONS

// ============================= LOCAL FUNCTIONS ===============================

// ----------------------------------------------------------------------------
// DeleteObject
// Deletes the file by TFileName presented by aHandle
// ----------------------------------------------------------------------------
//
LOCAL_C void DeleteObject( TAny* aObject )
    {
    __ASSERT_DEBUG( aObject, User::Panic( _L( "DeleteObject" ), KErrArgument ) );
    MDrmKeyStorage* object = reinterpret_cast< MDrmKeyStorage* >( aObject );
    delete object;
    object = NULL;
    }


// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// OmaCrypto::
//
// -----------------------------------------------------------------------------
//
EXPORT_C void OmaCrypto::WriteUint32ToBlock(
    TUint32 aInt,
    TDes8& aBlock,
    TInt aOffset)
    {
    aBlock[aOffset] =  (aInt & 0xff000000) >> 24;
    aBlock[aOffset + 1] = (aInt & 0x00ff0000) >> 16;
    aBlock[aOffset + 2] = (aInt & 0x0000ff00) >> 8;
    aBlock[aOffset + 3] = (aInt & 0x000000ff);
    }

// -----------------------------------------------------------------------------
// OmaCrypto::
//
// -----------------------------------------------------------------------------
//
EXPORT_C HBufC8* OmaCrypto::RsaDecryptL(
    MDrmKeyStorage* aKeyStorage,
    const TDesC8& aInput)
    {
    return aKeyStorage->RsaDecryptL(aInput);
    }

// -----------------------------------------------------------------------------
// OmaCrypto::
//
// -----------------------------------------------------------------------------
//
EXPORT_C HBufC8* OmaCrypto::RsaEncryptL(
    CRSAPublicKey* aKey,
    const TDesC8& aInput)
    {
    RInteger result;
    RInteger input;
    HBufC8* output;

    input = OS2IPL(aInput);
    CleanupClosePushL(input);
    result = TInteger::ModularExponentiateL(input, aKey->E(), aKey->N());
    CleanupClosePushL(result);
    output = I2OSPL(result);

#ifdef LOGGING
    HBufC8* buffer = NULL;
    LOG(_L8("RsaEncryptL"));
    LOG(_L8("Input"));
    LOGHEX(aInput);
    buffer = aKey->E().BufferLC();
    LOG(_L8("E"));
    LOGHEX((*buffer));
    CleanupStack::PopAndDestroy(buffer);
    buffer = aKey->N().BufferLC();
    LOG(_L8("N"));
    LOGHEX((*buffer));
    CleanupStack::PopAndDestroy(buffer);
    LOG(_L8("Result"));
    LOGHEX((*output));
#endif

    CleanupStack::PopAndDestroy(2); // result, input
    return output;
    }

// -----------------------------------------------------------------------------
// OmaCrypto::
//
// -----------------------------------------------------------------------------
//
EXPORT_C HBufC8* OmaCrypto::RsaVerifyL(
    CRSAPublicKey* aKey,
    const TDesC8& aInput)
    {
    RInteger result;
    RInteger input;
    HBufC8* output;

    input = OS2IPL(aInput);
    CleanupClosePushL(input);
    result = TInteger::ModularExponentiateL(input, aKey->E(), aKey->N());
    CleanupClosePushL(result);
    output = I2OSPL(result);
    CleanupStack::PopAndDestroy(2); // result, input
    return output;
    }

// -----------------------------------------------------------------------------
// OmaCrypto::
//
// -----------------------------------------------------------------------------
//
EXPORT_C HBufC8* OmaCrypto::RsaKemKwsEncryptL(
    CRSAPublicKey* aKey,
    const TDesC8& aRek,
    const TDesC8& aMac)
    {
    HBufC8* C1 = NULL;
    HBufC8* C2 = NULL;
    HBufC8* C;
    TPtr8 c(0, 0);
    TBuf8<128> Z;
    TBuf8<KKeySize + KMacSize> keyAndMac;
    HBufC8* kek = NULL;
    const TUint8 KFirstBitMask( 0x7f );

    Z.SetLength(128);
    MDrmKeyStorage* storage = DrmKeyStorageNewL();
    TCleanupItem storageCleanup( DeleteObject, storage );
    CleanupStack::PushL(storageCleanup);
    storage->RandomDataGetL(Z,Z.Size());
    CleanupStack::PopAndDestroy( storage );

    Z[0] &= KFirstBitMask;

#ifdef LOGGING
    LOG(_L("RsaKemKwsEncryptL"));
    LOG(_L("random Z"));
    LOGHEX(Z);
#endif
    C1 = RsaEncryptL(aKey, Z);
    CleanupStack::PushL(C1);
    kek = KdfL(Z, KNullDesC8, KKeySize);
    CleanupStack::PushL(kek);
    keyAndMac.Copy(aMac);
    keyAndMac.Append(aRek);
    C2 = AesWrapL(*kek, keyAndMac);
    CleanupStack::PushL(C2);
    C = HBufC8::NewL(C1->Length() + C2->Length());
    c.Set(C->Des());
    c.Copy(*C1);
    c.Append(*C2);

#ifdef LOGGING
    LOG(_L("RsaKemKwsEncryptL"));
    LOG(_L("Z"));
    LOGHEX(Z);
    LOG(_L("C1"));
    LOGHEX((*C1));
    LOG(_L("C2"));
    LOGHEX((*C2));
#endif

    CleanupStack::PopAndDestroy(3); // C2, kek, C1
    return C;
    }

// -----------------------------------------------------------------------------
// OmaCrypto::
//
// -----------------------------------------------------------------------------
//
EXPORT_C void OmaCrypto::RsaKemKwsDecryptL(
    MDrmKeyStorage* aKeyStorage,
    const TDesC8& aInput,
    TDes8& aRek,
    TDes8& aMac)
    {
    HBufC8* C1 = NULL;
    HBufC8* C2 = NULL;
    HBufC8* Z = NULL;
    HBufC8* keyAndMac = NULL;
    HBufC8* kek = NULL;

    C1 = aInput.Left(aInput.Length() - KWrappedKeySize).AllocL();
    CleanupStack::PushL(C1);
    C2 = aInput.Right(KWrappedKeySize).AllocL();
    CleanupStack::PushL(C2);
    Z = aKeyStorage->RsaDecryptL(*C1);
    CleanupStack::PushL(Z);
    kek = KdfL(*Z, KNullDesC8, KKeySize);
    CleanupStack::PushL(kek);
    keyAndMac = AesUnwrapL(*kek, *C2);
    aMac.Copy(keyAndMac->Left(KKeySize));
    aRek.Copy(keyAndMac->Right(KMacSize));

#ifdef LOGGING
    LOG(_L("RsaKemKwsDecryptL"));
    LOG(_L("Z"));
    LOGHEX((*Z));
    LOG(_L("C1"));
    LOGHEX((*C1));
    LOG(_L("C2"));
    LOGHEX((*C2));
    LOG(_L("REK + MAC"));
    LOGHEX((*keyAndMac));
    LOG(_L("KEK"));
    LOGHEX((*kek));
#endif

    delete keyAndMac;
    CleanupStack::PopAndDestroy(4); // kek, Z, C2, C1
    }

// -----------------------------------------------------------------------------
// OmaCrypto::
//
// -----------------------------------------------------------------------------
//
EXPORT_C HBufC8* OmaCrypto::KdfL(
    const TDesC8& aInput,
    const TDesC8& aOtherData,
    TInt aKLen)
    {
    TUint32 d;
    CSHA1* h = NULL;
    TBuf8<sizeof(d)> c;
    HBufC8* t = NULL;
    HBufC8* r = NULL;
    TPtr8 T(0, 0);

    h = CSHA1::NewL();
    CleanupStack::PushL(h);
    t = HBufC8::NewL(aKLen + SHA1_HASH);
    CleanupStack::PushL(t);
    T.Set(t->Des());
    c.SetLength(sizeof(d));
    d = 1;
    do
        {
        WriteUint32ToBlock(d, c, 0);
        h->Hash(aInput);
        h->Hash(c);
        if (aOtherData.Length() > 0)
            {
            h->Hash(aOtherData);
            }
        T.Append(h->Final());
        d++;
        }
    while (d < SHA1_HASH / aKLen);
    r = t->Left(aKLen).AllocL();
    CleanupStack::PopAndDestroy(2); // Tbuf, h
    return r;
    }

// -----------------------------------------------------------------------------
// OmaCrypto::
//
// -----------------------------------------------------------------------------
//
EXPORT_C HBufC8* OmaCrypto::I2OSPL(
    RInteger& aInt)
    {
    HBufC8* r = aInt.BufferLC();
    CleanupStack::Pop(r);
    return r;
    }

// -----------------------------------------------------------------------------
// OmaCrypto::
//
// -----------------------------------------------------------------------------
//
EXPORT_C RInteger OmaCrypto::OS2IPL(
    const TDesC8& aOctetStream)
    {
    RInteger r;
    TInt i;

    r = RInteger::NewL(0);
    for (i = 0; i < aOctetStream.Length(); i++)
        {
        r *= 256;
        r += aOctetStream[i];
        }
    return r;
    }

// -----------------------------------------------------------------------------
// OmaCrypto::
//
// -----------------------------------------------------------------------------
//
EXPORT_C HBufC8* OmaCrypto::AesWrapL(
    const TDesC8& aKey,
    const TDesC8& aInput)
    {
    HBufC8* ret = NULL;
    TBuf8<KWrapBlockSize> A;
    TBuf8<2 * KWrapBlockSize> B;
    TInt i;
    TInt j;
    TPtr8 R(0, 0);
    TPtr8 Rn(0, 0);
    TInt t;
    TInt n;
    CAESEncryptor* e;

    e = CAESEncryptor::NewL(aKey);
    User::LeaveIfNull(e);
    CleanupStack::PushL(e);
    n = aInput.Length() / KWrapBlockSize;
    ret = HBufC8::NewMax((n + 1) * KWrapBlockSize);
    User::LeaveIfNull(ret);
    R.Set(ret->Des());
    A.Copy(KWrapIv);
    R.Copy(A);
    R.Append(aInput);
    for (j = 0; j <= 5; j++)
        {
        for (i = 1; i <= n; i++)
            {
            t = (n * j) + i;
            Rn.Set(const_cast<TUint8*>(R.Ptr()) + KWrapBlockSize * i,
                KWrapBlockSize, KWrapBlockSize);
            B.Copy(A);
            B.Append(Rn);
            e->Transform(B);
            A.Copy(B.Left(KWrapBlockSize));
            A[KWrapBlockSize - 1] ^= t;
            Rn.Copy(B.Right(KWrapBlockSize));
            }
        }
    Rn.Set(const_cast<TUint8*>(R.Ptr()), KWrapBlockSize, KWrapBlockSize);
    Rn.Copy(A);
    CleanupStack::PopAndDestroy(); // e
    return ret;
    }

// -----------------------------------------------------------------------------
// OmaCrypto::
//
// -----------------------------------------------------------------------------
//
EXPORT_C HBufC8* OmaCrypto::AesUnwrapL(
    const TDesC8& aKey,
    const TDesC8& aInput)
    {
    HBufC8* ret = NULL;
    TBuf8<KWrapBlockSize> A;
    TBuf8<2 * KWrapBlockSize> B;
    TInt i;
    TInt j;
    TPtr8 R(0, 0);
    TPtr8 Rn(0, 0);
    TInt t;
    TInt n;
    CAESDecryptor* d;

    d = CAESDecryptor::NewL(aKey);
    User::LeaveIfNull(d);
    CleanupStack::PushL(d);
    n = (aInput.Length() / KWrapBlockSize) - 1;
    ret = HBufC8::NewMax((n + 1) * KWrapBlockSize);
    User::LeaveIfNull(ret);
    R.Set(ret->Des());
    A.Copy(aInput.Left(KWrapBlockSize));
    R.Copy(aInput);
    for (j = 5; j >= 0; j--)
        {
        for (i = n; i >= 1; i--)
            {
            t = (n * j) + i;
            Rn.Set(const_cast<TUint8*>(R.Ptr()) + KWrapBlockSize * i,
                KWrapBlockSize, KWrapBlockSize);
            A[KWrapBlockSize - 1] ^= t;
            B.Copy(A);
            B.Append(Rn);
            d->Transform(B);
            A.Copy(B.Left(KWrapBlockSize));
            Rn.Copy(B.Right(KWrapBlockSize));
            }
        }
    R.Delete(0, KWrapBlockSize);
    CleanupStack::PopAndDestroy(); // e

#ifdef LOGGING
    LOG(_L("OmaCrypto::AesUnwrapL"));
    LOG(_L("Key:"));
    LOGHEX(aKey);
    LOG(_L("Input:"));
    LOGHEX(aInput);
    LOG(_L("Ret:"));
    LOGHEX((*ret));
#endif

    return ret;
    }

// -----------------------------------------------------------------------------
// OmaCrypto::
//
// -----------------------------------------------------------------------------
//
EXPORT_C HBufC8* OmaCrypto::Mgf1L(
    const TDesC8& aMfgSeed,
    TInt aMaskLen)
    {
    TUint32 c;
    CSHA1* h = NULL;
    TBuf8<4> counter;
    HBufC8* t = NULL;
    TPtr8 T(0, 0);
    TInt n;

    n = aMaskLen / SHA1_HASH + (aMaskLen % SHA1_HASH ? 1 : 0);
    h = CSHA1::NewL();
    CleanupStack::PushL(h);
    t = HBufC8::NewL(aMaskLen + 2 * SHA1_HASH + 1);
    CleanupStack::PushL(t);
    T.Set(t->Des());
    counter.SetLength(4);
    c = 0;
    do
        {
        WriteUint32ToBlock(c, counter, 0);
        h->Hash(aMfgSeed);
        h->Hash(counter);
        T.Append(h->Final());
        c++;
        }
    while (c < n);
    T.SetLength(aMaskLen);
    CleanupStack::Pop(); // t
    CleanupStack::PopAndDestroy(); // h
    return t;
    }

// -----------------------------------------------------------------------------
// OmaCrypto::RsaPssSignHashL
// Sign a hash (not a message!) using RSASSA-PSS.
// NOTE: The implementation is only correct for a private key modulus divisible
// by eight!
// -----------------------------------------------------------------------------
//
EXPORT_C HBufC8* OmaCrypto::RsaPssSignHashL(
    MDrmKeyStorage* aKeyStorage,
    const TDesC8& aMHash)
    {
    CSHA1* hasher = NULL;
    HBufC8* em = NULL;
    HBufC8* S = NULL;
    TPtr8 EM(0, 0);
    TInt modBits;
    TInt emLen;
    TBuf8<KPssSaltLength> salt;
    TBuf8<SHA1_HASH> H;
    _LIT8(KMNullVector, "\0\0\0\0\0\0\0\0");
    HBufC8* db = NULL;
    TPtr8 DB(0, 0);
    HBufC8* dbMask = NULL;
    TInt i;

    modBits = aKeyStorage->ModulusSize();

    if( modBits < 0)
        {
        User::LeaveIfError( modBits );
        }
    emLen = modBits / 8;
    hasher = CSHA1::NewL();
    CleanupStack::PushL(hasher);

    salt.SetLength(KPssSaltLength);

    aKeyStorage->RandomDataGetL(salt,salt.Size());

#ifdef LOGGING
    LOG(_L("RsaKemKwsEncryptL"));
    LOG(_L("salt"));
    LOGHEX(salt);
#endif

    hasher->Hash(KMNullVector);
    hasher->Hash(aMHash);
    hasher->Hash(salt);
    H.Copy(hasher->Final());

    db = HBufC8::NewL(emLen - SHA1_HASH - 1);
    CleanupStack::PushL(db);
    DB.Set(db->Des());
    DB.FillZ(emLen - KPssSaltLength - SHA1_HASH - 2);
    DB.Append(0x01);
    DB.Append(salt);

    dbMask = Mgf1L(H, emLen - SHA1_HASH - 1);
    CleanupStack::PushL(dbMask);

    for (i = 0; i < emLen - SHA1_HASH - 1; i++)
        {
        DB[i] = DB[i] ^ (*dbMask)[i];
        }

    em = HBufC8::NewL(emLen);
    CleanupStack::PushL(em);
    EM.Set(em->Des());
    EM.Copy(DB);
    EM[0] &= 0x7f;
    EM.Append(H);
    EM.Append(0xbc);
    S = aKeyStorage->RsaSignL(EM);

    CleanupStack::PopAndDestroy(4); // em, dbMask, db, hasher
    return S;
    }

// -----------------------------------------------------------------------------
// OmaCrypto::
//
// -----------------------------------------------------------------------------
//
EXPORT_C TBool OmaCrypto::RsaPssVerifyHashL(
    CRSAPublicKey* aKey,
    const TDesC8& aSignature,
    const TDesC8& aMHash)
    {
    TBool r = EFalse;
    CSHA1* hasher = NULL;
    HBufC8* EM = NULL;
    TInt emLen;
    TBuf8<KPssSaltLength> salt;
    TBuf8<SHA1_HASH> H;
    _LIT8(KMNullVector, "\0\0\0\0\0\0\0\0");
    HBufC8* db = NULL;
    TPtr8 DB(0, 0);
    HBufC8* dbMask = NULL;
    TInt i;

    EM = RsaVerifyL(aKey, aSignature);
    CleanupStack::PushL(EM);
    emLen = EM->Size();

    hasher = CSHA1::NewL();
    CleanupStack::PushL(hasher);

    db = EM->Left(emLen - SHA1_HASH - 1).AllocL();
    CleanupStack::PushL(db);
    DB.Set(db->Des());
    H.Copy(EM->Mid(emLen - SHA1_HASH - 1, SHA1_HASH));

    dbMask = Mgf1L(H, emLen - SHA1_HASH - 1);
    CleanupStack::PushL(dbMask);

    for (i = 0; i < emLen - SHA1_HASH - 1; i++)
        {
        DB[i] = DB[i] ^ (*dbMask)[i];
        }

    salt.Copy(DB.Right(KPssSaltLength));

    hasher->Hash(KMNullVector);
    hasher->Hash(aMHash);
    hasher->Hash(salt);
    if (hasher->Final().Compare(H) == 0)
        {
        r = ETrue;
        }

    CleanupStack::PopAndDestroy(4); // dbMask, db, hasher, EM
    return r;
    }

//  End of File