[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