diff -r 000000000000 -r 2c201484c85f cryptoservices/asnpkcs/source/asnpkcs8.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cryptoservices/asnpkcs/source/asnpkcs8.cpp Wed Jul 08 11:25:26 2009 +0100 @@ -0,0 +1,506 @@ +/* +* Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of the License "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: +* +*/ + + +#include +#include +#include +#include "asnpkcs.h" +#include +#include +#include + +/*static*/ EXPORT_C CDecPKCS8Data* TASN1DecPKCS8::DecodeDERL(const TDesC8& aBinaryData) +{ + return (CDecPKCS8Data::NewL(aBinaryData)); +} + +/* +EncryptedPrivateKeyInfo ::= SEQUENCE { + encryptionAlgorithm EncryptionAlgorithmIdentifier, + encryptedData EncryptedData } +EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier +EncryptedData ::= OCTET STRING +*/ + +/*static*/ EXPORT_C CDecPKCS8Data* TASN1DecPKCS8::DecodeEncryptedDERL(const TDesC8& aBinaryData, const TDesC8& aPassword) +{ + TASN1DecGeneric seqGen(aBinaryData); + seqGen.InitL(); + if (seqGen.Tag() != EASN1Sequence) + { + User::Leave(KErrArgument); + } + + TASN1DecSequence dec; + CArrayPtrFlat* theData = dec.DecodeDERLC(seqGen); + TInt seqIndex = 0; + TInt count = theData->Count(); + if (seqIndex >= count) + { + User::Leave(KErrArgument); + } + +// Get the first part of the sequence -> PKCS5 data + TASN1DecGeneric* pkcs5 = theData->operator[](seqIndex); + TPtrC8 theContent(pkcs5->Encoding()); // expect this to be a sequence + CPBEncryptParms* encryptParams = TASN1DecPKCS5::DecodeDERL(theContent); + CleanupStack::PushL(encryptParams); + +// Create the decryptor + CPBEncryptElement* encryptElement = CPBEncryptElement::NewLC(aPassword, *encryptParams); + CPBDecryptor* decryptor = encryptElement->NewDecryptLC(); +// Decrypt the final part of the sequence -> encrypted PKCS8 object + TASN1DecGeneric* pkcs8 = theData->operator[](count-1); + if (pkcs8->Tag() != EASN1OctetString) + { + User::Leave(KErrArgument); + } + TPtrC8 encryptedKey(pkcs8->GetContentDER()); + TUint encryptLength = encryptedKey.Length(); + TUint maxDecryptLength = decryptor->MaxOutputLength(encryptLength); + if (maxDecryptLength<=0) + { + User::Leave(KErrGeneral); + } + + HBufC8* decryptOutput = HBufC8::NewLC(encryptLength); + TPtr8 decryptOutputPtr(decryptOutput->Des()); + decryptOutputPtr.FillZ(); + decryptor->Process(encryptedKey, decryptOutputPtr); + + CDecPKCS8Data* pkcs8Data = CDecPKCS8Data::NewL(decryptOutputPtr); + + // decryptOutput decryptor encryptElement + // encryptParams theData + CleanupStack::PopAndDestroy(5, theData); + + return (pkcs8Data); +} + +/* +Sample cleartext pkcs8 data from pkcs8dsa.001: + + SEQUENCE +30 Tag: Constructed sequence +81 c8 Length (may be one or more bytes) + + INTEGER +02 Tag: Integer +01 Length: 1 byte +00 Value: 0 + + SEQUENCE +30 Tag: Constructed sequence +81 a9 Length (may be one or more bytes) + + OID +06 Tag: OID +07 Length: 7 bytes +2a 86 48 ce 38 04 01 + Value: dsa (1 2 840 10040 4 1) +*/ + +_LIT8(KPKCS8DataVersion0, "\x02\x01\x00"); +_LIT8(KPKCS8DataOIDDSA, "\x06\x07\x2a\x86\x48\xce\x38\x04\x01"); +_LIT8(KPKCS8DataOIDRSA, "\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01"); + +/*static*/ EXPORT_C TBool TASN1DecPKCS8::IsPKCS8Data(const TDesC8& aBinaryData) + { + // We don't decode the data because we may only have the first few bytes - + // instead we check the ASN1 by hand. + + ASSERT(aBinaryData.Length() >= KIsPKCS8DataMinLength); + TInt pos = 0; + + return + IsASN1Sequence(aBinaryData, pos) && + IsExpectedData(aBinaryData, pos, KPKCS8DataVersion0) && + IsASN1Sequence(aBinaryData, pos) && + (IsExpectedData(aBinaryData, pos, KPKCS8DataOIDDSA) || + IsExpectedData(aBinaryData, pos, KPKCS8DataOIDRSA)); + } + +/* +Sample encrypted pkcs8 data from encryptPK8rsa1.txt + + SEQUENCE +30 Tag: Constructed sequence +82 01 a3 Length (may be one or more bytes) + + SEQUENCE +30 Tag: Constructed sequence +3d Length (may be one or more bytes) + + OID +06 Tag: OID +09 Length: 9 bytes +2a 86 48 86 f7 0d 01 05 0d + Value: pkcs5PBES2 (1 2 840 113549 1 5 13) + + SEQUENCE +30 Tag: Constructed sequence +30 Length (may be one or more bytes) + + SEQUENCE +30 Tag: Constructed sequence +1b Length (may be one or more bytes) + + OID +06 Tag: OID +09 Length: 9 bytes +2a 86 48 86 f7 0d 01 05 0c + Value: pkcs5PBKDF2 (1 2 840 113549 1 5 12) +*/ + +_LIT8(KEncryptedPKCS8DataOIDpkcs5PBES2, "\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x05\x0d"); +_LIT8(KEncryptedPKCS8DataOIDpkcs5PBKDF2, "\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x05\x0c"); + +EXPORT_C TBool TASN1DecPKCS8::IsEncryptedPKCS8Data(const TDesC8& aBinaryData) + { + // We don't decode the data because we may only have the first few bytes - + // instead we check the ASN1 by hand. + + ASSERT(aBinaryData.Length() >= KIsEncryptedPKCS8DataMinLength); + TInt pos = 0; + + return + IsASN1Sequence(aBinaryData, pos) && + IsASN1Sequence(aBinaryData, pos) && + IsExpectedData(aBinaryData, pos, KEncryptedPKCS8DataOIDpkcs5PBES2) && + IsASN1Sequence(aBinaryData, pos) && + IsASN1Sequence(aBinaryData, pos) && + IsExpectedData(aBinaryData, pos, KEncryptedPKCS8DataOIDpkcs5PBKDF2); + } + +/** + * Determine if the some data represents the start of an ASN1 sequence. The + * data is specified by a descriptor and an offset. If the data matches, the + * offset is advanced to point to the contents of the sequence. + */ +TBool TASN1DecPKCS8::IsASN1Sequence(const TDesC8& aBinaryData, TInt& aPos) + { + // Check we have enough data + if ((aPos + 2) >= aBinaryData.Length()) + { + return EFalse; + } + // Check the outermost sequence is valid + if (aBinaryData[aPos++] != 0x30 /* constructed sequence */) + { + return EFalse; + } + // Skip sequence length + TInt length0 = aBinaryData[aPos++]; + if (length0 & 0x80) + { + aPos += length0 & 0x7f; + } + return ETrue; + } + +/** + * Determine if some data starts with an expected string. The data is specified + * by a descriptor and an offset. If the data matches, the offset is advanced + * to point after the match. + */ +TBool TASN1DecPKCS8::IsExpectedData(const TDesC8& aBinaryData, TInt& aPos, const TDesC8& aExpectedData) + { + TInt length = aExpectedData.Length(); + // Check we have enough data + if (aPos + length >= aBinaryData.Length()) + { + return EFalse; + } + // Check data matches + if (aBinaryData.Mid(aPos, length) != aExpectedData) + { + return EFalse; + } + aPos += length; + return ETrue; + } + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ +// PKCS#8 object data representation +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +/*static*/ CDecPKCS8Data* CDecPKCS8Data::NewL(const TDesC8& aData) +{ + CDecPKCS8Data* me = new (ELeave) CDecPKCS8Data(); + CleanupStack::PushL(me); + me->ConstructL(aData); + CleanupStack::Pop(me); + return (me); +} + +CDecPKCS8Data::CDecPKCS8Data() +{} + +CDecPKCS8Data::~CDecPKCS8Data() +{ + if (iKeyPairData) + { + iKeyPairData->Release(); + iKeyPairData = NULL; + } + + if (iAttributes) + { + delete iAttributes; + iAttributes = NULL; + } +} + +// Because this is part of construction, don't rely on +// cleanup of partially constructed items... +void CDecPKCS8Data::ConstructL(const TDesC8& aData) + { + TASN1DecGeneric seqGen(aData); + seqGen.InitL(); + if (seqGen.Tag() != EASN1Sequence) + { + User::Leave(KErrArgument); + } + + TASN1DecSequence seq; + CArrayPtrFlat* seqContents = seq.DecodeDERLC(seqGen); + if (seqContents->Count() < 3 || seqContents->Count() > 4) + { + User::Leave(KErrArgument); + } + +// VERSION + if (seqContents->At(0)->Tag() != EASN1Integer) + { + User::Leave(KErrArgument); + } + TASN1DecInteger intDecoder; + iVersion = intDecoder.DecodeDERShortL(*(seqContents->At(0))); + if (iVersion!=0) + { + User::Leave(KErrArgument); + } + +// ALGORITHM + CX509AlgorithmIdentifier* algorithm = CX509AlgorithmIdentifier::NewLC(seqContents->At(1)->Encoding()); + iAlgorithmID = algorithm->Algorithm(); + +// KEY DATA + switch (iAlgorithmID) + {// Only support RSA, DSA, DH + case ERSA: + iKeyPairData = CPKCS8KeyPairRSA::NewL(*(seqContents->At(2))); + break; + + case EDSA: + { + TPtrC8 params(algorithm->EncodedParams()); + iKeyPairData = CPKCS8KeyPairDSA::NewL(params, *(seqContents->At(2))); + break; + } + + case EDH: // Currently not supported because no test data is available + default: + User::Leave(KErrNotSupported); + break; + } + + CleanupStack::PopAndDestroy(algorithm); + + if (seqContents->Count() == 4) + { + // I think we should check the tag is zero here, but we're going to be + // lenient due to lack of real test data + + //if (seqContents->At(3)->Tag() != 0) // Implicitly tagged + // { + // User::Leave(KErrArgument); + // } + + iAttributes = seqContents->At(3)->Encoding().AllocL(); + } + + CleanupStack::PopAndDestroy(seqContents); + } + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ +// RSA decoding +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +/*static*/ MPKCS8DecodedKeyPairData* CPKCS8KeyPairRSA::NewL(const TASN1DecGeneric& aSource) +{ + CPKCS8KeyPairRSA* me = new (ELeave) CPKCS8KeyPairRSA(); + CleanupStack::PushL(me); + me->ConstructL(aSource); + CleanupStack::Pop(me); + + return (me); +} + +MPKCS8DecodedKeyPairData::~MPKCS8DecodedKeyPairData() + { + } + +CPKCS8KeyPairRSA::~CPKCS8KeyPairRSA() +{ + delete iPublicKey; + delete iPrivateKey; +} + +void CPKCS8KeyPairRSA::Release() +{ + delete this; +} + +void CPKCS8KeyPairRSA::GetKeyIdentifierL(TKeyIdentifier& aKeyIdentifier) const +{ + if (iPublicKey) + KeyIdentifierUtil::RSAKeyIdentifierL(*iPublicKey, aKeyIdentifier); + else + User::Leave(KErrNotReady); +} + +TUint CPKCS8KeyPairRSA::KeySize() const +{ + if (!iPublicKey) + { + ASSERT(EFalse); + return (0xffffffff); + } + + const TInteger& modulus = iPublicKey->N(); + + TUint size = modulus.BitCount(); + return (size); +} + +void CPKCS8KeyPairRSA::ConstructL(const TASN1DecGeneric& aSource) +{ + TPtrC8 theContent(aSource.GetContentDER()); + TASN1DecRSAKeyPair keyPairDecoder; + TInt tempPos = 0; + keyPairDecoder.DecodeDERL(theContent, tempPos, iPublicKey, iPrivateKey); +} + + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ +// DSA decoding +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +/*static*/ MPKCS8DecodedKeyPairData* CPKCS8KeyPairDSA::NewL(const TDesC8& aParamsData, const TASN1DecGeneric& aSource) +{ + CPKCS8KeyPairDSA* me = new (ELeave) CPKCS8KeyPairDSA(); + CleanupStack::PushL(me); + me->ConstructL(aParamsData, aSource); + CleanupStack::Pop(me); + + return (me); +} + +CPKCS8KeyPairDSA::~CPKCS8KeyPairDSA() +{ + delete iPublicKey; + delete iPrivateKey; +} + +void CPKCS8KeyPairDSA::Release() +{ + delete this; +} + +void CPKCS8KeyPairDSA::GetKeyIdentifierL(TKeyIdentifier& aKeyIdentifier) const +{ + if (iPublicKey) + KeyIdentifierUtil::DSAKeyIdentifierL(*iPublicKey, aKeyIdentifier); + else + User::Leave(KErrNotReady); +} + +TUint CPKCS8KeyPairDSA::KeySize() const +{ + if (!iPublicKey) + { + ASSERT(EFalse); + return (0xffffffff); + } + + const TInteger& P = iPublicKey->P(); + + TUint size = P.BitCount(); + return (size); +} + +void CPKCS8KeyPairDSA::ConstructL(const TDesC8& aParamsData, const TASN1DecGeneric& aSource) +{ + TX509KeyFactory keyFactory; + CDSAParameters* params = keyFactory.DSAParametersL(aParamsData); + CleanupStack::PushL(params); + + RInteger P = RInteger::NewL(params->P()); + CleanupStack::PushL(P); + + RInteger Q = RInteger::NewL(params->Q()); + CleanupStack::PushL(Q); + + RInteger G = RInteger::NewL(params->G()); + CleanupStack::PushL(G); + + if (aSource.Tag() != EASN1OctetString) + { + User::Leave(KErrArgument); + } + TASN1DecOctetString octetDecoder; + HBufC8* wrapped = octetDecoder.DecodeDERL(aSource); + CleanupStack::PushL(wrapped); + + TASN1DecGeneric integer(*wrapped); + integer.InitL(); + if (integer.Tag() != EASN1Integer) + { + User::Leave(KErrArgument); + } + TASN1DecInteger decInt; + RInteger X = decInt.DecodeDERLongL(integer); + CleanupStack::PopAndDestroy(wrapped); + CleanupStack::PushL(X); + + RInteger P1 = RInteger::NewL(params->P()); + CleanupStack::PushL(P1); + + RInteger Q1 = RInteger::NewL(params->Q()); + CleanupStack::PushL(Q1); + + RInteger G1 = RInteger::NewL(params->G()); + CleanupStack::PushL(G1); + + RInteger Y = TInteger::ModularExponentiateL(G, X, P); + CleanupStack::PushL(Y); + + iPublicKey = CDSAPublicKey::NewL(P1, Q1, G1, Y); + + // Now have to pop everything owned by iPublicKey otherwise it will be + // deleted twice if the CDSAPrivateKey::NewL leaves + + CleanupStack::Pop(4, &P1); // now owned by iPublicKey + + iPrivateKey = CDSAPrivateKey::NewL(P, Q, G, X); + + CleanupStack::Pop(4, &P); // now owned by iPrivateKey + + CleanupStack::PopAndDestroy(params); +}