// V1ContRec.cpp -- definition of CV1ContainerRecord // (c) Copyright Schlumberger Technology Corp., unpublished work, created // 2000. This computer program includes Confidential, Proprietary // Information and is a Trade Secret of Schlumberger Technology Corp. All // use, disclosure, and/or reproduction is prohibited unless authorized // in writing. All Rights Reserved. ////////////////////////////////////////////////////////////////////// #include "NoWarning.h" #include #include #include #include #include "TransactionWrap.h" #include "V1Paths.h" #include "V1Card.h" #include "V1ContRec.h" using namespace std; using namespace scu; using namespace cci; using namespace iop; /////////////////////////// LOCAL/HELPER ///////////////////////////////// namespace { enum // KeyId in card { kidExchange = 0x00, kidSignature = 0x01, kidNone = 0xFF }; TCHAR const szCachedCertSignature[] = TEXT("CERTSI"); TCHAR const szCachedCertExchange[] = TEXT("CERTEX"); TCHAR const szCachedPublicKeySignature[] = TEXT("PUBKSI"); TCHAR const szCachedPublicKeyExchange[] = TEXT("PUBKEX"); BYTE AsKeyId(KeySpec ks) { BYTE kid; switch (ks) { case ksExchange: kid = kidExchange; break; case ksSignature: kid = kidSignature; break; default: throw cci::Exception(cci::ccBadKeySpec); } return kid; } } // namespace /////////////////////////// PUBLIC ///////////////////////////////// // Types // C'tors/D'tors CV1ContainerRecord::CV1ContainerRecord(CV1Card const &rv1card, string const &rsCntrType, CreateMode mode) : m_rcard(rv1card), m_sCntrType(rsCntrType), m_szKeyPath(0) { m_szKeyPath = IsDefault() ? CV1Paths::DefaultKey() : CV1Paths::DefaultContainer(); switch (mode) { case cmNever: if (!Exists()) throw Exception(ccInvalidParameter); break; case cmConditionally: if (!Exists()) Create(); break; case cmAlways: if (!Exists()) Create(); else throw Exception(ccOutOfSymbolTableEntries); break; case cmNoCheck: break; default: throw Exception(ccInvalidParameter); break; } } CV1ContainerRecord::~CV1ContainerRecord() {} // Operators // Operations string CV1ContainerRecord::ComputeSignature(KeySpec ks, string const &rsCipher) const { CTransactionWrap wrap(m_rcard); m_rcard.SmartCard().Select(m_szKeyPath); AutoArrayPtr aabBuffer(new BYTE[rsCipher.length()]); m_rcard.SmartCard().InternalAuth(ktRSA1024, AsKeyId(ks), static_cast(rsCipher.length()), reinterpret_cast(rsCipher.data()), aabBuffer.Get()); return string(reinterpret_cast(aabBuffer.Get()), rsCipher.length()); } void CV1ContainerRecord::Delete() const { CTransactionWrap wrap(m_rcard); // if (IsEmpty()) // throw scu::OsException(NTE_BAD_KEYSET_PARAM); // Open the container file and find the offset of the container DWORD dwFileSize = OpenContainer(); DWORD dwOffset = 0x00; DWORD dwLen = FindOffset(dwOffset); // Actually check the existence of key container if (sizeof ContainerBuffer > dwLen) throw scu::OsException(NTE_BAD_KEYSET); // Intialize search variables DWORD dwNext = dwOffset + dwLen; // Get following ContainerBuffer ContainerBuffer container; GetContainer(dwNext, container); dwLen = container.Size; // Move all following blocks up to deleted block position while (sizeof container <= dwLen) { basic_string bsBuffer(reinterpret_cast(&container), sizeof container); WORD cPublicKeysLength = dwLen - sizeof container; AutoArrayPtr aabPublicKeys(new BYTE[cPublicKeysLength]); if (cPublicKeysLength > 0) { m_rcard.SmartCard().ReadBinary(dwNext + sizeof container, cPublicKeysLength, aabPublicKeys.Get()); } bsBuffer.append(aabPublicKeys.Get(), cPublicKeysLength); m_rcard.SmartCard().WriteBinary(dwOffset, static_cast(bsBuffer.length()), bsBuffer.data()); dwOffset += dwLen; dwNext += dwLen; GetContainer(dwNext, container); dwLen = container.Size; }; // end while loop // NO MORE CONTAINERS TO MOVE UP // if there is still room put 2 null bytes of termination const BYTE NullSize[]= {0x00, 0x00}; if ((dwOffset + 2) <= dwFileSize) m_rcard.SmartCard().WriteBinary(dwOffset, 2, NullSize); } void CV1ContainerRecord::Name(string const &rsNewName) { m_rcard.CardId(rsNewName); } void CV1ContainerRecord::Read(KeySpec ks, CPublicKeyBlob &rKeyBlob) const { CTransactionWrap wrap(m_rcard); if ((ksSignature != ks) && (ksExchange != ks)) throw Exception(ccBadKeySpec); string sBuffer; DWORD dwExponent; if (GetContainerContent(ks, sBuffer, dwExponent)) { CopyMemory(rKeyBlob.bModulus, sBuffer.data(), sBuffer.length()); rKeyBlob.bModulusLength = static_cast(sBuffer.length()); *reinterpret_cast(rKeyBlob.bExponent) = dwExponent; } else rKeyBlob.bModulusLength = 0; } void CV1ContainerRecord::Read(KeySpec ks, string &rsBlob) const { CTransactionWrap wrap(m_rcard); DWORD dwOriginalCrc = 0; // Get blob from the container if (!GetContainerContent(ks, rsBlob, dwOriginalCrc)) throw Exception(ccNoCertificate); // If a non-zero CRC exists, then verify integrity of the // compressed certificate by comparing the CRC read // (original) against a test one generated using the // compressed certificate read. If the CRCs aren't equal, // then the certificate is corrupted and it shouldn't be // decompressed because the decompress routine may go into // an infinite loop or otherwise fail badly without // notification. If the original CRC is zero, then a CRC // wasn't performed so for backward compatibility with // earlier versions the decompression is taken with the // inherent risk. if (0 != dwOriginalCrc) { DWORD dwTestCrc = Crc32(rsBlob.data(), rsBlob.length()); if (dwTestCrc != dwOriginalCrc) throw Exception(ccSymbolDataCorrupted); } } void CV1ContainerRecord::Write(KeySpec ks, CPrivateKeyBlob const &rKeyBlob) { CTransactionWrap wrap(m_rcard); m_rcard.SmartCard().Select(CV1Paths::PrivateKeys()); // Make sure that previous key blocks exists in Secret Key file // or at least the header of the block exists // in order for the Card OS to be able to retrieve the key // that is added in this procedure: // Write the header of previous keys WORD const wPrivateKeyBlockSize = 323; // inversion necessary for PRIVATE KEY BLOC SIZE WORD wBSize = (wPrivateKeyBlockSize >> 8) & 0x00FF; wBSize += (wPrivateKeyBlockSize << 8) & 0x00FF00; BYTE bId; DWORD dwOffset; BYTE bKeyId = AsKeyId(ks); for (dwOffset = 0x00, bId = 0; bId < bKeyId; bId++, dwOffset += wPrivateKeyBlockSize) { BYTE Header[3]; CopyMemory(Header, &wBSize, sizeof WORD); Header[2] = bId + 1; m_rcard.SmartCard().WriteBinary(dwOffset, 3, Header); } m_rcard.SmartCard().WritePrivateKey(rKeyBlob, bKeyId); } void CV1ContainerRecord::Write(KeySpec ks, CPublicKeyBlob const &rKeyBlob) { CTransactionWrap wrap(m_rcard); DWORD dwExponent = *(reinterpret_cast(rKeyBlob.bExponent)); Write(ks, reinterpret_cast(rKeyBlob.bModulus), rKeyBlob.bModulusLength, dwExponent); } void CV1ContainerRecord::Write(KeySpec ks, string const &rsBlob) const { CTransactionWrap wrap(m_rcard); // Calculate the CRC to verify when reading reading the // blob back. DWORD dwCrc = 0; if (rsBlob.length()) dwCrc = Crc32(rsBlob.data(), rsBlob.length()); Write(ks, reinterpret_cast(rsBlob.data()), static_cast(rsBlob.length()), dwCrc); } // Access string CV1ContainerRecord::CertName() { static string const sCertContainerName("CERT"); return sCertContainerName; } string CV1ContainerRecord::DefaultName() { static string const sDefaultName("USER"); return sDefaultName; } string CV1ContainerRecord::Name() const { return m_rcard.CardId(); } // Predicates bool CV1ContainerRecord::Exists() const { CTransactionWrap wrap(m_rcard); DWORD dwLen = 0; try { if (m_rcard.CardId() == m_sCntrType) return true; DWORD dwOffset = 0x00; dwLen = FindOffset(dwOffset); } catch (iop::CSmartCard::Exception &) { } return (sizeof ContainerBuffer <= dwLen); } bool CV1ContainerRecord::KeyExists(KeySpec ks) const { CTransactionWrap wrap(m_rcard); bool fExists = false; // // Does a key of this type exist in this container? // Note: assumes that m_KeyPath is set to correct container path? // // Open the container file DWORD dwFileSize = OpenContainer(); DWORD dwOffset = 0x00; DWORD const dwLen = FindOffset(dwOffset); // // Actually check the existence of key container // by seeing if we have a record of the right size // ContainerBuffer container; if (sizeof container <= dwLen) { GetContainer(dwOffset, container); // // Check which key exists by checking lengths // switch (ks) { case ksExchange: if (0x00 < container.XK_wLen) fExists = true; break; case ksSignature: if (0x00 < container.SK_wLen) fExists = true; break; } } return fExists; } // Static Variables /////////////////////////// PROTECTED ///////////////////////////////// // C'tors/D'tors // Operators // Operations // Access // Predicates // Static Variables /////////////////////////// PRIVATE ///////////////////////////////// // C'tors/D'tors // Operators // Operations void CV1ContainerRecord::Create() const { // Open the file and find the offset to the container DWORD dwFileSize = OpenContainer(); DWORD dwOffset = 0x00; DWORD dwLen = FindOffset(dwOffset); // Actually check the existence of key container if (sizeof ContainerBuffer <= dwLen) throw scu::OsException(NTE_EXISTS); // Set the new the container management data dwLen = SetContainer(dwOffset); // if there is still room put 2 null bytes of termination if ((dwOffset + dwLen + 2) <= dwFileSize) { const BYTE NullSize[] = { 0x00, 0x00 }; m_rcard.SmartCard().WriteBinary(dwOffset + dwLen, sizeof NullSize, NullSize); } } DWORD CV1ContainerRecord::FindOffset(DWORD &rdwOffset) const { DWORD dwFileSize = OpenContainer(); if ((rdwOffset + sizeof ContainerBuffer) > dwFileSize) return 0x00; bool fFound = false; DWORD dwLen = sizeof ContainerBuffer; // arbitrary value to start size_t const cBufferSize = sizeof WORD + (sizeof BYTE * ContainerBuffer::cMaxContainerNameLength) + 1; // +1 allows null terminator AutoArrayPtr aabBuffer(new BYTE[cBufferSize]); while (!fFound && (0x00 < dwLen) && ((rdwOffset + sizeof ContainerBuffer) <= dwFileSize)) { m_rcard.SmartCard().ReadBinary(rdwOffset, cBufferSize - 1, aabBuffer.Get()); WORD const *pwLen = reinterpret_cast(aabBuffer.Get()); dwLen = *pwLen; aabBuffer[cBufferSize - 1] = 0x00; // ensure null terminate string string sName(reinterpret_cast(&aabBuffer[sizeof WORD])); if ((m_sCntrType == sName) && (0x00 < dwLen)) fFound = true; else rdwOffset += dwLen; } if (fFound) return (dwLen & 0x00FFFF); else return 0x00; } void CV1ContainerRecord::GetContainer(DWORD dwOffset, ContainerBuffer &rcontainer) const { bool fClearContainer = true; try { DWORD dwFileSize = OpenContainer(); if ((dwOffset + sizeof rcontainer) <= dwFileSize) { m_rcard.SmartCard().ReadBinary(dwOffset, sizeof rcontainer, reinterpret_cast(&rcontainer)); fClearContainer = false; } } catch (...) { } if (fClearContainer) { rcontainer.Size = 0x00; rcontainer.Name[0] = '\0'; } } bool CV1ContainerRecord::GetContainerContent(KeySpec ks, string &rsBuffer, DWORD &rdwExponent) const { bool fExists = false; OpenContainer(); DWORD dwOffset = 0x00; if (0x00 != FindOffset(dwOffset)) { fExists = true; ContainerBuffer container; GetContainer(dwOffset, container); DWORD dwKeyLength = 0; AutoArrayPtr aabKey; if (ksExchange == ks) { if (0x00 < container.XK_wLen) { rdwExponent = container.XK_dwExp; dwKeyLength = container.XK_wLen; aabKey = AutoArrayPtr(new BYTE[container.XK_wLen]); m_rcard.SmartCard().ReadBinary(dwOffset + sizeof container, container.XK_wLen, aabKey.Get()); } } else { if (0x00 < container.SK_wLen) { rdwExponent = container.SK_dwExp; dwKeyLength = container.SK_wLen; aabKey = AutoArrayPtr(new BYTE[container.SK_wLen]); m_rcard.SmartCard().ReadBinary(dwOffset + sizeof container + container.XK_wLen, container.SK_wLen, aabKey.Get()); } } if (aabKey.Get()) rsBuffer.assign(reinterpret_cast(aabKey.Get()), dwKeyLength); } return fExists; } DWORD CV1ContainerRecord::OpenContainer() const { DWORD dwFileSize; string sPath(m_szKeyPath); sPath.append("/"); sPath.append(CV1Paths::RelativeContainers()); dwFileSize = m_rcard.OpenFile(sPath.c_str()); return dwFileSize; } DWORD CV1ContainerRecord::SetContainer(DWORD dwOffset) const { DWORD dwFileSize; dwFileSize = OpenContainer(); if ((dwOffset + sizeof ContainerBuffer) > dwFileSize) throw Exception(ccOutOfSymbolTableSpace); // Create the container buffer ContainerBuffer container; ZeroMemory(&container, sizeof container); container.Size = sizeof container; // Security: Protect from buffer overrun if (m_sCntrType.length() > (sizeof container.Name / sizeof *container.Name)) throw cci::Exception(ccBadLength); CopyMemory(container.Name, m_sCntrType.data(), m_sCntrType.length()); m_rcard.SmartCard().WriteBinary(dwOffset, sizeof container, reinterpret_cast(&container)); return container.Size; } void CV1ContainerRecord::Write(KeySpec ks, BYTE const *pbModulus, WORD wModulusLength, DWORD dwExponent) const { // Open container, get the data DWORD dwFileSize = OpenContainer(); DWORD dwOffset = 0x00; DWORD dwLen = FindOffset(dwOffset); ContainerBuffer container; GetContainer(dwOffset, container); // Check which key exists AutoArrayPtr aabXKey(new BYTE[container.XK_wLen]); if (0x00 < container.XK_wLen) m_rcard.SmartCard().ReadBinary(dwOffset + sizeof container, container.XK_wLen, aabXKey.Get()); AutoArrayPtr aabSKey(new BYTE[container.SK_wLen]); if (0x00 < container.SK_wLen) m_rcard.SmartCard().ReadBinary(dwOffset + sizeof container + container.XK_wLen, container.SK_wLen, aabSKey.Get()); // Give an arbitrary value if key spec not specified if ((ksSignature != ks) && (ksExchange != ks)) { if (0x00 == container.XK_wLen) ks = ksExchange; else { if (0x00 == container.SK_wLen) ks = ksSignature; else throw Exception(ccBadKeySpec); } } // Is it the last container of Container file? ContainerBuffer NextContainer; GetContainer(dwOffset + dwLen, NextContainer); bool fDeleted = false; if (sizeof NextContainer <= NextContainer.Size) { // Delete the existing container Delete(); fDeleted = true; // No need to recreate it now } // Now the container is at the end of the Container file // Find the "NEW" offset of the container which may not exist anymore dwOffset = 0x00; FindOffset(dwOffset); // keep the INITIAL dwLen of the container // Check that there is enough room to put the new key bool fEnoughMemory = false; switch (ks) { case ksExchange: if ((dwOffset + dwLen - container.XK_wLen + wModulusLength) <= dwFileSize) { aabXKey = AutoArrayPtr(new BYTE[wModulusLength]); CopyMemory(aabXKey.Get(), pbModulus, wModulusLength); container.XK_dwExp = dwExponent; container.XK_wLen = wModulusLength; fEnoughMemory = true; } break; case ksSignature: if ((dwOffset + dwLen - container.SK_wLen + wModulusLength) <= dwFileSize) { aabSKey = AutoArrayPtr(new BYTE[wModulusLength]); CopyMemory(aabSKey.Get(), pbModulus, wModulusLength); container.SK_dwExp = dwExponent; container.SK_wLen = wModulusLength; fEnoughMemory = true; } break; } // Recreate the container buffer accounting for "card tearing" // where the card could be pulled during the write operation. // This is done using a type of transact and commit phases. // The container size is initially set to zero, then the container // contents are written (transaction), followed by resetting the // container size to the actual length of the container to // "commit" the changes to the card. container.Size = 0; DWORD const dwTrueSize = sizeof container + container.XK_wLen + container.SK_wLen; size_t cBufferSize = dwTrueSize; BYTE const abNull[] = {0x00,0x00}; bool fAppendNull = (dwTrueSize + sizeof abNull) <= dwFileSize; if (fAppendNull) cBufferSize += sizeof abNull; AutoArrayPtr aabBuffer(new BYTE[cBufferSize]); BYTE *pbBuffer = aabBuffer.Get(); CopyMemory(pbBuffer, &container, sizeof container); pbBuffer += sizeof container; CopyMemory(pbBuffer, aabXKey.Get(), container.XK_wLen); pbBuffer += container.XK_wLen; CopyMemory(pbBuffer, aabSKey.Get(), container.SK_wLen); pbBuffer += container.SK_wLen; if (fAppendNull) { CopyMemory(pbBuffer, abNull, sizeof abNull); pbBuffer += sizeof abNull; } // Rewrite the container even if there is not enough to write the // NEW public key, then there should be enough room to write the // existing key. m_rcard.SmartCard().WriteBinary(dwOffset, pbBuffer - aabBuffer.Get(), aabBuffer.Get()); // Now commit these changes with the actual size. container.Size = dwTrueSize; m_rcard.SmartCard().WriteBinary(dwOffset, sizeof container.Size, reinterpret_cast(&container)); if (!fEnoughMemory) throw Exception(ccOutOfSymbolTableSpace); } // Access // Predicates bool CV1ContainerRecord::IsDefault() const { return (DefaultName() == m_sCntrType); } // Static Variables