[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [KSecretService] acd56af: Decided to put the master key verification
From:       Michael Leupold <lemma () confuego ! org>
Date:       2010-11-09 19:14:30
Message-ID: 20101109191430.2686EA60F0 () git ! kde ! org
[Download RAW message or body]

commit acd56af358474680e84522328edd30e2455a149f
branch master
Author: Michael Leupold <lemma@confuego.org>
Date:   Mon Sep 27 18:54:20 2010 +0000

    Decided to put the master key verification code in a separate place after all. \
More thinking in the first place would have helped avoiding unneeded commits *gah*. \
On to finishing the ksecret backend unit-test.  
    svn path=/trunk/playground/base/ksecretservice/; revision=1180348

diff --git a/backend/ksecret/FORMAT b/backend/ksecret/FORMAT
index 76eb902..086e85c 100644
--- a/backend/ksecret/FORMAT
+++ b/backend/ksecret/FORMAT
@@ -41,7 +41,7 @@ When adding new features that make it impossible for an older \
version of ksecret  to interpret the file's contents, version-major has to be \
increased. Like this  forward- and backward-compatibility among the same major \
version can be sustained.  
-header             = magic version algorithms part-table
+header             = magic version algorithms verifier part-table
 
 magic              = "KSECRET\n\r\0\r\n"              ;; Magic to identify the file \
format  
@@ -56,7 +56,7 @@ algorithms         = algo-hash algo-encrypt
    algo-hash       = UINT                             ;; Identifier for the hash \
algorithm  
    algo-encrypt    = UINT                             ;; Identifer for the sym. \
                encryption algorithm
-
+   
 part-table         = num-parts *part-desc
 
    num-parts       = UINT                             ;; number of data parts in \
this file @@ -70,6 +70,23 @@ part-desc          = part-type part-pos part-length   ;; \
describes a part of the  part-length     = UINT                             ;; \
describes the part's length  
 
+verifier
+========
+
+Once the master key (see "Symmetric Keys") has been derived, it's neccessary to \
check whether +it's correct. Thus the header contains a chunk for verification. It \
consists of a block of +encrypted random data as well as a hash of the random data. \
Upon deriving the master key, +the random data and its hash are checked to verify \
that the key is indeed correct. +
+HASH{} is the result of the hash function stored as a BYTEARRAY. It's used to verify \
that +decrypting the data was successful. See "Encrypted Parts" for an explanation of \
ENCRYPT{}. +
+verifier           = init-vector ENCRYPT{ random-data HASH{ random-data } }
+
+   random-data     = BYTEARRAY                        ;; random data used for \
verifying the master +                                                         key
+
+
 parts
 =====
 
@@ -149,10 +166,9 @@ contains a hash of the decrypted data to validate with. The \
algorithm used to cr  algo-hash.
 
 Contrary to the other representation, ENCRYPT{} is meant to be the result of the \
                encryption
-function stored as a BYTEARRAY. HASH{} is the result of the hash function stored as \
                a BYTEARRAY.
-It's used to verify that decrypting the data was successful.
+function stored as a BYTEARRAY.
 
-encrypted-part     = init-vector ENCRYPT{ part-to-encrypt HASH{ part-to-encrypt } }
+encrypted-part     = init-vector ENCRYPT{ part-to-encrypt }
 
    init-vector     = BYTEARRAY                        ;; initialization-vector used \
for encryption  
diff --git a/backend/ksecret/ksecretcollection.cpp \
b/backend/ksecret/ksecretcollection.cpp index f1e6b95..e90b3f3 100644
--- a/backend/ksecret/ksecretcollection.cpp
+++ b/backend/ksecret/ksecretcollection.cpp
@@ -33,6 +33,9 @@
 #include <klocalizedstring.h>
 #include <ksavefile.h>
 
+// this must not be changed or else file compatibility is gone!
+#define VERIFIER_LENGTH 64
+
 static const QString genericLoadingErrorMessage()
 {
    return i18nc("Error message: Generic error loading the ksecret file",
@@ -366,6 +369,28 @@ void KSecretCollection::setSymKey(QCA::SymmetricKey *key)
 
 bool KSecretCollection::tryUnlock()
 {
+   // before actually trying to unlock, check if the key matches by decrypting
+   // the verifier.
+   m_cipher->setup(QCA::Decode, *m_symmetricKey, m_verInitVector);
+   QCA::SecureArray verifier = m_cipher->update(m_verEncryptedRandom);
+   verifier.append(m_cipher->final());
+   QCA::SecureArray randomData(VERIFIER_LENGTH);
+   QCA::SecureArray verifierHash(verifier.size() - VERIFIER_LENGTH);
+   int i = 0;
+   for ( ; i < VERIFIER_LENGTH; ++i) {
+      randomData[i] = verifier[i];
+   }
+   for ( ; i < verifier.size(); ++i) {
+      verifierHash[i - VERIFIER_LENGTH] = verifier[i];
+   }
+   // rebuild the hash and compare to the decrypted one
+   m_hash->clear();
+   m_hash->update(randomData);
+   if (verifierHash != m_hash->final()) {
+      // hashes don't match, the master key is wrong.
+      return false;
+   }
+   
    QCA::SecureArray decryptedPart;
    Q_FOREACH(const QByteArray &partContents, m_encryptedItemParts) {
       if (!deserializePartEncrypted(partContents, decryptedPart)) {
@@ -486,6 +511,13 @@ bool KSecretCollection::deserializeHeader(KSecretFile &file, \
QString &errorMessa  return false;
    }
    if (!setupAlgorithms(errorMessage)) {
+      // TODO: error message
+      return false;
+   }
+   
+   // verifier
+   if (!file.readBytearray(&m_verInitVector) || \
!file.readBytearray(&m_verEncryptedRandom)) { +      errorMessage = \
genericLoadingErrorMessage();  return false;
    }
 
@@ -577,9 +609,23 @@ bool KSecretCollection::deserializePartCollProps(const \
QByteArray &partContents)  QBuffer buffer;
    buffer.setData(partContents);
    KSecretFile file(&buffer, KSecretFile::Read);
+   if (!file.isValid()) {
+      return false;
+   }
    
-   if (!file.readString(&m_id) || !file.readString(&m_label) ||
-       !file.readDatetime(&m_created) || !file.readDatetime(&m_modified)) {
+   QByteArray partProps;
+   if (!file.readBytearray(&partProps) || !file.readBytearray(&m_propertiesMac)) {
+      return false;
+   }
+   
+   QBuffer propsBuffer(&partProps);
+   KSecretFile propsFile(&propsBuffer, KSecretFile::Read);
+   if (!propsFile.isValid()) {
+      return false;
+   }
+   
+   if (!propsFile.readString(&m_id) || !propsFile.readString(&m_label) ||
+       !propsFile.readDatetime(&m_created) || !propsFile.readDatetime(&m_modified)) \
{  return false;
    }
    
@@ -852,12 +898,6 @@ bool KSecretCollection::serialize(QString &errorMessage) const
       return false;
    }
    
-   // collection properties
-   if (!file.writeString(m_id) || !file.writeString(m_label) ||
-       !file.writeDatetime(m_created) || !file.writeDatetime(m_modified)) {
-      return false;
-   }
-   
    if (!serializeParts(file)) {
       errorMessage = genericSavingErrorMessage();
       return false;
@@ -877,6 +917,24 @@ bool KSecretCollection::serializeHeader(KSecretFile &file) const
    if (!file.writeUint(m_algoHash) && !file.writeUint(m_algoCipher)) {
       return false;
    }
+   
+   // verifier
+   QCA::InitializationVector iv(m_cipher->blockSize());
+   if (!file.writeSecret(iv)) {
+      return false;
+   }
+   // get random data and compute its hash
+   QCA::SecureArray randomData = QCA::Random::randomArray(VERIFIER_LENGTH);
+   m_hash->clear();
+   m_hash->update(randomData);
+   randomData.append(m_hash->final());
+   // now encrypt randomData
+   m_cipher->setup(QCA::Encode, *m_symmetricKey, iv);
+   QCA::SecureArray verifier = m_cipher->update(randomData);
+   verifier.append(m_cipher->final());
+   if (!file.writeSecret(verifier)) {
+      return false;
+   }
 
    return true;
 }
@@ -887,10 +945,10 @@ bool KSecretCollection::serializeParts(KSecretFile &file) const
    // TODO: only save if the collection is open.
    
    // determine the number of parts
-   // item hash part + items part + acls part + config part + number of keys +
-   // number of unknown parts = 4 + number of keys + number of unknown parts
+   // properties part + item hash part + items part + acls part + config part + \
number of keys + +   // number of unknown parts = 5 + number of keys + number of \
unknown parts  int curFilePartEntry = 0;
-   quint32 numParts = 4 + m_encryptedSymKeys.size() + m_unknownParts.size();
+   quint32 numParts = 5 + m_encryptedSymKeys.size() + m_unknownParts.size();
    quint32 partTableSize = 4 + numParts * 12;
    QList<FilePartEntry> filePartEntries;
    for (quint32 i = 0; i < numParts; ++i) {
@@ -903,6 +961,11 @@ bool KSecretCollection::serializeParts(KSecretFile &file) const
       return false;
    }
    
+   // collection properties part
+   if (!serializePropertiesPart(file, filePartEntries[curFilePartEntry])) {
+      return false;
+   }
+   
    // config part
    if (!serializeConfigPart(file, filePartEntries[curFilePartEntry])) {
       return false;
@@ -1016,6 +1079,29 @@ bool KSecretCollection::serializeAclsPart(KSecretFile &file, \
FilePartEntry &entr  return true;
 }
 
+bool KSecretCollection::serializePropertiesPart(KSecretFile &file, FilePartEntry \
&entry) const +{
+   entry.m_type = KSecretFile::PartCollProps;
+   entry.m_position = (quint32)file.pos();
+   
+   // build temporary buffer containing properties
+   QBuffer propsBuffer;
+   KSecretFile tempFile(&propsBuffer, KSecretFile::Write);
+   
+   if (!tempFile.writeString(m_id) || !tempFile.writeString(m_label) ||
+       !tempFile.writeDatetime(m_created) || !tempFile.writeDatetime(m_modified)) {
+      return false;
+   }
+   
+   tempFile.close();
+   if (!serializeAuthenticated(propsBuffer.data(), file)) {
+      return false;
+   }
+   entry.m_length = (quint32)file.pos() - entry.m_position;
+   
+   return true;
+}
+
 bool KSecretCollection::serializeConfigPart(KSecretFile &file, FilePartEntry &entry) \
const  {
    entry.m_type = KSecretFile::PartConfig;
@@ -1053,6 +1139,7 @@ bool KSecretCollection::serializeConfigPart(KSecretFile &file, \
FilePartEntry &en  }
    }
    
+   tempFile.close();
    if (!serializeAuthenticated(configBuffer.data(), file)) {
       return false;
    }
diff --git a/backend/ksecret/ksecretcollection.h \
b/backend/ksecret/ksecretcollection.h index 5548b12..72175a4 100644
--- a/backend/ksecret/ksecretcollection.h
+++ b/backend/ksecret/ksecretcollection.h
@@ -413,6 +413,15 @@ private:
    bool serializeParts(KSecretFile &file) const;
    
    /**
+    * Serialize a collection's properties to a ksecret file.
+    * 
+    * @param file ksecret file to serialize the properties to
+    * @param entry file part descriptor to put part information to
+    * @return true if serialization was successful, false else
+    */
+   bool serializePropertiesPart(KSecretFile &file, FilePartEntry &entry) const;
+   
+   /**
     * Serialize a collection's configuration to a ksecret file.
     *
     * @param file ksecret file to serialize the configuration values to
@@ -469,9 +478,14 @@ private:
    QString m_label;
    QDateTime m_created;
    QDateTime m_modified;
+   QMap<QString, QByteArray> m_propUnknownKeys; // unknown collection properties
 
    QString m_path; // path of the ksecret file on disk
    
+   // verifier
+   QByteArray m_verInitVector; // initialization vector of the verifier
+   QByteArray m_verEncryptedRandom; // encrypted random data of the verifier
+   
    // configuration values
    bool m_cfgCloseScreensaver;     // close when the screensaver starts
    bool m_cfgCloseIfUnused;        // close when the last application stops using it
@@ -486,8 +500,10 @@ private:
 
    QCA::SymmetricKey *m_symmetricKey; // the symmetric key used for \
encryption/decryption  
-   // the configuration values message authentication code.
+   // the configuration values message authentication code
    QByteArray m_configValuesMac;
+   // the properties message authentication code
+   QByteArray m_propertiesMac;
 
    // the acls message authentication code
    QHash<QString, ApplicationPermission> m_acls;


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic