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

List:       kde-commits
Subject:    [KSecretService] 6c02d6f: Add the new backend for so-called .ksecret
From:       Michael Leupold <lemma () confuego ! org>
Date:       2010-11-09 19:14:25
Message-ID: 20101109191425.8AAFBA60D7 () git ! kde ! org
[Download RAW message or body]


	A	 backend/ksecret/ksecretitem.h	 [License: GPL(v2)]


	A	 backend/ksecret/ksecretitem.cpp	 [License: GPL(v2)]


	A	 backend/ksecret/ksecretfile.h	 [License: GPL(v2)]


	A	 backend/ksecret/ksecretfile.cpp	 [License: GPL(v2)]


	A	 backend/ksecret/ksecretcollectionmanager.h	 [License: GPL(v2)]


	A	 backend/ksecret/ksecretcollectionmanager.cpp	 [License: GPL(v2)]


	A	 backend/ksecret/ksecretcollection.h	 [License: GPL(v2)]


	A	 backend/ksecret/ksecretcollection.cpp	 [License: GPL(v2)]


	A	 backend/ksecret/FORMAT	 [License: UNKNOWN]

commit 6c02d6fde7a22b322599696076378dc9305dda42
Author: Michael Leupold <lemma@confuego.org>
Date:   Sun Jul 11 14:57:55 2010 +0000

    Add the new backend for so-called .ksecret files. Untested, not very pretty, but \
it compiles. Need unit-tests soon.  
    svn path=/trunk/playground/base/ksecretservice/; revision=1148680

diff --git a/CMakeLists.txt b/CMakeLists.txt
index c140252..e09c3c4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,6 +25,10 @@ SET (ksecretservice_backend_SRCS
    backend/temporarycollectionmanager.cpp
    backend/temporaryitem.cpp
    backend/securebuffer.cpp
+   backend/ksecret/ksecretfile.cpp
+   backend/ksecret/ksecretcollectionmanager.cpp
+   backend/ksecret/ksecretcollection.cpp
+   backend/ksecret/ksecretitem.cpp
 )
 
 KDE4_ADD_LIBRARY (ksecretservicebackend STATIC ${ksecretservice_backend_SRCS})
diff --git a/backend/ksecret/FORMAT b/backend/ksecret/FORMAT
new file mode 100644
index 0000000..0dbddce
--- /dev/null
+++ b/backend/ksecret/FORMAT
@@ -0,0 +1,252 @@
+This document describes the ksecret format for storing ksecretservice items and \
collection. +
+The ksecret format is a binary format. TODO
+
+
+Basic types
+===========
+
+BYTE
+   an 8-bit byte
+
+UINT
+   a 32-bit unsigned integer stored in big endian format
+
+DATETIME
+   a 64-bit unsigned integer stored in big endian format
+   
+BYTEARRAY = UINT *BYTE
+   a byte-array is stored as its length followed by each of the bytes
+
+STRING    = BYTEARRAY
+   a string is stored as a bytearray using UTF-8 encoding
+
+
+ksecret file
+============
+
+ksecret          = header *part
+
+
+file header
+===========
+
+The file header contains some magic to identify this file as a ksecret file, the
+version number, the algorithms used and a lookup table for the other parts of the
+file.
+The parts are basically containers which can be interpreted on their own. If a
+program reading a ksecret file can't handle a certain part, it should ignore it
+and one saving rewrite it as it was.
+When adding new features that make it impossible for an older version of \
ksecretservice +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 coll-props part-table
+
+magic              = "KSECRET\n\r\0\r\n"              ;; Magic to identify the file \
format +
+version            = version-major version-minor
+
+   version-major   = UINT                             ;; Major version of the file \
format +
+   version-minor   = UINT                             ;; Minor version of the file \
format +
+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 +
+part-desc          = part-type part-pos part-length   ;; describes a part of the \
file +
+   part-type       = UINT                             ;; describes the type of the \
part +
+   part-pos        = UINT                             ;; describes the part's file \
position +
+   part-length     = UINT                             ;; describes the part's length
+
+
+parts
+=====
+
+Parts come in various flavours, including a part for hashes for item attribute \
lookup, +a part containing the encrypted, symmetric keys as well as parts for the \
actual encrypted +data. Parts can be identified using the part-table. Each part's \
semantics is defined by +its type.
+
+
+part               = part-item-hashes / part-symkey / encrypted-part / mac-part
+
+
+Item hashes
+===========
+
+Each item's attributes are hashed using algo-hash and stored so the collection can \
be +searched even without being decrypted. An attribute hash (hash-attrib) is derived \
by +concatenating the property key with the property value and creating this string's \
hash using +algo-hash.
+
+
+part-item-hashes   = num-items *item-hash
+
+   num-items       = UINT                             ;; number of items inside this \
part +
+item-hash          = item-id num-attribs *hash-attrib
+
+   item-id         = STRING                           ;; unique item identifier
+
+   num-attribs     = UINT                             ;; number of attributes this \
item hash +
+   hash-attrib     = BYTEARRAY                        ;; attribute hash
+
+
+Symmetric Keys
+==============
+
+All of the encrypted parts are encrypted using a symmetric key (master key). However \
contrary +to kwallet this key is not derived directly from a passphrase. Instead the \
master key +is created from random data and encrypted itself using different methods, \
eg. +using symmetric or assymmetric encryption (hash from passphrase, smartcard, \
possibly +fingerprint). As several methods to encrypt the master key exist, it could \
get stored inside +a ksecret file several times. The application is responsible for \
making sure only valid +encrypted keys are contained withing the ksecret file.
+
+enc-symkey should also contain a method to verify if the master key was derived \
successfully, +eg. a hash of the key.
+
+// TODO: add a method to figure out if decrypting a key worked (ie. add
+//       a hash of the key after enc-symkey.
+
+part-symkey        = key-type enc-symkey
+
+   key-type        = UINT                             ;; method for encrypting the \
key +
+   enc-symkey      = BYTEARRAY                        ;; the encrypted symmetric \
master key +
+
+Encrypted parts
+===============
+
+part-items is an encrypted part. The structure described below applies to the \
structure of +the data AFTER it has been decrypted using the key with the encryption \
algorithm (algo-encrypt). +To verify if the master key used for decryption was the \
right one, each encrypted part also +contains a hash of the decrypted data to \
validate with. The algorithm used to create the hash is +algo-hash.
+
+Contrary to the other representation, ENCRYPT{} is meant to be the result of the \
encryption +function stored as a BYTEARRAY.
+
+encrypted-part     = init-vector ENCRYPT{ part-to-encrypt }
+
+   init-vector     = BYTEARRAY                        ;; initialization-vector used \
for encryption +
+   part-to-encrypt = part-items
+
+
+Authenticated parts
+===================
+
+Configuration values and ACLs get signed (authenticated) by creating a message \
authentication code +using the master key. Like this any tampering can be detected \
once the master-key is decrypted. +
+Contrary to the other representation, MAC{} is meant to be the message \
authentication code +generated from the data enclosed using the master-key which is \
stored as a BYTEARRAY. +
+mac-part           = auth-size part-to-auth MAC{ part-to-auth }
+
+   auth-size       = UINT                             ;; length of the data the mac \
is created for +
+   part-to-auth    = part-config / part-acls / part-collprops
+
+
+Collection properties
+=====================
+
+Collection properties are stored in a signed part so tampering with them
+can be detected.
+
+part-collprops     = coll-id coll-label coll-created coll-modified
+
+   coll-id         = STRING                           ;; unique collection \
identifier +
+   coll-label      = STRING                           ;; The name of this collection
+
+   coll-created    = DATETIME                         ;; The creation time of this \
collection +
+   coll-modified   = DATETIME                         ;; The time this collection \
was last modified +
+
+Configuration
+=============
+
+Collection-specific configuration values are stored directly inside the ksecret \
file. Like +this security-related configuration changes can be protected from being \
changed without +authentication.
+
+
+part-config        = num-cfg-values *config-item
+
+   num-cfg-values  = UINT                             ;; number of config-values \
stored +
+   config-item     = config-key config-value
+
+   config-key      = STRING                           ;; the key of a configuration \
item +
+   config-value    = BYTEARRAY                        ;; the value of a \
configuration item +
+
+ACLs
+====
+
+acls               = num-acls *acl-item               ;; acl part
+
+   num-acls        = UINT                             ;; number of entries in the \
acl +
+   acl-item        = acl-path acl-value
+
+   acl-path        = STRING                           ;; path of the application
+
+   acl-value       = UINT                             ;; access of the application
+
+
+Items
+=====
+
+part-items         = num-items *item
+
+item               = item-id item-size item-label item-created item-modified \
attributes secret +
+   item-size       = UINT                             ;; byte length of the \
remainder of the item +
+   item-label      = STRING                           ;; human-readable label of \
this item +
+   item-created    = DATETIME                         ;; time the item was created
+
+   item-modified   = DATETIME                         ;; time the item was last \
modified +
+   secret          = STRING                           ;; the actual secret
+
+attributes         = num-attribs *attrib
+
+attrib             = attrib-key attrib-value
+
+   attrib-key      = STRING                           ;; attribute key
+
+   attrib-value    = STRING                           ;; attribute value
+
+
+ACLs
+====
+
+ACLs are stored verbatim and authenticated so they can only be modified while the \
configuration is +unlocked.
+
+TODO
+
+
+Michael Leupold <lemma@confuego.org>
diff --git a/backend/ksecret/ksecretcollection.cpp \
b/backend/ksecret/ksecretcollection.cpp new file mode 100644
index 0000000..671a7b7
--- /dev/null
+++ b/backend/ksecret/ksecretcollection.cpp
@@ -0,0 +1,1168 @@
+/*
+ * Copyright 2010, Michael Leupold <lemma@confuego.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License or (at your option) version 3 or any later version
+ * accepted by the membership of KDE e.V. (or its successor approved
+ * by the membership of KDE e.V.), which shall act as a proxy
+ * defined in Section 14 of version 3 of the license.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ksecretcollection.h"
+#include "ksecretcollectionmanager.h"
+#include "ksecretfile.h"
+#include "../securebuffer.h"
+#include "secrettool.h"
+
+#include <QtCore/QBuffer>
+#include <QtCore/QFile>
+#include <QtCore/QSet>
+
+#include <klocalizedstring.h>
+#include <ksavefile.h>
+
+static const QString genericLoadingErrorMessage()
+{
+   return i18nc("Error message: Generic error loading the ksecret file",
+                "There was an error reading the ksecret file.");
+}
+
+static const QString genericSavingErrorMessage()
+{
+   return i18nc("Error message: Generic error saving the ksecret file",
+                "There was an error writing the ksecret file.");
+}
+
+KSecretCollection *KSecretCollection::create(const QString &id, \
BackendCollectionManager *parent, +                                             \
QString &errorMessage) +{
+   KSecretCollection *coll = new KSecretCollection(parent);
+   coll->m_id = id;
+   coll->m_algoHash = KSecretFile::SHA256;
+   coll->m_algoCipher = KSecretFile::AES256;
+
+   // initialize default encryption algorithms
+   if (!coll->setupAlgorithms(errorMessage)) {
+      delete coll;
+      return 0;
+   }
+
+   // create a new symmetric key
+   // TODO: is minimum() right in all cases?
+   coll->m_symmetricKey = new \
QCA::SymmetricKey(coll->m_cipher->keyLength().minimum()); +   
+   // FIXME: this is bogus. Actually the unlocking method needs to be chosen.
+   //        here we just use a bogus key to encrypt the symmetric key using
+   //        blowfish.
+   QCA::SymmetricKey *keyUnlockKey = new \
QCA::SymmetricKey(QByteArray("12345678901234567890")); +   EncryptedKey *key = new \
EncryptedKey; +   key->m_type = KSecretFile::KeyBogus;
+   QCA::Cipher keyCipher("blowfish", QCA::Cipher::CBC);
+   keyCipher.setup(QCA::Encode, *keyUnlockKey);
+   key->m_key.append(keyCipher.update(*coll->m_symmetricKey).toByteArray());
+   key->m_key.append(keyCipher.final().toByteArray());
+   coll->m_encryptedSymKeys.append(key);
+}
+
+KSecretCollection::KSecretCollection(BackendCollectionManager *parent)
+ : BackendCollection(parent), m_hash(0), m_cipher(0),
+   m_symmetricKey(0)
+{
+}
+
+KSecretCollection::~KSecretCollection()
+{
+   // TODO: delete items?
+   delete m_hash;
+   delete m_cipher;
+   delete m_mac;
+   delete m_symmetricKey;
+   qDeleteAll(m_unknownParts);
+   qDeleteAll(m_encryptedSymKeys);
+   // TODO: make sure there's nothing else to delete
+}
+
+QString KSecretCollection::id() const
+{
+   return m_id;
+}
+
+BackendReturn<QString> KSecretCollection::label() const
+{
+   return m_label;
+}
+
+BackendReturn<void> KSecretCollection::setLabel(const QString &label)
+{
+   // label can only be set if the collection is unlocked
+   if (isLocked()) {
+      return ErrorIsLocked;
+   }
+   
+   m_label = label;
+   return NoError;
+}
+
+QDateTime KSecretCollection::created() const
+{
+   return m_created;
+}
+
+QDateTime KSecretCollection::modified() const
+{
+   return m_modified;
+}
+
+bool KSecretCollection::isLocked() const
+{
+   // a collection is unlocked if m_cipher is initialized
+   return (m_symmetricKey != 0);
+}
+
+BackendReturn<QList<BackendItem*> > KSecretCollection::items() const
+{
+   QList<BackendItem*> itemList;
+   QHash<QString, KSecretItem*>::const_iterator it = m_items.constBegin();
+   QHash<QString, KSecretItem*>::const_iterator end = m_items.constEnd();
+   for ( ; it != end; ++it) {
+      itemList.append(it.value());
+   }
+   
+   return itemList;
+}
+
+bool KSecretCollection::isCallImmediate(AsyncCall::AsyncType type) const
+{
+   switch (type) {
+      
+   case AsyncCall::AsyncUnlock:
+      // unlock is immediate if the collection is already unlocked
+      return !isLocked();
+      
+   case AsyncCall::AsyncChangeAuthentication:
+      // changing the authentication is never immediate
+      return false;
+      
+   default:
+      // all other calls are immediate
+      return true;
+   }
+}
+
+BackendReturn<QList<BackendItem*> > KSecretCollection::searchItems(
+   const QMap<QString, QString> &attributes) const
+{
+   QList<BackendItem*> itemList;
+   // create hashes for each of the attributes queried
+   QSet<QByteArray> attributeHashes = KSecretItem::createHashes(attributes, m_hash);
+   if (attributeHashes.isEmpty()) {
+      return items();
+   } else {
+      // use the first attribute hash to build an initial list of items matching
+      QHash<QByteArray, KSecretItem*>::const_iterator resit =
+         m_itemHashes.constFind(*attributeHashes.constBegin());
+      QHash<QByteArray, KSecretItem*>::const_iterator resend = \
m_itemHashes.constEnd(); +      // now check which of the items in resit match the \
remainder of the attributes +      // to do this the actual attributes are used \
instead of the hashes +      for ( ; resit != resend; ++resit) {
+         if (resit.value()->matches(attributes)) {
+            itemList.append(resit.value());
+         }
+      }
+      return itemList;
+   }
+}
+
+BackendReturn<bool> KSecretCollection::unlock()
+{
+   if (!isLocked()) {
+      return true;
+   } else {
+      // TODO: make it actually unlock, needs UI
+      
+      // TODO: check authenticated parts
+      
+      // TODO: check if the key is actually correct
+      
+      // FIXME: this is bogus
+      
+      // create a key to unlock the actual symmetric key
+      m_symmetricKey = new QCA::SymmetricKey;
+      QCA::SymmetricKey *keyUnlockKey = new \
QCA::SymmetricKey(QByteArray("12345678901234567890")); +      Q_FOREACH(EncryptedKey \
*key, m_encryptedSymKeys) { +         if (key->m_type == KSecretFile::KeyBogus) {
+            QCA::Cipher keyCipher("blowfish", QCA::Cipher::CBC);
+            keyCipher.setup(QCA::Decode, *keyUnlockKey);
+            *m_symmetricKey += QCA::SecureArray(keyCipher.update(key->m_key));
+            *m_symmetricKey += QCA::SecureArray(keyCipher.final());
+            break;
+         }
+      }
+      
+      Q_ASSERT(m_symmetricKey);
+      
+      QCA::SecureArray decryptedPart;
+      Q_FOREACH(const QByteArray &partContents, m_encryptedItemParts) {
+         if (!deserializePartEncrypted(partContents, decryptedPart)) {
+            return false;
+         }
+         
+         // deserialize the items inside the decrypted part
+         SecureBuffer device(&decryptedPart);
+         KSecretFile itemFile(&device, KSecretFile::Read);
+         if (!deserializeItemsUnlocked(itemFile)) {
+            return false;
+         }
+      }
+      return false;
+   }
+}
+
+BackendReturn<bool> KSecretCollection::lock()
+{
+   // TODO: reference/open counting?
+   
+   // TODO: emit signals?
+   
+   if (isLocked()) {
+      return true;
+   } else {
+      // TODO: only serialize if dirty
+      QString errorMessage;
+      if (!serialize(errorMessage)) {
+         return BackendReturn<bool>(false, ErrorOther, errorMessage);
+      }
+      
+      // then remove the key
+      delete m_hash;
+      m_hash = 0;
+      delete m_cipher;
+      m_cipher = 0;
+      
+      // remove individual item secrets
+      QHash<QString, KSecretItem*>::iterator it = m_items.begin();
+      QHash<QString, KSecretItem*>::iterator end = m_items.end();
+      for ( ; it != end; ++it) {
+         // FIXME: it.value()->removeSecrets();
+      }
+      
+      return true;
+   }
+}
+
+BackendReturn<bool> KSecretCollection::deleteCollection()
+{
+   // remove the ksecret file
+   if (!QFile::remove(m_path)) {
+      return false;
+   }
+   
+   // emit signals and actually delete
+   emit collectionDeleted(this);
+   deleteLater();
+   return true;
+}
+
+BackendReturn<BackendItem*> KSecretCollection::createItem(const QString &label,
+                                                          const QMap<QString, \
QString> &attributes, +                                                          \
const QCA::SecureArray &secret, +                                                     \
bool replace, bool locked) +{
+   // TODO: use locked argument
+   Q_UNUSED(locked);
+   
+   // only works if unlocked
+   if (isLocked()) {
+      return BackendReturn<BackendItem*>(0, ErrorIsLocked);
+   } else {
+      KSecretItem *item = 0;
+      bool replacing = false;
+      
+      // check for duplicates
+      BackendReturn<QList<BackendItem*> > foundItems = searchItems(attributes);
+      if (!foundItems.isError() && foundItems.value().size() > 0) {
+         QList<BackendItem*> oldlist = foundItems.value();
+         Q_FOREACH(BackendItem *olditem, oldlist) {
+            if (olditem->attributes().value() == attributes) {
+               if (replace) {
+                  // replace an existing item
+                  item = qobject_cast<KSecretItem*>(olditem);
+                  replacing = true;
+               } else {
+                  // item existing but should not be replaced
+                  return BackendReturn<BackendItem*>(0, ErrorAlreadyExists);
+               }
+               break;
+            }
+         }
+      }
+      
+      if (!item) {
+         item = new KSecretItem(createId(), this);
+         connect(item, SIGNAL(attributesChanged(KSecretItem*)),
+                       SLOT(changeAttributeHashes(KSecretItem*)));
+      }
+      item->m_label = label;
+      item->m_attributes = attributes;
+      item->m_secret = secret;
+      
+      // insert new item's hashes
+      changeAttributeHashes(item);
+      
+      if (replacing) {
+         emit itemChanged(item);
+      } else {
+         m_items.insert(item->id(), item);
+         // new item, signals need to be wired
+         connect(item, SIGNAL(itemDeleted(BackendItem*)), \
SLOT(slotItemDeleted(BackendItem*))); +         connect(item, \
SIGNAL(itemChanged(BackendItem*)), SIGNAL(itemChanged(BackendItem*))); +         emit \
itemCreated(item); +      }
+      
+      return item;
+   }
+}
+
+BackendReturn<bool> KSecretCollection::changeAuthentication()
+{
+   // TODO: needs ui
+   return false;
+}
+
+const QString &KSecretCollection::path() const
+{
+   return m_path;
+}
+
+void KSecretCollection::slotItemDeleted(BackendItem *item)
+{
+   KSecretItem *kitem = qobject_cast<KSecretItem*>(item);
+   Q_ASSERT(kitem);
+   
+   // remove the item as well as item hashes
+   if (m_reverseItemHashes.contains(kitem)) {
+      Q_FOREACH(const QByteArray &hash, m_reverseItemHashes.value(kitem)) {
+         m_itemHashes.remove(hash);
+      }
+      m_reverseItemHashes.remove(kitem);
+   }
+   m_items.remove(kitem->id());
+   
+   emit itemDeleted(item);
+}
+
+void KSecretCollection::changeAttributeHashes(KSecretItem *item)
+{
+   Q_ASSERT(item);
+   
+   // remove previous item hashes
+   if (m_reverseItemHashes.contains(item)) {
+      QSet<QByteArray> oldHashes = m_reverseItemHashes.value(item);
+      Q_FOREACH(const QByteArray &hash, oldHashes) {
+         m_itemHashes.remove(hash, item);
+      }
+   }
+   
+   // insert new hashes
+   QSet<QByteArray> attributeHashes = item->createAttributeHashes(m_hash);
+   Q_FOREACH(const QByteArray &hash, attributeHashes) {
+      m_itemHashes.insert(hash, item);
+   }
+   m_reverseItemHashes.insert(item, attributeHashes);
+}
+
+KSecretCollection *KSecretCollection::deserialize(const QString &path,
+                                                  KSecretCollectionManager *parent,
+                                                  QString &errorMessage)
+{
+   QFile device(path);
+   KSecretFile file(&device, KSecretFile::Read);
+   if (!file.isValid()) {
+      errorMessage = i18nc("Error message: collection file to be opened doesn't \
exist", +                           "Collection does not exist.");
+      return 0;
+   }
+
+   KSecretCollection *coll = new KSecretCollection(parent);
+   coll->m_path = path;
+
+   if (!coll->deserializeHeader(file, errorMessage) ||
+       !coll->deserializeParts(file, errorMessage)) {
+      delete coll;
+      return 0;
+   }
+
+   return coll;
+}
+
+bool KSecretCollection::setupAlgorithms(QString &errorMessage)
+{
+   // figure out algorithms to use
+   switch (m_algoHash) {
+
+   case KSecretFile::SHA256:
+      if (!QCA::isSupported("sha256") || !QCA::isSupported("hmac(sha256))")) {
+         errorMessage = i18nc("Error message: unsupported hashing algorithm SHA256 \
used", +                              "The hashing algorithm SHA256 is not supported \
by your installation."); +         return false;
+      }
+      m_hash = new QCA::Hash("sha256");
+      m_mac = new QCA::MessageAuthenticationCode("hmac(sha256))", \
QCA::SymmetricKey()); +      break;
+
+   default:
+      errorMessage = i18nc("Error message: unknown hashing algorithm used",
+                           "The file uses an unknown hashing algorithm.");
+      return false;
+   }
+
+   switch (m_algoCipher) {
+
+   case KSecretFile::AES256:
+      if (!QCA::isSupported("aes256")) {
+         errorMessage = i18nc("Error message: unsupported encryption algorithm \
AES256 used", +                              "The encryption algorithm AES256 is not \
suppored by your installation."); +         return false;
+      }
+      m_cipher = new QCA::Cipher("aes256", QCA::Cipher::CBC);
+      break;
+
+   default:
+      errorMessage = i18nc("Error message: unknown encryption algorithm used",
+                           "The file uses an unknown encryption algorithm.");
+      return false;
+   }
+   
+   return true;
+}
+
+bool KSecretCollection::deserializeHeader(KSecretFile &file, QString &errorMessage)
+{
+   // magic, version numbers
+   quint32 versionMajor;
+   quint32 versionMinor;
+   if (!file.readMagic() || !file.readUint(&versionMajor) || \
!file.readUint(&versionMinor)) { +      errorMessage = i18nc("Error message: \
collection has wrong file format or is corrupted", +                           \
"Collection is not a ksecret file or is corrupted."); +      return false;
+   }
+
+   // check if version number matches something we understand
+   if (versionMajor > VERSION_MAJOR) {
+      errorMessage = i18nc("Error message: collection's file format is too recent",
+                           "The file format used is too recent.");
+      return false;
+   }
+
+   // algorithms
+   if (!file.readUint(&m_algoHash) || !file.readUint(&m_algoCipher)) {
+      errorMessage = genericLoadingErrorMessage();
+      return false;
+   }
+   if (!setupAlgorithms(errorMessage)) {
+      return false;
+   }
+
+   return true;
+}
+
+bool KSecretCollection::deserializeParts(KSecretFile &file, QString &errorMessage)
+{
+   // parts table
+   quint32 numParts;
+   if (!file.readUint(&numParts)) {
+      errorMessage = genericLoadingErrorMessage();
+      return false;
+   }
+   QList<FilePartEntry> filePartEntries;
+   quint32 partType;
+   quint32 partPos;
+   quint32 partLength;
+   for (quint32 i = 0; i < numParts; ++i) {
+      if (!file.readUint(&partType) || !file.readUint(&partPos) || \
!file.readUint(&partLength)) { +         errorMessage = genericLoadingErrorMessage();
+         return false;
+      }
+
+      FilePartEntry part;
+      part.m_type = partType;
+      part.m_position = partPos;
+      part.m_length = partLength;
+      filePartEntries.append(part);
+   }
+
+   // read parts
+   Q_FOREACH(const FilePartEntry &filePartEntry, filePartEntries) {
+      // read the contents into a bytearray
+      QByteArray contents;
+      if (!file.readPart(&contents, filePartEntry.m_position, \
filePartEntry.m_length)) { +         errorMessage = genericLoadingErrorMessage();
+         return false;
+      }
+
+      bool rc;
+      switch (filePartEntry.m_type) {
+
+      case KSecretFile::PartItemHashes:
+         rc = deserializePartItemHashes(contents);
+         break;
+
+      case KSecretFile::PartSymKey:
+         rc = deserializePartSymKey(contents);
+         break;
+
+      case KSecretFile::PartItems:
+         m_encryptedItemParts.append(contents);
+         rc = true;
+         break;
+
+      case KSecretFile::PartAcls:
+         rc = deserializePartAcls(contents);
+         break;
+
+      case KSecretFile::PartConfig:
+         rc = deserializePartConfig(contents);
+         break;
+         
+      case KSecretFile::PartCollProps:
+         rc = deserializePartCollProps(contents);
+         break;
+
+      default: // unknown part type
+         UnknownFilePart *ufp = new UnknownFilePart;
+         ufp->m_type = filePartEntry.m_type;
+         ufp->m_contents = contents;
+         m_unknownParts.append(ufp);
+         rc = true;
+         break;
+      }
+
+      if (!rc) {
+         errorMessage = genericLoadingErrorMessage();
+         return false;
+      }
+   }
+
+   return true;
+}
+
+bool KSecretCollection::deserializePartCollProps(const QByteArray &partContents)
+{
+   QBuffer buffer;
+   buffer.setData(partContents);
+   KSecretFile file(&buffer, KSecretFile::Read);
+   
+   if (!file.readString(&m_id) || !file.readString(&m_label) ||
+       !file.readDatetime(&m_created) || !file.readDatetime(&m_modified)) {
+      return false;
+   }
+   
+   return true;
+}
+
+bool KSecretCollection::deserializePartItemHashes(const QByteArray &partContents)
+{
+   QBuffer buffer;
+   buffer.setData(partContents);
+   KSecretFile file(&buffer, KSecretFile::Read);
+   quint32 numItems;
+   if (!file.isValid() || !file.readUint(&numItems)) {
+      return false;
+   }
+
+   // read each item hash
+   KSecretItem *item;
+   for (quint32 i = 0; i < numItems; ++i) {
+      QString itemId;
+      quint32 numAttribs;
+      if (!file.readString(&itemId) || !file.readUint(&numAttribs)) {
+         return false;
+      }
+
+      // read each attribute
+      QSet<QByteArray> attributeHashes;
+      for (quint32 j = 0; j < numAttribs; ++j) {
+         QByteArray attributeHash;
+         if (!file.readBytearray(&attributeHash)) {
+            return false;
+         }
+         attributeHashes.insert(attributeHash);
+      }
+
+      item = new KSecretItem(itemId, this);
+      connect(item, SIGNAL(attributesChanged(KSecretItem*)),
+                    SLOT(changeAttributeHashes(KSecretItem*)));
+      Q_FOREACH(const QByteArray &hash, attributeHashes) {
+         m_itemHashes.insert(hash, item);
+      }
+      
+      // append hashes to the list of reverse hashes
+      m_reverseItemHashes.insert(item, attributeHashes);
+   }
+
+   return true;
+}
+
+bool KSecretCollection::deserializePartSymKey(const QByteArray &partContents)
+{
+   QBuffer buffer;
+   buffer.setData(partContents);
+   KSecretFile file(&buffer, KSecretFile::Read);
+   quint32 keyType;
+   QByteArray keyData;
+   if (!file.isValid() || !file.readUint(&keyType) || !file.readBytearray(&keyData)) \
{ +      return false;
+   }
+   EncryptedKey *key = new EncryptedKey;
+   key->m_type = keyType;
+   key->m_key = keyData;
+   m_encryptedSymKeys.append(key);
+   return true;
+}
+
+bool KSecretCollection::deserializePartAcls(const QByteArray &partContents)
+{
+   QBuffer buffer;
+   buffer.setData(partContents);
+   KSecretFile file(&buffer, KSecretFile::Read);
+   if (!file.isValid()) {
+      return false;
+   }
+   
+   QByteArray partAcls;
+   if (!file.readBytearray(&partAcls) || !file.readBytearray(&m_aclsMac)) {
+      return false;
+   }
+   
+   QBuffer aclsBuffer(&partAcls);
+   KSecretFile aclsFile(&aclsBuffer, KSecretFile::Read);
+   quint32 numAcls;
+   if (!aclsFile.isValid() || !aclsFile.readUint(&numAcls)) {
+      return false;
+   }
+   
+   QString path;
+   quint32 value;
+   ApplicationPermission perm;
+   for (quint32 i = 0; i < numAcls; ++i) {
+      if (!aclsFile.readString(&path) || !file.readUint(&value)) {
+         return false;
+      }
+      
+      switch (value) {
+       
+      case PermissionAsk:
+         perm = PermissionAsk;
+         break;
+         
+      case PermissionDeny:
+         perm = PermissionDeny;
+         break;
+         
+      case PermissionAllow:
+         perm = PermissionAllow;
+         break;
+         
+      default:
+         // unknown acl rule
+         m_unknownAcls.insert(path, value);
+         continue;
+      }
+      
+      m_acls.insert(path, perm);
+   }
+   
+   return true;
+}
+
+bool KSecretCollection::deserializePartConfig(const QByteArray &partContents)
+{
+   QBuffer buffer;
+   buffer.setData(partContents);
+   KSecretFile file(&buffer, KSecretFile::Read);
+   if (!file.isValid()) {
+      return false;
+   }
+
+   QByteArray partConfig;
+   if (!file.readBytearray(&partConfig) || !file.readBytearray(&m_configValuesMac)) \
{ +      return false;
+   }
+
+   QBuffer configBuffer(&partConfig);
+   KSecretFile configFile(&configBuffer, KSecretFile::Read);
+   quint32 numConfigValues;
+   if (!configFile.isValid() || !configFile.readUint(&numConfigValues)) {
+      return false;
+   }
+
+   QString configKey;
+   QByteArray configValue;
+   for (quint32 i = 0; i < numConfigValues; ++i) {
+      if (!configFile.readString(&configKey) || \
!configFile.readBytearray(&configValue)) { +         return false;
+      }
+      
+      if (configKey == QLatin1String("CloseScreensaver")) {
+         if (configValue.size() == 1 && configValue[0] == '0') {
+            m_cfgCloseScreensaver = false;
+         }
+      } else if (configKey == QLatin1String("CloseIfUnused")) {
+         if (configValue.size() == 1 && configValue[0] == '0') {
+            m_cfgCloseIfUnused = false;
+         }
+      } else if (configKey == QLatin1String("CloseUnusedTimeout")) {
+         bool ok;
+         m_cfgCloseUnusedTimeout = configValue.toUInt(&ok);
+         if (!ok) {
+            m_cfgCloseUnusedTimeout = 30;
+         }
+      } else {
+         m_cfgUnknownKeys.insert(configKey, configValue);
+      }
+   }
+
+   return true;
+}
+
+bool KSecretCollection::deserializeItemsUnlocked(KSecretFile &file)
+{
+   Q_ASSERT(file.isValid());
+
+   KSecretItem *item;
+   quint32 numItems;
+   if (!file.readUint(&numItems)) {
+      return false;
+   }
+
+   QString itemId;
+   for (quint32 i = 0; i < numItems; ++i) {
+      // read the identifier of the item
+      if (!file.readString(&itemId)) {
+         return false;
+      }
+
+      // find the item matching the identifier
+      bool recreateHashes = false;
+      if (!m_items.contains(itemId)) {
+         // item without a hash. ignore this inconsistency and simply
+         // load the item, creating the hashes on-the-go.
+         item = new KSecretItem(itemId, this);
+         connect(item, SIGNAL(attributesChanged(KSecretItem*)),
+                       SLOT(changeAttributeHashes(KSecretItem*)));
+         m_items.insert(itemId, item);
+         recreateHashes = true;
+      } else {
+         item = m_items.value(itemId);
+      }
+      
+      QCA::SecureArray itemData;
+      if (!file.readSecret(&itemData)) {
+         return false;
+      }
+
+      SecureBuffer device(&itemData);
+      KSecretFile itemFile(&device, KSecretFile::Read);
+      if (!item->deserializeUnlocked(itemFile)) {
+         return false;
+      }
+
+      if (recreateHashes) {
+         changeAttributeHashes(item);
+      }
+   }
+}
+
+bool KSecretCollection::deserializePartEncrypted(const QByteArray &partContents,
+                                                 QCA::SecureArray &decryptedPart)
+{
+   Q_ASSERT(m_symmetricKey);
+
+   QBuffer buffer;
+   buffer.setData(partContents);
+   KSecretFile file(&buffer, KSecretFile::Read);
+   if (!file.isValid()) {
+      return false;
+   }
+   
+   QCA::SecureArray ivdata;
+   if (!file.readSecret(&ivdata)) {
+      return false;
+   }
+   QCA::InitializationVector iv(ivdata);
+   
+   QCA::SecureArray encryptedPart;
+   if (!file.readSecret(&encryptedPart)) {
+      return false;
+   }
+
+   // decrypt the data
+   m_cipher->setup(QCA::Decode, *m_symmetricKey, iv);
+   decryptedPart = m_cipher->update(encryptedPart);
+   if (!m_cipher->ok()) {
+      return false;
+   }
+   decryptedPart.append(m_cipher->final());
+   if (!m_cipher->ok()) {
+      return false;
+   }
+
+   return true;
+}
+
+bool KSecretCollection::serialize(QString &errorMessage) const
+{
+   KSaveFile device(m_path);
+   KSecretFile file(&device, KSecretFile::Write);
+   if (!file.isValid()) {
+      errorMessage = i18nc("Error message: collection file couldn't be opened for \
writing", +                           "Collection file couldn't be opened for \
writing"); +      return false;
+   }
+
+   if (!serializeHeader(file) || !serializeParts(file)) {
+      errorMessage = genericSavingErrorMessage();
+      return false;
+   }
+   
+   return true;
+}
+
+bool KSecretCollection::serializeHeader(KSecretFile &file) const
+{
+   // write the ksecret file header
+   if (!file.writeMagic() || !file.writeUint(VERSION_MAJOR) || \
!file.writeUint(VERSION_MINOR)) { +      return false;
+   }
+   
+   // algorithms
+   if (!file.writeUint(m_algoHash) && !file.writeUint(m_algoCipher)) {
+      return false;
+   }
+
+   return true;
+}
+
+bool KSecretCollection::serializeParts(KSecretFile &file) const
+{
+   QString errorMessage;
+   // 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
+   int curFilePartEntry = 0;
+   quint32 numParts = 4 + m_encryptedSymKeys.size() + m_unknownParts.size();
+   quint32 partTableSize = 4 + numParts * 12;
+   QList<FilePartEntry> filePartEntries;
+   for (quint32 i = 0; i < numParts; ++i) {
+      filePartEntries.append(FilePartEntry());
+   }
+   // remember the part table position and skip the part table so it can be written
+   // later.
+   quint64 partTablePos = file.pos();
+   if (!file.seek(partTablePos + partTableSize)) {
+      return false;
+   }
+   
+   // collection properties part
+   filePartEntries[curFilePartEntry].m_type = KSecretFile::PartCollProps;
+   filePartEntries[curFilePartEntry].m_position = (quint32)file.pos();
+   if (!file.writeString(m_id) || !file.writeString(m_label) ||
+       !file.writeDatetime(m_created) || !file.writeDatetime(m_modified)) {
+      return false;
+   }
+   curFilePartEntry++;
+
+   // config part
+   if (!serializeConfigPart(file, filePartEntries[curFilePartEntry])) {
+      return false;
+   }
+   curFilePartEntry++;
+
+   // acls part
+   filePartEntries[curFilePartEntry].m_type = KSecretFile::PartAcls;
+   filePartEntries[curFilePartEntry].m_position = (quint32)file.pos();
+   QBuffer buffer;
+   KSecretFile device(&buffer, KSecretFile::Write);
+   QHash<QString, KSecretCollection::ApplicationPermission>::const_iterator it = \
m_acls.constBegin(); +   QHash<QString, \
KSecretCollection::ApplicationPermission>::const_iterator end = m_acls.constEnd(); +  \
for ( ; it != end; ++it) { +      if (!file.writeString(it.key()) || \
!file.writeUint((quint32)it.value())) { +         return false;
+      }
+   }
+   if (!serializeAuthenticated(buffer.data(), file)) {
+      return false;
+   }
+   filePartEntries[curFilePartEntry].m_length =
+      (quint32)file.pos() - filePartEntries[curFilePartEntry].m_position;
+   curFilePartEntry++;
+
+   // item hash part
+   if (!serializeItemHashes(file, filePartEntries[curFilePartEntry])) {
+      return false;
+   }
+   curFilePartEntry++;
+
+   // items part
+   if (!serializeItems(file, filePartEntries[curFilePartEntry])) {
+      return false;
+   }
+   curFilePartEntry++;
+
+   // keys
+   Q_FOREACH(EncryptedKey *key, m_encryptedSymKeys) {
+      filePartEntries[curFilePartEntry].m_type = KSecretFile::PartSymKey;
+      filePartEntries[curFilePartEntry].m_position = (quint32)file.pos();
+      if (!file.writeUint(key->m_type) || !file.writeBytearray(key->m_key)) {
+         return false;
+      }
+      filePartEntries[curFilePartEntry].m_length =
+         (quint32)file.pos() - filePartEntries[curFilePartEntry].m_position;
+      curFilePartEntry++;
+   }
+
+   // unknown parts
+   Q_FOREACH(UnknownFilePart *unknown, m_unknownParts) {
+      filePartEntries[curFilePartEntry].m_type = unknown->m_type;
+      filePartEntries[curFilePartEntry].m_position = (quint32)file.pos();
+      if (!file.writeBytearray(unknown->m_contents)) {
+         return false;
+      }
+      filePartEntries[curFilePartEntry].m_length =
+         (quint32)file.pos() - filePartEntries[curFilePartEntry].m_position;
+      curFilePartEntry++;
+   }
+   
+   // write part table
+   if (!file.seek(partTablePos) || !file.writeUint(numParts)) {
+      return false;
+   }
+
+   Q_FOREACH(const FilePartEntry &part, filePartEntries) {
+      if (!file.writeUint(part.m_type) || !file.writeUint(part.m_position) ||
+          !file.writeUint(part.m_length)) {
+         return false;
+      }
+   }
+
+   file.close();
+   return true;
+}
+
+bool KSecretCollection::serializeAclsPart(KSecretFile &file, FilePartEntry &entry) \
const +{
+   entry.m_type = KSecretFile::PartAcls;
+   entry.m_position = (quint32)file.pos();
+   
+   // build temporary buffer containing acls
+   QBuffer aclsBuffer;
+   KSecretFile tempFile(&aclsBuffer, KSecretFile::Write);
+   if (!tempFile.writeUint(m_acls.size())) {
+      return false;
+   }
+   
+   QHash<QString, ApplicationPermission>::const_iterator it = m_acls.constBegin();
+   QHash<QString, ApplicationPermission>::const_iterator end = m_acls.constEnd();
+   for ( ; it != end; ++it) {
+      if (!tempFile.writeString(it.key()) || \
!tempFile.writeUint((quint32)it.value())) { +         return false;
+      }
+   }
+   
+   QMap<QString, int>::const_iterator it2 = m_unknownAcls.constBegin();
+   QMap<QString, int>::const_iterator end2 = m_unknownAcls.constEnd();
+   for ( ; it2 != end2; ++it2) {
+      if (!tempFile.writeString(it2.key()) || !tempFile.writeUint(it2.value())) {
+         return false;
+      }
+   }
+   
+   if (!serializeAuthenticated(aclsBuffer.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;
+   entry.m_position = (quint32)file.pos();
+   
+   // build temporary buffer containing config keys and values
+   QBuffer configBuffer;
+   KSecretFile tempFile(&configBuffer, KSecretFile::Write);
+   if (!tempFile.writeUint(3 + m_cfgUnknownKeys.size())) {
+      return false;
+   }
+   
+   // save known configuration values
+   QByteArray valCloseScreensaver;
+   valCloseScreensaver.setNum((int)m_cfgCloseScreensaver);
+   QByteArray valCloseIfUnused;
+   valCloseIfUnused.setNum((int)m_cfgCloseIfUnused);
+   QByteArray valCloseUnusedTimeout;
+   valCloseUnusedTimeout.setNum(m_cfgCloseUnusedTimeout);
+   if (!tempFile.writeString("CloseScreensaver") ||
+       !tempFile.writeBytearray(valCloseScreensaver) ||
+       !tempFile.writeString("CloseIfUnused") ||
+       !tempFile.writeBytearray(valCloseIfUnused) ||
+       !tempFile.writeString("CloseUnusedTimeout") ||
+       !tempFile.writeBytearray(valCloseUnusedTimeout)) {
+      return false;
+   }
+   
+   // save unknown configuration values
+   QMap<QString, QByteArray>::const_iterator it = m_cfgUnknownKeys.constBegin();
+   QMap<QString, QByteArray>::const_iterator end = m_cfgUnknownKeys.constEnd();
+   for ( ; it != end; ++it) {
+      if (!tempFile.writeString(it.key()) || !tempFile.writeBytearray(it.value())) {
+         return false;
+      }
+   }
+   
+   if (!serializeAuthenticated(configBuffer.data(), file)) {
+      return false;
+   }
+   entry.m_length = (quint32)file.pos() - entry.m_position;
+   
+   return true;
+}
+
+bool KSecretCollection::serializeItemHashes(KSecretFile &file, FilePartEntry &entry) \
const +{
+   entry.m_type = KSecretFile::PartItemHashes;
+   entry.m_position = (quint32)file.pos();
+
+   // as the hashes are stored as hash-value => item and they have to be written
+   // to the file the other way round (item => hash-value, hash-value, ...), we
+   // have to manually reverse them.
+
+   QHash<KSecretItem*, QList<QByteArray> > hashes;
+
+   QMultiHash<QByteArray, KSecretItem*>::const_iterator it = \
m_itemHashes.constBegin(); +   const QMultiHash<QByteArray, \
KSecretItem*>::const_iterator end = m_itemHashes.constEnd(); +   for ( ; it != end; \
++it) { +      hashes[it.value()].append(it.key());
+   }
+
+   if (!file.writeUint(hashes.size())) {
+      return false;
+   }
+
+   QHash<KSecretItem*, QList<QByteArray> >::const_iterator it2 = \
hashes.constBegin(); +   QHash<KSecretItem*, QList<QByteArray> >::const_iterator end2 \
= hashes.constEnd(); +   for ( ; it2 != end2; ++it2) {
+      if (!file.writeString(it2.key()->id()) || !file.writeUint(it2.value().size())) \
{ +         return false;
+      }
+      QList<QByteArray> hv = it2.value();
+      Q_FOREACH(const QByteArray &hash, hv) {
+         if (!file.writeBytearray(hash)) {
+            return false;
+         }
+      }
+   }
+
+   entry.m_length = (quint32)file.pos() - entry.m_position;
+
+   return true;
+}
+
+bool KSecretCollection::serializeItems(KSecretFile &file, FilePartEntry &entry) \
const +{
+   Q_ASSERT(file.isValid());
+
+   entry.m_type = KSecretFile::PartItems;
+   entry.m_position = (quint32)file.pos();
+   
+   // construct a file to write the unlocked items to
+   SecureBuffer device;
+   KSecretFile tempFile(&device, KSecretFile::Write);
+
+   if (!tempFile.writeUint(m_items.size())) {
+      return false;
+   }
+
+   QHash<QString, KSecretItem*>::const_iterator it = m_items.constBegin();
+   const QHash<QString, KSecretItem*>::const_iterator end = m_items.constEnd();
+   for ( ; it != end; ++it) {
+      if (!it.value()->serializeUnlocked(tempFile)) {
+         return false;
+      }
+   }
+
+   bool rc = serializeEncrypted(device.buffer(), file);
+   if (rc) {
+      entry.m_length = (quint32)file.pos() - entry.m_position;
+      return true;
+   } else {
+      return false;
+   }
+}
+
+bool KSecretCollection::serializeEncrypted(const QCA::SecureArray &data, KSecretFile \
&file) const +{
+   Q_ASSERT(file.isValid());
+   // TODO: those errors either have to be caught before calling this method
+   //       or inside.
+   Q_ASSERT(m_symmetricKey);
+   Q_ASSERT(m_hash);
+   Q_ASSERT(m_cipher);
+
+   QCA::InitializationVector iv(m_cipher->blockSize());
+   if (!file.writeSecret(iv)) {
+      return false;
+   }
+
+   // TODO: random padding?
+   
+   // encrypt the data
+   m_cipher->setup(QCA::Encode, *m_symmetricKey, iv);
+   QCA::SecureArray encryptedPart = m_cipher->update(data);
+   if (!m_cipher->ok()) {
+      return false;
+   }
+   encryptedPart.append(m_cipher->final());
+   if (!m_cipher->ok()) {
+      return false;
+   }
+
+   // write the encrypted data to file
+   if (!file.writeSecret(encryptedPart)) {
+      return false;
+   }
+
+   return true;
+}
+
+bool KSecretCollection::serializeAuthenticated(const QByteArray &data, KSecretFile \
&file) const +{
+   Q_ASSERT(m_mac);
+
+   // write actual data first
+   if (!file.writeUint(data.size()) || !file.writeBytearray(data)) {
+      return false;
+   }
+   
+   // compute the HMAC for the data
+   m_mac->clear();
+   m_mac->update(QCA::SecureArray(data));
+   QCA::SecureArray dataHash = m_mac->final();
+   
+   return file.writeSecret(dataHash);
+}
+
+#include "ksecretcollection.moc"
diff --git a/backend/ksecret/ksecretcollection.h \
b/backend/ksecret/ksecretcollection.h new file mode 100644
index 0000000..c796009
--- /dev/null
+++ b/backend/ksecret/ksecretcollection.h
@@ -0,0 +1,480 @@
+/*
+ * Copyright 2010, Michael Leupold <lemma@confuego.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License or (at your option) version 3 or any later version
+ * accepted by the membership of KDE e.V. (or its successor approved
+ * by the membership of KDE e.V.), which shall act as a proxy
+ * defined in Section 14 of version 3 of the license.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef KSECRETCOLLECTION_H
+#define KSECRETCOLLECTION_H
+
+#include "../backendcollection.h"
+#include "ksecretitem.h"
+#include "ksecretfile.h"
+
+class KSecretCollectionManager;
+
+/**
+ * Represents a part of the ksecret file stored in memory. The part's type
+ * is stored as unsigned integer as the part is unknown to this version of
+ * ksecretservice.
+ */
+struct UnknownFilePart
+{
+   quint32 m_type;
+   QByteArray m_contents;
+};
+
+/**
+ * Represents an encrypted key as stored inside the ksecret file. The key
+ * type is stored as unsigned integer as the key type might not be known
+ * to this version of ksecretservice.
+ */
+struct EncryptedKey
+{
+   quint32 m_type;
+   QByteArray m_key;
+};
+
+/**
+ * Holds a description of a ksecret file part.
+ */
+struct FilePartEntry
+{
+   quint32 m_type;
+   quint32 m_position;
+   quint32 m_length;
+};
+
+/**
+ * A collection stored on disk using the ksecret file format.
+ */
+class KSecretCollection : public BackendCollection
+{
+   Q_OBJECT
+
+private:
+   /**
+    * Constructor for loading an existing collection from a file or creating
+    * a new collection using create().
+    *
+    * @param parent the collection manager that loads this collection
+    */
+   KSecretCollection(BackendCollectionManager *parent);
+
+public:
+   /**
+    * Create a new collection.
+    *
+    * @param id id for the new collection
+    * @param parent parent collection manager
+    * @param errorMessage set in case of an error
+    * @return the new collection or 0 in case of an error
+    */
+   static KSecretCollection *create(const QString &id, BackendCollectionManager \
*parent, +                                    QString &errorMessage);
+   
+   /**
+    * Destructor
+    */
+   virtual ~KSecretCollection();
+
+   /**
+    * The unique identifier for this collection
+    */
+   virtual QString id() const;
+
+   /**
+    * The human-readable label for this collection.
+    * @todo error
+    */
+   virtual BackendReturn<QString> label() const;
+
+   /**
+    * Set this collection's label human-readable label.
+    *
+    * @todo error
+    * @param label the new label for this collection
+    */
+   virtual BackendReturn<void> setLabel(const QString &label);
+
+   /**
+    * The time this collection was created.
+    */
+   virtual QDateTime created() const;
+
+   /**
+    * The time this collection was last modified.
+    */
+   virtual QDateTime modified() const;
+
+   /**
+    * Check whether this collection is locked.
+    *
+    * @return true if the collection is locked, false if the collection
+    *         is unlocked.
+    */
+   virtual bool isLocked() const;
+
+   /**
+    * List all items inside this backend.
+    *
+    * @return a list containing all items inside this backend. An empty list
+    *         either means that no items were found or that an error occurred
+    *         (eg. collection needs unlocking before listing the items).
+    * @todo error
+    */
+   virtual BackendReturn<QList<BackendItem*> > items() const;
+
+   /**
+    * Check if a type of call can be handled immediately
+    * (synchronously) without requiring an async call.
+    *
+    * @param type the type of call to check
+    * @return true if the type of call can be handled
+    *         immediately, false if an async call is
+    *         required.
+    */
+   virtual bool isCallImmediate(AsyncCall::AsyncType type) const;
+
+   /**
+    * Return all items whose attributes match the search terms.
+    *
+    * @param attributes attributes against which the items should be matched
+    * @return a list of items matching the attributes. An empty list either means \
that +    *         no items were found or that an error occurred (eg. collection \
needs +    *         unlocking before listing the items).
+    * @todo error
+    */
+   virtual BackendReturn<QList<BackendItem*> > searchItems(
+      const QMap<QString, QString> &attributes) const;
+
+   /**
+    * Unlock this collection.
+    *
+    * @return true if the collection is now unlocked, false if unlocking
+    *         failed.
+    */
+   virtual BackendReturn<bool> unlock();
+
+   /**
+    * Lock this collection.
+    *
+    * @return true if the collection is now locked, false if locking
+    *         failed (the collection couldn't be locked or unlocking is not
+    *         supported by this backend).
+    */
+   virtual BackendReturn<bool> lock();
+
+   /**
+    * Delete this collection.
+    *
+    * @return true if the collection was deleted, false if it wasn't.
+    */
+   virtual BackendReturn<bool> deleteCollection();
+
+   /**
+    * Create an item.
+    *
+    * @param label label to assign to the new item
+    * @param attributes attributes to store for the new item
+    * @param secret the secret to store
+    * @param replace if true, an existing item with the same attributes
+    *                will be replaced, if false no item will be created
+    *                if one with the same attributes already exists
+    * @param locked if true, the item will be locked after creation
+    * @return the item created or 0 if an error occurred.
+    */
+   virtual BackendReturn<BackendItem*> createItem(const QString &label,
+                                                  const QMap<QString, QString> \
&attributes, +                                                  const \
QCA::SecureArray &secret, bool replace, +                                             \
bool locked); +
+   /**
+    * Change this collection's authentication.
+    *
+    * @return Always false as TemporaryCollection doesn't support authentication
+    */
+   virtual BackendReturn<bool> changeAuthentication();
+   
+   /**
+    * Get the path of the ksecret file the collection is stored inside.
+    *
+    * @return the path of the collection file
+    */
+   const QString &path() const;
+
+private Q_SLOTS:
+   /**
+    * Remove an item from our list of known items.
+    *
+    * @param item Item to remove
+    */
+   void slotItemDeleted(BackendItem *item);
+   
+   /**
+    * This slot is called whenever an Item's attributes change to rebuild the item \
lookup +    * hashes the collection uses to search items.
+    *
+    * @param item Item whose attributes changed
+    */
+   void changeAttributeHashes(KSecretItem *item);
+
+public:
+   /**
+    * Deserialize a ksecret collection from a KSecretFile.
+    *
+    * @param path Path to load the collection from
+    * @param parent parent collection manager
+    * @param errorMessage set if there's an error
+    * @return the KSecretCollection on success, 0 in case of an error
+    */
+   static KSecretCollection *deserialize(const QString &path, \
KSecretCollectionManager *parent, +                                         QString \
&errorMessage); +
+private:
+   /**
+    * Application permissions stored inside the ksecret file.
+    */
+   enum ApplicationPermission {
+      PermissionAsk = 0,   /// ask every time
+      PermissionDeny = 1,  /// deny every time
+      PermissionAllow = 2  /// always allow
+   };
+   
+   /**
+    * Set-up the encryption to be used by the secret collection.
+    * This creates hash and cipher functors as configured.
+    *
+    * @param errorMessage set in case of an error
+    * @return true if setting up the encryption worked, false if
+    *         there were errors (ie. unsupported encryption methods.
+    */
+   bool setupAlgorithms(QString &errorMessage);
+   
+   /**
+    * Deserialize the ksecret file header.
+    *
+    * @param file ksecret file to read from
+    * @param errorMessage set if there's an error
+    * @return true on success, false in case of an error
+    */
+   bool deserializeHeader(KSecretFile &file, QString &errorMessage);
+
+   /**
+    * Deserialize the algorithms used by a collection and check whether they
+    * are supported.
+    *
+    * @param file ksecret file to read from
+    * @param errorMessage set if there's an error
+    * @return true on success, false in case of an error
+    */
+   bool deserializeAlgorithms(KSecretFile &file, QString &errorMessage);
+
+   /**
+    * Deserialize the parts inside the ksecret file.
+    *
+    * @param file ksecret file to read from
+    * @param errorMessage set if there's an error
+    * @return true on success, false in case of an error
+    */
+   bool deserializeParts(KSecretFile &file, QString &errorMessage);
+
+   /**
+    * Deserialize the collection property part inside a ksecret file.
+    *
+    * @param partContents contents of the part to deserialize
+    * @return true on success, false in case of an error
+    */
+   bool deserializePartCollProps(const QByteArray &partContents);
+   
+   /**
+    * Deserialize an item hashes part inside a ksecret file.
+    *
+    * @param partContents contents of the part to deserialize
+    * @return true on success, false in case of an error
+    */
+   bool deserializePartItemHashes(const QByteArray &partContents);
+
+   /**
+    * Deserialize a symmetric key part inside a ksecret file.
+    *
+    * @param partContents contents of the part to deserialize
+    * @return true on success, false in case of an error
+    */
+   bool deserializePartSymKey(const QByteArray &partContents);
+
+   /**
+    * Deserialize an acl part inside a ksecret file.
+    *
+    * @param partContents contents of the part to deserialize
+    * @return true on success, false in case of an error
+    */
+   bool deserializePartAcls(const QByteArray &partContents);
+
+   /**
+    * Deserialize a config part inside a ksecret file.
+    *
+    * @param partContents contents of the part to deserialize
+    * @return true on success, false in case of an error
+    */
+   bool deserializePartConfig(const QByteArray &partContents);
+
+   /**
+    * Deserialize the unlocked items part contained in file.
+    *
+    * @param file File to read the unlocked items from
+    * @return true if reading the items was successful, false else
+    * @remarks if the return value of this method is false, some of the
+    *          items might already have been overwritten. So in this case
+    *          it's wise to clear the items' data and re-lock them.
+    */
+   bool deserializeItemsUnlocked(KSecretFile &file);
+   
+   /**
+    * Deserialize the contents of an encrypted part.
+    *
+    * @param partContents the contents of the part
+    * @param decryptedPart the decrypted part contents
+    * @return true if decrypting and deserializing was successful, false else
+    */
+   bool deserializePartEncrypted(const QByteArray &partContents, QCA::SecureArray \
&decryptedPart); +
+   /**
+    * Serialize this ksecret collection back to a KSecretFile.
+    *
+    * @param errorMessage set if there's an error
+    * @return true on success, false in case of an error
+    */
+   bool serialize(QString &errorMessage) const;
+
+   /**
+    * Serialize a ksecret file's headers.
+    *
+    * @param file ksecret file to write to
+    * @return true if serialization was successful, false else
+    */
+   bool serializeHeader(KSecretFile &file) const;
+   
+   /**
+    * Serialize a collection's parts to a ksecret file.
+    *
+    * @param file ksecret file to write to
+    * @return true if serialization was successful, false else
+    */
+   bool serializeParts(KSecretFile &file) const;
+   
+   /**
+    * Serialize a collection's configuration to a ksecret file.
+    *
+    * @param file ksecret file to serialize the configuration values to
+    * @param entry file part descriptor to put part information to
+    * @return true if serialization was successful, false else
+    */
+   bool serializeConfigPart(KSecretFile &file, FilePartEntry &entry) const;
+   
+   /**
+    * Serialize a collection's acls to a ksecret file.
+    *
+    * @param file ksecret file to serialize the acls to
+    * @param entry file part descriptor to put part information to
+    * @return true if serialization was successful, false else
+    */
+   bool serializeAclsPart(KSecretFile &file, FilePartEntry &entry) const;
+   
+   /**
+    * Serialize the item hashes of this collection to the ksecret file.
+    *
+    * @param file ksecret file to write to
+    * @return true on success, false in case of an error
+    */
+   bool serializeItemHashes(KSecretFile &file, FilePartEntry &entry) const;
+
+   /**
+    * Serialize the unlocked items of this collection to the ksecret file.
+    *
+    * @param file ksecret file to write to
+    * @return true on success, false in case of an error
+    */
+   bool serializeItems(KSecretFile &file, FilePartEntry &entry) const;
+
+   /**
+    * Write an encrypted part.
+    *
+    * @param data data to be encrypted and written
+    * @param file ksecret file to write to
+    * @param true on success, false in case of an error
+    */
+   bool serializeEncrypted(const QCA::SecureArray &data, KSecretFile &file) const;
+   
+   /**
+    * Serialize a file part by writing it to the ksecret file and appending a hash \
mac +    * to sign its contents.
+    *
+    * @param data data to write to the part
+    * @param file ksecret file to write the data to
+    * @return true if serializing was successful, false else
+    */
+   bool serializeAuthenticated(const QByteArray &data, KSecretFile &file) const;
+
+   QString m_id;
+   QString m_label;
+   QDateTime m_created;
+   QDateTime m_modified;
+
+   QString m_path; // path of the ksecret file on disk
+   
+   // configuration values
+   bool m_cfgCloseScreensaver;     // close when the screensaver starts
+   bool m_cfgCloseIfUnused;        // close when the last application stops using it
+   quint32 m_cfgCloseUnusedTimeout; // timeout the collection will be closed after \
if unused (secs) +   QMap<QString, QByteArray> m_cfgUnknownKeys; // unknown \
configuration keys +
+   quint32 m_algoHash;             // hashing/mac algorithm identifier
+   QCA::Hash *m_hash;              // hashing algorithm
+   QCA::MessageAuthenticationCode *m_mac; // message authentication code algorithm
+   quint32 m_algoCipher;           // encryption algorithm identifier
+   QCA::Cipher *m_cipher;          // encryption algorithm
+
+   QCA::SymmetricKey *m_symmetricKey; // the symmetric key used for \
encryption/decryption +
+   // the configuration values message authentication code.
+   QByteArray m_configValuesMac;
+
+   // the acls message authentication code
+   QHash<QString, ApplicationPermission> m_acls;
+   QMap<QString, int> m_unknownAcls;
+   QByteArray m_aclsMac;
+
+   // unknown file parts stored as-is
+   QList<UnknownFilePart*> m_unknownParts;
+
+   // contains the encrypted item parts
+   QList<QByteArray> m_encryptedItemParts;
+
+   // contains the encrypted symmetric keys
+   QList<EncryptedKey*> m_encryptedSymKeys;
+
+   // maps lookup attribute hashes to items
+   QMultiHash<QByteArray, KSecretItem*> m_itemHashes;
+   // maps item ids to their hashes for changing/removal
+   QHash<KSecretItem*, QSet<QByteArray> > m_reverseItemHashes;
+
+   // maps item identifiers to items
+   QHash<QString, KSecretItem*> m_items;
+};
+
+#endif
diff --git a/backend/ksecret/ksecretcollectionmanager.cpp \
b/backend/ksecret/ksecretcollectionmanager.cpp new file mode 100644
index 0000000..7dc31d6
--- /dev/null
+++ b/backend/ksecret/ksecretcollectionmanager.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2010, Michael Leupold <lemma@confuego.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License or (at your option) version 3 or any later version
+ * accepted by the membership of KDE e.V. (or its successor approved
+ * by the membership of KDE e.V.), which shall act as a proxy
+ * defined in Section 14 of version 3 of the license.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ksecretcollectionmanager.h"
+#include "ksecretcollection.h"
+
+#include "secrettool.h"
+
+#include <QtCore/QTimer>
+#include <QtCore/QDir>
+
+KSecretCollectionManager::KSecretCollectionManager(const QString &path, QObject \
*parent) + : BackendCollectionManager(parent), m_watcher(QStringList(path))
+{
+   connect(&m_watcher, SIGNAL(directoryChanged(QString)), \
SLOT(slotDirectoryChanged(QString))); +   // list directory contents to discover \
existing collections on startup +   QTimer::singleShot(0, this, \
SLOT(slotStartupDiscovery())); +}
+
+KSecretCollectionManager::~KSecretCollectionManager()
+{
+   // TODO: cleanup?
+}
+
+bool KSecretCollectionManager::isCallImmediate(AsyncCall::AsyncType type) const
+{
+   // TODO: based on available authentication methods this may return true
+   if (type == AsyncCall::AsyncCreateCollectionMaster) {
+      return false;
+   } else {
+      return true;
+   }
+}
+
+BackendReturn<BackendCollection*> KSecretCollectionManager::createCollection(const \
QString &label, +                                                                     \
bool locked) +{
+   // TODO: collection needs authentication methods, filenames, ...
+   QString errorMessage;
+   KSecretCollection *coll = KSecretCollection::create(createId(), this, \
errorMessage); +   coll->setLabel(label);
+   
+   // TODO: coll has be added to m_collections before serializing it for the first
+   //       time, so when slotDirectoryChanged is called, the collection is already
+   //       known.
+   m_collections.insert(coll->path(), coll);
+
+   connect(coll, SIGNAL(collectionDeleted(BackendCollection*)),
+                 SIGNAL(collectionDeleted(BackendCollection*)));
+   emit collectionCreated(coll);
+
+   if (locked) {
+      coll->lock();
+   }
+   
+   return coll;
+}
+
+void KSecretCollectionManager::slotDirectoryChanged(const QString &path)
+{
+   // list all collections in the directory and check if there's a collection
+   // which we don't know yet.
+   QDir dir(path);
+   QStringList entries = dir.entryList(QStringList("*.ksecret"), QDir::Files);
+   Q_FOREACH(const QString &file, entries) {
+      if (!m_collections.contains(file)) {
+         QString errorMessage;
+         KSecretCollection *coll = KSecretCollection::deserialize(file, this, \
errorMessage); +         if (coll) {
+            m_collections.insert(file, coll);
+         }
+      }
+   }
+}
+
+void KSecretCollectionManager::slotStartupDiscovery()
+{
+   Q_ASSERT(m_watcher.directories().count() == 1);
+   slotDirectoryChanged(m_watcher.directories().at(0));
+}
+
+#include "ksecretcollectionmanager.moc"
diff --git a/backend/ksecret/ksecretcollectionmanager.h \
b/backend/ksecret/ksecretcollectionmanager.h new file mode 100644
index 0000000..4633dc8
--- /dev/null
+++ b/backend/ksecret/ksecretcollectionmanager.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2010, Michael Leupold <lemma@confuego.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License or (at your option) version 3 or any later version
+ * accepted by the membership of KDE e.V. (or its successor approved
+ * by the membership of KDE e.V.), which shall act as a proxy
+ * defined in Section 14 of version 3 of the license.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef KSECRETCOLLECTIONMANAGER_H
+#define KSECRETCOLLECTIONMANAGER_H
+
+#include "../backendcollectionmanager.h"
+
+#include <QtCore/QFileSystemWatcher>
+
+class KSecretCollection;
+
+/**
+ * Manager for collections stored in ksecret files.
+ */
+class KSecretCollectionManager : public BackendCollectionManager
+{
+   Q_OBJECT
+
+public:
+   /**
+    * Constructor
+    *
+    * @param path Path to detect ksecret files in
+    * @param parent parent object
+    */
+   KSecretCollectionManager(const QString &path, QObject *parent = 0);
+
+   /**
+    * Destructor
+    */
+   virtual ~KSecretCollectionManager();
+
+   /**
+    * Check if a type of call can be handled immediately
+    * (synchronously) without requiring an async call.
+    *
+    * @param type the type of call to check
+    * @return true if the type of call can be handled
+    *         immediately, false if an async call is required.
+    */
+   virtual bool isCallImmediate(AsyncCall::AsyncType type) const;
+
+   /**
+    * Create a new collection.
+    *
+    * @param label the label of the new collection
+    * @param lock if true, the collection should be locked after creation,
+    *             if false it should stay unlocked
+    * @return the collection or 0 on error
+    */
+   virtual BackendReturn<BackendCollection*> createCollection(const QString &label, \
bool locked); +
+private Q_SLOTS:
+   /**
+    * Connected to a filesystem watcher this slot is called whenever
+    * a ksecret file is added or removed.
+    *
+    * @param path directory that changed
+    */
+   void slotDirectoryChanged(const QString &path);
+   
+   /**
+    * Collection discovery method which can be called using a single-shot timer.
+    */
+   void slotStartupDiscovery();
+
+private:
+   // filesystem watcher to detect new/removed ksecret files
+   QFileSystemWatcher m_watcher;
+   
+   // map of paths pointing to the respective collection objects
+   QMap<QString, KSecretCollection*> m_collections;
+};
+
+#endif
diff --git a/backend/ksecret/ksecretfile.cpp b/backend/ksecret/ksecretfile.cpp
new file mode 100644
index 0000000..7f0e76c
--- /dev/null
+++ b/backend/ksecret/ksecretfile.cpp
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2010, Michael Leupold <lemma@confuego.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License or (at your option) version 3 or any later version
+ * accepted by the membership of KDE e.V. (or its successor approved
+ * by the membership of KDE e.V.), which shall act as a proxy
+ * defined in Section 14 of version 3 of the license.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ksecretfile.h"
+
+#include <QtCore/QtEndian>
+
+#define KSECRET_MAGIC "KSECRET\n\r\0\r\n"
+#define KSECRET_MAGIC_LEN 12
+
+// FIXME: currently the daemon could be tricked into reading a large portion of a \
forged +//        ksecret file wasting a lot of memory and making the pc unusable.
+
+KSecretFile::KSecretFile(QIODevice *device, OpenMode mode)
+ : m_device(device), m_mode(mode)
+{
+   m_valid = m_device->open((mode == Read) ? QIODevice::ReadOnly : \
QIODevice::WriteOnly); +}
+
+KSecretFile::~KSecretFile()
+{
+   m_device->close();
+   delete m_device;
+}
+
+void KSecretFile::close()
+{
+   m_device->close();
+}
+
+bool KSecretFile::isValid() const
+{
+   return m_valid;
+}
+
+qint64 KSecretFile::pos() const
+{
+   return m_device->pos();
+}
+
+bool KSecretFile::seek(qint64 pos)
+{
+   return m_device->seek(pos);
+}
+
+bool KSecretFile::readMagic()
+{
+   Q_ASSERT(m_mode == Read);
+
+   if (!m_valid) {
+      return false;
+   }
+   
+   m_readBuffer.resize(KSECRET_MAGIC_LEN);
+   if (m_device->read(m_readBuffer.data(), KSECRET_MAGIC_LEN) != KSECRET_MAGIC_LEN) \
{ +      m_valid = false;
+      return false;
+   }
+   return m_readBuffer == KSECRET_MAGIC;
+}
+
+bool KSecretFile::writeMagic()
+{
+   Q_ASSERT(m_mode == Write);
+
+   if (!m_valid) {
+      return false;
+   }
+   
+   if (m_device->write(KSECRET_MAGIC) != KSECRET_MAGIC_LEN) {
+      m_valid = false;
+   }
+   return m_valid;
+}
+
+bool KSecretFile::readUint(quint32 *value)
+{
+   Q_ASSERT(value);
+   Q_ASSERT(m_mode == Read);
+
+   if (!m_valid) {
+      return false;
+   }
+
+   m_readBuffer.resize(4);
+   if (m_device->read(m_readBuffer.data(), 4) != 4) {
+      m_valid = false;
+      return false;
+   }
+   *value = qFromBigEndian<quint32>((const uchar*)m_readBuffer.constData());
+   return true;
+}
+
+bool KSecretFile::writeUint(quint32 value)
+{
+   Q_ASSERT(m_mode == Write);
+
+   if (!m_valid) {
+      return false;
+   }
+
+   uchar outputBuffer[4];
+   qToBigEndian<quint32>(value, outputBuffer);
+   if (m_device->write((const char*)outputBuffer, 4) != 4) {
+      m_valid = false;
+   }
+   return m_valid;
+}
+
+bool KSecretFile::readDatetime(QDateTime *value)
+{
+   Q_ASSERT(value);
+   Q_ASSERT(m_mode == Read);
+
+   if (!m_valid) {
+      return false;
+   }
+
+   m_readBuffer.resize(8);
+   if (m_device->read(m_readBuffer.data(), 8) != 8) {
+      m_valid = false;
+      return false;
+   }
+   value->setTime_t(qFromBigEndian<quint64>((const \
uchar*)m_readBuffer.constData())); +   return true;
+}
+
+bool KSecretFile::writeDatetime(const QDateTime &value)
+{
+   Q_ASSERT(m_mode == Write);
+
+   if (!m_valid) {
+      return false;
+   }
+
+   uchar outputBuffer[8];
+   qToBigEndian<quint64>(value.toTime_t(), outputBuffer);
+   if (m_device->write((const char*)outputBuffer, 8) != 8) {
+      m_valid = false;
+   }
+   return m_valid;
+}
+
+bool KSecretFile::readBytearray(QByteArray *value)
+{
+   Q_ASSERT(value);
+   Q_ASSERT(m_mode == Read);
+
+   if (!m_valid) {
+      return false;
+   }
+
+   quint32 length;
+   if (!readUint(&length)) {
+      m_valid = false;
+      return false;
+   }
+
+   value->resize(length);
+   if (m_device->read(value->data(), length) != length) {
+      m_valid = false;
+   }
+   return m_valid;
+}
+
+bool KSecretFile::writeBytearray(const QByteArray &value)
+{
+   Q_ASSERT(m_mode == Write);
+
+   if (!m_valid) {
+      return false;
+   }
+
+   if (!writeUint(value.size()) || m_device->write(value) != value.size()) {
+      m_valid = false;
+   }
+   return m_valid;
+}
+
+bool KSecretFile::readString(QString *value)
+{
+   Q_ASSERT(value);
+   Q_ASSERT(m_mode == Read);
+
+   if (!m_valid) {
+      return false;
+   }
+
+   QByteArray bytearray;
+   if (!readBytearray(&bytearray)) {
+      m_valid = false;
+      return false;
+   }
+
+   // TODO: detect invalid Utf-8 strings!
+   *value = QString::fromUtf8(bytearray);
+   return true;
+}
+
+bool KSecretFile::writeString(const QString &value)
+{
+   return writeBytearray(value.toUtf8());
+}
+
+bool KSecretFile::readSecret(QCA::SecureArray *value)
+{
+   Q_ASSERT(value);
+   Q_ASSERT(m_mode == Read);
+
+   if (!m_valid) {
+      return false;
+   }
+
+   quint32 length;
+   if (!readUint(&length)) {
+      m_valid = false;
+      return false;
+   }
+
+   value->resize(length);
+   if (m_device->read(value->data(), length) != length) {
+      m_valid = false;
+      return false;
+   }
+   return true;
+}
+
+bool KSecretFile::writeSecret(const QCA::SecureArray &value)
+{
+   Q_ASSERT(m_mode == Write);
+
+   if (!m_valid) {
+      return false;
+   }
+
+   if (!writeUint(value.size()) ||
+       m_device->write(value.constData(), value.size()) != value.size()) {
+      m_valid = false;
+   }
+   return m_valid;
+}
+
+bool KSecretFile::readPart(QByteArray *value, quint32 position, quint32 length)
+{
+   Q_ASSERT(value);
+   Q_ASSERT(m_mode == Read);
+
+   if (!m_valid) {
+      return false;
+   }
+
+   // remember current position
+   qint64 oldPos = m_device->pos();
+
+   bool rc = true;
+   
+   // seek new position
+   if (m_device->seek(position)) {
+      value->resize(length);
+      if (m_device->read(value->data(), length) != length) {
+         rc = false;
+      }
+   } else {
+      rc = false;
+   }
+
+   m_device->seek(oldPos);
+   m_valid = rc;
+   return rc;
+}
diff --git a/backend/ksecret/ksecretfile.h b/backend/ksecret/ksecretfile.h
new file mode 100644
index 0000000..0b7385c
--- /dev/null
+++ b/backend/ksecret/ksecretfile.h
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2010, Michael Leupold <lemma@confuego.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License or (at your option) version 3 or any later version
+ * accepted by the membership of KDE e.V. (or its successor approved
+ * by the membership of KDE e.V.), which shall act as a proxy
+ * defined in Section 14 of version 3 of the license.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef KSECRETFILE_H
+#define KSECRETFILE_H
+
+#define VERSION_MAJOR 0
+#define VERSION_MINOR 1
+
+#include <QtCore/QIODevice>
+#include <QtCore/QDateTime>
+#include <QtCrypto>
+
+/**
+ * Encapsulates reading from and writing to a ksecret file.
+ */
+class KSecretFile
+{
+public:
+   /**
+    * Modes for opening the ksecret file.
+    */
+   enum OpenMode {
+      Read,  /// Open file for reading
+      Write  /// Open file for writing
+   };
+
+   /**
+    * Known hashing and MAC algorithms.
+    */
+   enum AlgorithmHash {
+      SHA256 = 0 /// SHA256
+   };
+
+   /**
+    * Known encryption algorithms.
+    */
+   enum AlgorithmEncryption {
+      AES256 = 0 /// AES256 using CBC and default padding
+   };
+
+   /**
+    * Known types of parts in the ksecret file.
+    */
+   enum PartType {
+      PartItemHashes = 0,  /// Attribute hashes for item lookup
+      PartSymKey = 1,      /// Encrypted symmetric master-key
+      PartItems = 2,       /// Encrypted items
+      PartAcls = 3,        /// Signed ACLs
+      PartConfig = 4,      /// Signed collection configuration
+      PartCollProps = 5    /// Signed collection properties
+   };
+   
+   /**
+    * Known encryption types for the symmetric key.
+    */
+   enum KeyType {
+      KeyPassword = 0,     /// Key encrypted using a password
+      KeyBogus = 666       /// TODO: remove this once other keys are implemented
+   };
+
+   /**
+    * Constructor.
+    *
+    * @param device device to read the contents from
+    * @param mode set whether to read or to write
+    */
+   KSecretFile(QIODevice *device, OpenMode mode);
+
+   /**
+    * Destructor.
+    */
+   ~KSecretFile();
+
+   /**
+    * Check if the secret file was constructed validly.
+    * This DOES NOT check if the file contents are valid.
+    *
+    * @return true if the object can be worked with, false if an
+    *         error occurred during opening or reading/writing data.
+    */
+   bool isValid() const;
+
+   /**
+    * Close this ksecret file.
+    */
+   void close();
+
+   /**
+    * Get the current position withing the file.
+    *
+    * @return the current file position
+    */
+   qint64 pos() const;
+
+   /**
+    * Set the current position withing the file to pos.
+    *
+    * @param pos position to seek inside the underlying device
+    * @return true on success, false on error
+    */
+   bool seek(qint64 pos);
+
+   /**
+    * Read the ksecret file's magic value.
+    *
+    * @return true if the magic indicates this file is a ksecret file,
+    *         false else
+    */
+   bool readMagic();
+
+   /**
+    * Write the ksecret file's magic value.
+    *
+    * @return true if the magic was written successfull, false else
+    */
+   bool writeMagic();
+   
+   /**
+    * Read a UINT from the file.
+    *
+    * @param value pointer the UINT will be written to
+    * @return true if reading was successful, false else
+    */
+   bool readUint(quint32 *value);
+
+   /**
+    * Write a UINT to the file.
+    *
+    * @param value the UINT that should be written
+    * @return true if writing was successful, false else
+    */
+   bool writeUint(quint32 value);
+
+   /**
+    * Read a DATETIME from the file.
+    *
+    * @param value pointer the DATETIME will be written to
+    * @return true if reading was successful, false else
+    */
+   bool readDatetime(QDateTime *value);
+
+   /**
+    * Write a DATETIME to the file.
+    *
+    * @param value DATETIME to be written
+    * @return true if writing was successful, false else
+    */
+   bool writeDatetime(const QDateTime &value);
+
+   /**
+    * Read a BYTEARRAY from the file.
+    *
+    * @param value pointer the BYTEARRAY will be written to
+    * @return true if reading was successful, false else
+    */
+   bool readBytearray(QByteArray *value);
+
+   /**
+    * Write a BYTEARRAY to the file.
+    *
+    * @param value BYTEARRAY to be written
+    * @return true if writing was successful, false else
+    */
+   bool writeBytearray(const QByteArray &value);
+
+   /**
+    * Read a STRING from the file.
+    *
+    * @param value pointer the STRING will be written to
+    * @return true if reading was successful, false else
+    */
+   bool readString(QString *value);
+
+   /**
+    * Write a STRING to the file.
+    *
+    * @param value the STRING to be written
+    * @return true if writing was successful, false else
+    */
+   bool writeString(const QString &value);
+
+   /**
+    * Read a secret BYTEARRAY/STRING from the file.
+    *
+    * @param value pointer the secret will be written to
+    * @return true if reading was successul, false else
+    * @remarks basically the same as \sa readBytearray
+    *          but writes the results into a SecureArray.
+    */
+   bool readSecret(QCA::SecureArray *value);
+
+   /**
+    * Write a secret BYTEARRAY/STRING to the file.
+    *
+    * @param value the secret to be written
+    * @return true if writing was successful, false else
+    */
+   bool writeSecret(const QCA::SecureArray &value);
+
+   /**
+    * Read an entire part of the ksecret file.
+    *
+    * @param value pointer to the bytearray that should be filled
+    * @param position position inside the ksecret file
+    * @param length length of the part to read
+    */
+   bool readPart(QByteArray *value, quint32 position, quint32 length);
+
+private:
+   QIODevice *m_device;
+   OpenMode m_mode;
+   bool m_valid;
+
+   QByteArray m_readBuffer;
+};
+
+#endif
diff --git a/backend/ksecret/ksecretitem.cpp b/backend/ksecret/ksecretitem.cpp
new file mode 100644
index 0000000..8d63d34
--- /dev/null
+++ b/backend/ksecret/ksecretitem.cpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2010, Michael Leupold <lemma@confuego.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License or (at your option) version 3 or any later version
+ * accepted by the membership of KDE e.V. (or its successor approved
+ * by the membership of KDE e.V.), which shall act as a proxy
+ * defined in Section 14 of version 3 of the license.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ksecretitem.h"
+#include "ksecretcollection.h"
+#include "../securebuffer.h"
+
+KSecretItem::KSecretItem(const QString &id, KSecretCollection *parent)
+ : BackendItem(parent), m_collection(parent), m_id(id)
+{
+   Q_ASSERT(parent);
+}
+
+KSecretItem::~KSecretItem()
+{
+}
+
+QString KSecretItem::id() const
+{
+   return m_id;
+}
+
+BackendReturn<QString> KSecretItem::label() const
+{
+   if (isLocked()) {
+      return BackendReturn<QString>(QString(), ErrorIsLocked);
+   } else {
+      markAsUsed();
+      return m_label;
+   }
+}
+
+BackendReturn<void> KSecretItem::setLabel(const QString &label)
+{
+   if (isLocked()) {
+      return BackendReturn<void>(ErrorIsLocked);
+   } else {
+      m_label = label;
+      markAsModified();
+      return BackendReturn<void>();
+   }
+}
+
+BackendReturn<QCA::SecureArray> KSecretItem::secret() const
+{
+   if (isLocked()) {
+      return BackendReturn<QCA::SecureArray>(QCA::SecureArray(), ErrorIsLocked);
+   } else {
+      markAsUsed();
+      return m_secret;
+   }
+}
+
+BackendReturn<void> KSecretItem::setSecret(const QCA::SecureArray &secret)
+{
+   if (isLocked()) {
+      return BackendReturn<void>(ErrorIsLocked);
+   } else {
+      m_secret = secret;
+      markAsModified();
+      return BackendReturn<void>();
+   }
+}
+
+BackendReturn<QMap<QString, QString> > KSecretItem::attributes() const
+{
+   if (isLocked()) {
+      return BackendReturn<QMap<QString, QString> >(QMap<QString, QString>(), \
ErrorIsLocked); +   } else {
+      markAsUsed();
+      return m_attributes;
+   }
+}
+
+BackendReturn<void> KSecretItem::setAttributes(const QMap<QString, QString> \
&attributes) +{
+   if (isLocked()) {
+      return BackendReturn<void>(ErrorIsLocked);
+   } else {
+      m_attributes = attributes;
+      markAsModified();
+      emit attributesChanged(this);
+      return BackendReturn<void>();
+   }
+}
+
+QDateTime KSecretItem::created() const
+{
+   markAsUsed();
+   return m_created;
+}
+
+QDateTime KSecretItem::modified() const
+{
+   markAsUsed();
+   return m_modified;
+}
+
+bool KSecretItem::isLocked() const
+{
+   return m_collection->isLocked();
+}
+
+bool KSecretItem::isCallImmediate(AsyncCall::AsyncType type) const
+{
+   Q_ASSERT(m_collection);
+   
+   if (type == AsyncCall::AsyncDeleteItem) {
+      // delete item calls are always immediate
+      return true;
+   }
+
+   // all other relevant async calls are forwarded to the collection.
+   return m_collection->isCallImmediate(type);
+}
+
+BackendReturn<bool> KSecretItem::unlock()
+{
+   Q_ASSERT(m_collection);
+   // pass the unlock call on to the collection
+   return m_collection->unlock();
+}
+
+BackendReturn<bool> KSecretItem::lock()
+{
+   Q_ASSERT(m_collection);
+   // pass the lock call on to the collection
+   return m_collection->lock();
+}
+
+BackendReturn<bool> KSecretItem::deleteItem()
+{
+   emit itemDeleted(this);
+   deleteLater();
+   return true;
+}
+
+BackendReturn<bool> KSecretItem::changeAuthentication()
+{
+   // changing individual items' authentication is not supported
+   return BackendReturn<bool>(false, ErrorNotSupported);
+}
+
+bool KSecretItem::matches(const QMap<QString, QString> &attributes)
+{
+   QMap<QString, QString>::const_iterator it = attributes.constBegin();
+   const QMap<QString, QString>::const_iterator end = attributes.constEnd();
+   for ( ; it != end; ++it) {
+      if (!m_attributes.contains(it.key()) ||
+          m_attributes.value(it.key()) != it.value()) {
+         return false;
+      }
+   }
+   return true;
+}
+
+bool KSecretItem::deserializeUnlocked(KSecretFile &file)
+{
+   Q_ASSERT(file.isValid());
+
+   // deserialize everything to temporary variables/objects and write it
+   // into the members in one go once everything could be read successfully.
+   
+   // file currently points at item-label
+   QString itemLabel;
+   QDateTime itemCreated;
+   QDateTime itemModified;
+   quint32 numAttribs;
+   if (!file.readString(&itemLabel) || !file.readDatetime(&itemCreated) ||
+       !file.readDatetime(&itemModified) || !file.readUint(&numAttribs)) {
+      return false;
+   }
+
+   // read the attributes
+   QMap<QString, QString> attributes;
+   for (quint32 i = 0; i < numAttribs; ++i) {
+      QString attribKey;
+      QString attribValue;
+      if (!file.readString(&attribKey) || !file.readString(&attribValue)) {
+         return false;
+      }
+      attributes.insert(attribKey, attribValue);
+   }
+
+   // read the secret
+   QCA::SecureArray secret;
+   if (!file.readSecret(&secret)) {
+      return false;
+   }
+
+   m_label = itemLabel;
+   m_created = itemCreated;
+   m_modified = itemModified;
+   m_attributes = attributes;
+   m_secret = secret;
+
+   return true;
+}
+
+bool KSecretItem::serializeUnlocked(KSecretFile &file)
+{
+   Q_ASSERT(file.isValid());
+
+   if (!file.writeString(m_id)) {
+      return false;
+   }
+
+   // serialize data to a temporary array
+   SecureBuffer device;
+   KSecretFile tempFile(&device, KSecretFile::Write);
+   if (!tempFile.isValid()) {
+      return false;
+   }
+
+   if (!tempFile.writeString(m_label) || !tempFile.writeDatetime(m_created) ||
+       !tempFile.writeDatetime(m_modified) || \
!tempFile.writeUint(m_attributes.size())) { +      return false;
+   }
+
+   // serialize attributes
+   QMap<QString, QString>::const_iterator it = m_attributes.constBegin();
+   const QMap<QString, QString>::const_iterator end = m_attributes.constEnd();
+   for ( ; it != end; ++it) {
+      if (!tempFile.writeString(it.key()) || !tempFile.writeString(it.value())) {
+         return false;
+      }
+   }
+
+   // serialize secret
+   if (!tempFile.writeSecret(m_secret)) {
+      return false;
+   }
+
+   // now take the tempFile and write its contents to the actual file
+   if (!file.writeSecret(device.buffer())) {
+      return false;
+   }
+   return true;
+}
+
+QSet<QByteArray> KSecretItem::createHashes(const QMap<QString, QString> &attributes,
+                                           QCA::Hash *hash)
+{
+   Q_ASSERT(hash);
+   
+   QSet<QByteArray> hashSet;
+   QMap<QString, QString>::const_iterator it = attributes.constBegin();
+   QMap<QString, QString>::const_iterator end = attributes.constEnd();
+   for ( ; it != end; ++it) {
+      hash->clear();
+      hash->update(it.key().toUtf8());
+      hash->update(it.value().toUtf8());
+      hashSet.insert(hash->final().toByteArray());
+   }
+   
+   return hashSet;
+}
+
+QSet<QByteArray> KSecretItem::createAttributeHashes(QCA::Hash *hash) const
+{
+   return createHashes(m_attributes, hash);
+}
+
+void KSecretItem::markAsModified()
+{
+   m_modified = QDateTime::currentDateTime();
+   emit itemUsed(this);
+   emit itemChanged(this);
+}
+
+void KSecretItem::markAsUsed() const
+{
+   // TODO: figure out what to do. this method exists so a "close-if-unused" timer
+   //       can be implemented.
+}
+
+#include "ksecretitem.moc"
diff --git a/backend/ksecret/ksecretitem.h b/backend/ksecret/ksecretitem.h
new file mode 100644
index 0000000..678db96
--- /dev/null
+++ b/backend/ksecret/ksecretitem.h
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2010, Michael Leupold <lemma@confuego.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License or (at your option) version 3 or any later version
+ * accepted by the membership of KDE e.V. (or its successor approved
+ * by the membership of KDE e.V.), which shall act as a proxy
+ * defined in Section 14 of version 3 of the license.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef KSECRETITEM_H
+#define KSECRETITEM_H
+
+#include "../backenditem.h"
+#include "ksecretfile.h"
+
+#include <QtCore/QSet>
+
+class KSecretCollection;
+
+/**
+ * Represents an item stored inside a ksecret file.
+ */
+class KSecretItem : public BackendItem
+{
+   Q_OBJECT
+
+public:
+   /**
+    * Constructor.
+    *
+    * @param id unique identifier of the new item
+    * @param collection collection that created this item
+    */
+   KSecretItem(const QString &id, KSecretCollection *parent);
+
+   /**
+    * Destructor.
+    */
+   ~KSecretItem();
+
+   /**
+    * The unique identifer for this item.
+    */
+   virtual QString id() const;
+
+   /**
+    * The human-readable label for this item.
+    * @todo error
+    */
+   virtual BackendReturn<QString> label() const;
+
+   /**
+    * Set the human-readable label for this item.
+    *
+    * @param label the new label for this item
+    * @todo error
+    */
+   virtual BackendReturn<void> setLabel(const QString &label);
+
+   /**
+    * Get the secret stored inside this item.
+    *
+    * @return the secret
+    * @todo this will most likely become non-const as that might make sense
+    *       with certain backends me reckons.
+    */
+   virtual BackendReturn<QCA::SecureArray> secret() const;
+
+   /**
+    * Set the secret stored inside this item.
+    *
+    * @param secret the secret to store
+    */
+   virtual BackendReturn<void> setSecret(const QCA::SecureArray &secret);
+
+   /**
+    * The attributes of the item.
+    *
+    * @return the item's attributes
+    */
+   virtual BackendReturn<QMap<QString, QString> > attributes() const;
+
+   /**
+    * Set the attributes of this item.
+    *
+    * @param attributes attributes to assign to this item
+    */
+   virtual BackendReturn<void> setAttributes(const QMap<QString, QString> \
&attributes); +
+   /**
+    * The time this item was created.
+    */
+   virtual QDateTime created() const;
+
+   /**
+    * The time this item was last modified.
+    */
+   virtual QDateTime modified() const;
+
+   /**
+    * Check whether this item is locked.
+    *
+    * @return true if the item is locked, false else
+    */
+   virtual bool isLocked() const;
+
+   /**
+    * Check if a type of call can be handled immediately
+    * (synchronously) without requiring an async call.
+    *
+    * @param type the type of call to check
+    * @return true if the type of call can be handled
+    *         immediately, false if an async call is
+    *         required.
+    */
+   virtual bool isCallImmediate(AsyncCall::AsyncType type) const;
+
+   /**
+    * Unlock this item.
+    *
+    * @return true if the item was unlocked successfully, false if unlocking failed
+    *         or an error occurred.
+    * @remarks this is only called by processCall. To set an unlock call
+    *          erroneous, use the Base::setError() call.
+    */
+   virtual BackendReturn<bool> unlock();
+
+   /**
+    * Lock this item.
+    *
+    * @return true if the item was locked successfully, false if locking failed
+    *         or an error occurred.
+    * @remarks this is only called by processCall. To set a lock call
+    *          erroneous, use the Base::setError() call.
+    */
+   virtual BackendReturn<bool> lock();
+
+   /**
+    * Delete this item.
+    *
+    * @return true if the item was deleted, false if it wasn't or an error occurred.
+    * @remarks this is only called by processCall. To set a delete call
+    *          erroneous, use the Base::setError() call.
+    */
+   virtual BackendReturn<bool> deleteItem();
+
+   /**
+    * Change this item's authentication.
+    *
+    * @return always false as TemporaryItem doesn't support authentication
+    */
+   virtual BackendReturn<bool> changeAuthentication();
+
+   /**
+    * Check whether this item matches the attributes given.
+    *
+    * @param attributes attributes to match against
+    * @return true if this item matches the attributes, false
+    *         if the item doesn't match the attributes.
+    */
+   bool matches(const QMap<QString, QString> &attributes);
+
+protected:
+   /**
+    * Fill this item using the values contained in the unlocked file.
+    *
+    * @param file the file to read the values from
+    * @return true on success, false on error
+    */
+   bool deserializeUnlocked(KSecretFile &file);
+
+   /**
+    * Serialize this item's unlocked contents to the file given.
+    *
+    * @param file the file to write the values to
+    * @return true on success, false on error
+    */
+   bool serializeUnlocked(KSecretFile &file);
+   
+   /**
+    * Create a list of hashes out of this item's attributes.
+    *
+    * @param hash the hash function to use
+    * @return a list of hashes for each of the item's attributes
+    * @remarks called by KSecretCollection on-demand
+    */
+   QSet<QByteArray> createAttributeHashes(QCA::Hash *hash) const;
+   
+   /**
+    * Create a list of hashes out of some attributes.
+    *
+    * @param attributes the attributes to create the hashes for
+    * @param hash the hash function to use
+    * @returns a list of hashes for each of the attributes
+    */
+   static QSet<QByteArray> createHashes(const QMap<QString, QString> &attributes,
+                                        QCA::Hash *hash);
+
+Q_SIGNALS:
+   /**
+    * Emitted when the item has been "used" ie. one of its values has been
+    * read or written.
+    *
+    * @param item the item that was used
+    * @remarks this is used internally to implement the "close-if-unused" timer
+    */
+   void itemUsed(BackendItem *item);
+   
+   /**
+    * This signal is emitted when an item's attributes change so the
+    * collection can rebuild the attribute lookup hashes related to this
+    * item.
+    *
+    * @param item Item whose attributes changed
+    */
+   void attributesChanged(KSecretItem *item);
+   
+private:
+   friend class KSecretCollection;
+   
+   /**
+    * Mark this item as modified and emit the \sa itemChanged signal.
+    */
+   void markAsModified();
+
+   /**
+    * Mark this item as used and emit the \sa itemUsed signal.
+    */
+   void markAsUsed() const;
+
+   KSecretCollection *m_collection;
+   
+   QString m_id;
+   QString m_label;
+   QDateTime m_created;
+   QDateTime m_modified;
+   QMap<QString, QString> m_attributes;
+
+   QCA::SecureArray m_secret;
+};
+
+#endif
diff --git a/backend/temporarycollection.cpp b/backend/temporarycollection.cpp
index b9ce79a..d8cddba 100644
--- a/backend/temporarycollection.cpp
+++ b/backend/temporarycollection.cpp
@@ -125,7 +125,7 @@ BackendReturn<BackendItem*> TemporaryCollection::createItem(const \
QString &label  BackendReturn<QList<BackendItem*> > foundItems = \
searchItems(attributes);  if (!foundItems.isError() && foundItems.value().size() > 0) \
{  QList<BackendItem*> oldlist = foundItems.value();
-      Q_FOREACH(BackendItem* olditem, oldlist) {
+      Q_FOREACH(BackendItem *olditem, oldlist) {
          if (olditem->attributes().value() == attributes) {
             if (replace) {
                // replacing an existing item


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

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