sl@0: /* sl@0: * Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: * All rights reserved. sl@0: * This component and the accompanying materials are made available sl@0: * under the terms of the License "Eclipse Public License v1.0" sl@0: * which accompanies this distribution, and is available sl@0: * at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: * sl@0: * Initial Contributors: sl@0: * Nokia Corporation - initial contribution. sl@0: * sl@0: * Contributors: sl@0: * sl@0: * Description: sl@0: * sl@0: */ sl@0: sl@0: sl@0: #include "pkcs12recog.h" sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: /** UID for PKCS#12 mime-type recognizer */ sl@0: static const TUint KPkcs12RecognizerImplementationId = 0x20001520; sl@0: static const TUid KUidMimePkcs12Recognizer = { KPkcs12RecognizerImplementationId }; sl@0: sl@0: /** sl@0: Minimum buffer size assuming worst-case 3 byte lengths for variable sl@0: length fields. The file will never be smaller that this because it must sl@0: also contain either the MacData or be a signed object. sl@0: sl@0: SEQ 5 bytes sl@0: INTEGER 3 bytes sl@0: SEQ 5 bytes sl@0: OID 11 bytes sl@0: sl@0: total = 24 sl@0: */ sl@0: static const TInt KPkcs12MinBufSize = 24; sl@0: sl@0: /** Mime-type for PKCS#12 */ sl@0: _LIT8(KDataTypePkcs12, "application/x-pkcs12"); sl@0: /** The number of mime-types recognised */ sl@0: static const TInt KSupportedDataTypesTotal = 1; sl@0: sl@0: // ASN.1 / DER constants sl@0: /** DER encoding of an ASN.1 sequence tag */ sl@0: static const TUint8 KDerSequenceOctet = 0x30; sl@0: /** DER encoding of an ASN.1 integer tag */ sl@0: static const TUint8 KDerIntegerOctet = 0x02; sl@0: /** Expected PKCS#12 version number */ sl@0: static const TInt KPkcs12Version = 3; sl@0: sl@0: /** sl@0: DER encoding of PKCS#7 data content type OID sl@0: OID = 1.2.840.113549.1.7.1 sl@0: */ sl@0: static const TUint8 KPkcs7DataOid[] = sl@0: { sl@0: 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 sl@0: }; sl@0: /** Length of encoded PKCS#7 data OID */ sl@0: static const TUint KPkcs7DataOidLen = sizeof(KPkcs7DataOid); sl@0: sl@0: /** sl@0: DER encoding of PKCS#7 signed data content type OID sl@0: OID = 1.2.840.113549.1.7.2 sl@0: */ sl@0: static const TUint8 KPkcs7SignedDataOid[] = sl@0: { sl@0: 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 sl@0: }; sl@0: /** Length of encoded PKCS#7 signed data OID */ sl@0: static const TUint KPkcs7SignedDataOidLen = sizeof(KPkcs7SignedDataOid); sl@0: sl@0: /** PKCS#12 recognizer panic identifier */ sl@0: _LIT(KPkcs12RecogPanic, "PKCS12RECOG"); sl@0: sl@0: CPkcs12Recognizer::CPkcs12Recognizer() sl@0: : CApaDataRecognizerType(KUidMimePkcs12Recognizer, CApaDataRecognizerType::ENormal) sl@0: { sl@0: iCountDataTypes = KSupportedDataTypesTotal; sl@0: } sl@0: sl@0: TUint CPkcs12Recognizer::PreferredBufSize() sl@0: { sl@0: return KPkcs12MinBufSize; sl@0: } sl@0: sl@0: TDataType CPkcs12Recognizer::SupportedDataTypeL(TInt aIndex) const sl@0: { sl@0: __ASSERT_DEBUG(aIndex >= 0 && aIndex < KSupportedDataTypesTotal, sl@0: Panic(EPanicInvalidDataType)); sl@0: sl@0: if (aIndex < 0 || aIndex >= KSupportedDataTypesTotal) sl@0: { sl@0: User::Leave(KErrArgument); sl@0: } sl@0: sl@0: return TDataType(KDataTypePkcs12); sl@0: } sl@0: sl@0: void CPkcs12Recognizer::DoRecognizeL(const TDesC& aName, const TDesC8& aBuffer) sl@0: { sl@0: __UHEAP_MARK; sl@0: iConfidence = ENotRecognized; sl@0: sl@0: TPtrC8 pkcs12Buffer(aBuffer); sl@0: TBuf8 fileBuffer; sl@0: sl@0: if (aBuffer.Length() < KPkcs12MinBufSize) sl@0: { sl@0: if (RFile* fh = FilePassedByHandleL()) sl@0: { sl@0: User::LeaveIfError(fh->Read(0, fileBuffer, KPkcs12MinBufSize)); sl@0: } sl@0: else sl@0: { sl@0: // no file handle so attempt to read the file. This may sl@0: // fail if the file is in a private directory sl@0: RFs fs; sl@0: User::LeaveIfError(fs.Connect()); sl@0: CleanupClosePushL(fs); sl@0: RFile file; sl@0: TInt err = file.Open(fs, aName, EFileRead | EFileShareAny | EFileStream); sl@0: sl@0: // do nothing if the file fails to open, for any reason sl@0: if (err == KErrNone) sl@0: { sl@0: CleanupClosePushL(file); sl@0: User::LeaveIfError(file.Read(0, fileBuffer, KPkcs12MinBufSize)); sl@0: CleanupStack::PopAndDestroy(&file); sl@0: } sl@0: CleanupStack::PopAndDestroy(&fs); sl@0: } sl@0: // buffer does not exist or is too small so attempt to sl@0: // read a buffer from the file sl@0: pkcs12Buffer.Set(fileBuffer); sl@0: } sl@0: sl@0: sl@0: if (pkcs12Buffer.Length() > 0 && DoRecognizeBufferL(pkcs12Buffer)) sl@0: { sl@0: iDataType = TDataType(KDataTypePkcs12); sl@0: iConfidence = EProbable; sl@0: if (HasPkcs12Extension(aName)) sl@0: { sl@0: iConfidence = ECertain; sl@0: } sl@0: } sl@0: __UHEAP_MARKEND; sl@0: } sl@0: sl@0: TBool CPkcs12Recognizer::HasPkcs12Extension(const TDesC& aName) sl@0: { sl@0: _LIT(KPfxExt, ".pfx"); sl@0: _LIT(KP12Ext, ".p12"); sl@0: sl@0: TBool match = EFalse; sl@0: sl@0: // It is not possible to use a TParse/TParsePtr here because the filename sl@0: // supplied when a file-handle is passed is of the form ::filename.ext and sl@0: // is not recognised as a valid filename sl@0: if (aName.Length() > 4) sl@0: { sl@0: const TPtrC ext = aName.Right(4); sl@0: if (ext.CompareF(KPfxExt) == 0 || ext.CompareF(KP12Ext) == 0) sl@0: { sl@0: match = ETrue; sl@0: } sl@0: } sl@0: return match; sl@0: } sl@0: sl@0: TBool CPkcs12Recognizer::DoRecognizeBufferL(const TDesC8& aBuffer) sl@0: { sl@0: TBool isPkcs12 = EFalse; sl@0: TUint offset = 0; sl@0: sl@0: if (aBuffer.Length() >= KPkcs12MinBufSize) sl@0: { sl@0: // PFX sl@0: if (ConsumeSequenceL(aBuffer, offset)) sl@0: { sl@0: // Version sl@0: TInt version; sl@0: if (ConsumeIntegerL(aBuffer, offset, version)) sl@0: { sl@0: if (version == KPkcs12Version) sl@0: { sl@0: // authSafe sl@0: if (ConsumeSequenceL(aBuffer, offset)) sl@0: { sl@0: const TPtrC8 dataOid(KPkcs7DataOid, KPkcs7DataOidLen); sl@0: const TPtrC8 signedDataOid(KPkcs7SignedDataOid, KPkcs7SignedDataOidLen); sl@0: // check whether content-type is data or signed data sl@0: if ((aBuffer.Mid(offset, KPkcs7DataOidLen).Compare(dataOid) == 0) || sl@0: (aBuffer.Mid(offset, KPkcs7SignedDataOidLen).Compare(signedDataOid) == 0)) sl@0: { sl@0: isPkcs12 = ETrue; sl@0: } sl@0: } sl@0: } sl@0: } sl@0: } sl@0: } sl@0: return isPkcs12; sl@0: } sl@0: sl@0: TBool CPkcs12Recognizer::ConsumeSequenceL(const TDesC8& aBuffer, TUint& aOffset) const sl@0: { sl@0: TBool isSequence = EFalse; sl@0: if (aBuffer[aOffset] == KDerSequenceOctet) sl@0: { sl@0: // sequence tag found, skip tag and length sl@0: aOffset++; sl@0: TInt length; sl@0: isSequence = ConsumeLengthL(aBuffer, aOffset, length); sl@0: } sl@0: return isSequence; sl@0: } sl@0: sl@0: TBool CPkcs12Recognizer::ConsumeLengthL(const TDesC8& aBuffer, TUint& aOffset, TInt& aLengthOctets) const sl@0: { sl@0: TBool isValidLength = EFalse; sl@0: TUint8 lengthOctet = aBuffer[aOffset]; sl@0: if (lengthOctet & 0x80) sl@0: { sl@0: // The top bit is set so assume the length encoding is in long form sl@0: TUint numOctets = lengthOctet & 0x7f; sl@0: aOffset++; sl@0: sl@0: if (ConsumeBase256L(aBuffer, aOffset, numOctets, aLengthOctets)) sl@0: { sl@0: if (aLengthOctets >= 0) sl@0: { sl@0: // lengths must not be -ve sl@0: isValidLength = ETrue; sl@0: } sl@0: } sl@0: } sl@0: else sl@0: { sl@0: // Top bit zero so assume short form encoding i.e. one octet sl@0: aLengthOctets = lengthOctet & 0x7f; sl@0: aOffset++; sl@0: isValidLength = ETrue; sl@0: } sl@0: return isValidLength; sl@0: } sl@0: sl@0: TBool CPkcs12Recognizer::ConsumeIntegerL(const TDesC8& aBuffer, TUint& aOffset, TInt& aIntVal) const sl@0: { sl@0: TBool isValidInteger = EFalse; sl@0: if (aBuffer[aOffset] == KDerIntegerOctet) sl@0: { sl@0: aOffset++; sl@0: TInt length; sl@0: if (ConsumeLengthL(aBuffer, aOffset, length)) sl@0: { sl@0: isValidInteger = ConsumeBase256L(aBuffer, aOffset, length, aIntVal); sl@0: } sl@0: } sl@0: return isValidInteger; sl@0: } sl@0: sl@0: TBool CPkcs12Recognizer::ConsumeBase256L(const TDesC8& aBuffer, TUint& aOffset, TInt aLengthOctets, TInt& aIntVal) const sl@0: { sl@0: TInt isValid = EFalse; sl@0: if (aLengthOctets <= 4) sl@0: { sl@0: aIntVal = 0; sl@0: while (aLengthOctets-- > 0) sl@0: { sl@0: aIntVal <<= 8; sl@0: aIntVal |= aBuffer[aOffset++]; sl@0: } sl@0: isValid = ETrue; sl@0: } sl@0: return isValid; sl@0: } sl@0: sl@0: CApaDataRecognizerType* CPkcs12Recognizer::CreateRecognizerL() sl@0: { sl@0: return new (ELeave) CPkcs12Recognizer(); sl@0: } sl@0: sl@0: static const TImplementationProxy ImplementationTable[] = sl@0: { sl@0: IMPLEMENTATION_PROXY_ENTRY(KPkcs12RecognizerImplementationId, CPkcs12Recognizer::CreateRecognizerL) sl@0: }; sl@0: sl@0: EXPORT_C const TImplementationProxy* ImplementationGroupProxy(TInt& aTableCount) sl@0: { sl@0: aTableCount = sizeof(ImplementationTable) / sizeof(TImplementationProxy); sl@0: return ImplementationTable; sl@0: } sl@0: sl@0: void CPkcs12Recognizer::Panic(TPkcs12RecogPanic aReason) const sl@0: { sl@0: User::Panic(KPkcs12RecogPanic, aReason); sl@0: }