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

List:       kde-commits
Subject:    [ksecrets] src/runtime/ksecrets_store: Starting to implement createCollection
From:       Valentin Rusu <kde () rusu ! info>
Date:       2015-08-15 22:02:45
Message-ID: E1ZQjXJ-0005RD-N5 () scm ! kde ! org
[Download RAW message or body]

Git commit 58c491477506a1fe6adbd04412da565dba0f6268 by Valentin Rusu.
Committed on 15/08/2015 at 22:02.
Pushed by vrusu into branch 'master'.

Starting to implement createCollection

M  +1    -0    src/runtime/ksecrets_store/CMakeLists.txt
M  +153  -24   src/runtime/ksecrets_store/ksecrets_crypt.cpp
M  +52   -18   src/runtime/ksecrets_store/ksecrets_data.cpp
M  +24   -9    src/runtime/ksecrets_store/ksecrets_data.h
M  +43   -2    src/runtime/ksecrets_store/ksecrets_file.cpp
M  +14   -1    src/runtime/ksecrets_store/ksecrets_file.h
M  +9    -3    src/runtime/ksecrets_store/ksecrets_store.cpp
M  +1    -0    src/runtime/ksecrets_store/ksecrets_store_p.h

http://commits.kde.org/ksecrets/58c491477506a1fe6adbd04412da565dba0f6268

diff --git a/src/runtime/ksecrets_store/CMakeLists.txt \
b/src/runtime/ksecrets_store/CMakeLists.txt index 5805ce7..1236bf0 100644
--- a/src/runtime/ksecrets_store/CMakeLists.txt
+++ b/src/runtime/ksecrets_store/CMakeLists.txt
@@ -9,6 +9,7 @@ ecm_setup_version(${KF5_VERSION} VARIABLE_PREFIX KSECRETS_BACKEND
                   PACKAGE_VERSION_FILE \
"${CMAKE_CURRENT_BINARY_DIR}/KF5SecretsStoreConfigVersion.cmake")  
 set(ksecrets_store_SRC
+    ksecrets_data.cpp
     ksecrets_file.cpp
     ksecrets_crypt.cpp
     ksecrets_credentials.cpp
diff --git a/src/runtime/ksecrets_store/ksecrets_crypt.cpp \
b/src/runtime/ksecrets_store/ksecrets_crypt.cpp index 0af3e1f..a5a32f8 100644
--- a/src/runtime/ksecrets_store/ksecrets_crypt.cpp
+++ b/src/runtime/ksecrets_store/ksecrets_crypt.cpp
@@ -20,6 +20,7 @@
 */
 
 #include "defines.h"
+#include "ksecrets_crypt.h"
 
 #include <sys/types.h>
 #include <errno.h>
@@ -51,7 +52,8 @@ int kss_init_gcry()
     gcry_error_t gcryerr;
     gcryerr = gcry_control(GCRYCTL_INIT_SECMEM, 32768, 0);
     if (gcryerr != 0) {
-        syslog(KSS_LOG_ERR, "ksecrets_store: cannot get secure memory: %d", gcryerr);
+        syslog(KSS_LOG_ERR, "ksecrets_store: cannot get secure memory: %d",
+            gcryerr);
         return 0;
     }
 
@@ -60,59 +62,79 @@ int kss_init_gcry()
     return 1;
 }
 
-int kss_derive_keys(const char* salt, const char* password, char* encryption_key, char* \
mac_key, size_t keySize) +int kss_derive_keys(const char* salt, const char* password,
+    char* encryption_key, char* mac_key, size_t keySize)
 {
     gpg_error_t gcryerr;
 
     syslog(KSS_LOG_INFO, "kss_set_credentials: attempting keys generation");
     if (0 == password) {
-        syslog(KSS_LOG_INFO, "NULL password given. ksecrets will not be available.");
+        syslog(KSS_LOG_INFO,
+            "NULL password given. ksecrets will not be available.");
         return FALSE;
     }
 
     /* generate both encryption and MAC key in one go */
     char keys[2 * keySize];
-    gcryerr = gcry_kdf_derive(password, strlen(password), GCRY_KDF_ITERSALTED_S2K, \
GCRY_MD_SHA512, salt, 8, KSECRETS_ITERATIONS, 2 * keySize, keys); +    gcryerr
+        = gcry_kdf_derive(password, strlen(password), GCRY_KDF_ITERSALTED_S2K,
+            GCRY_MD_SHA512, salt, 8, KSECRETS_ITERATIONS, 2 * keySize, keys);
     if (gcryerr) {
-        syslog(KSS_LOG_ERR, "key derivation failed: code 0x%0x: %s/%s", gcryerr, \
gcry_strsource(gcryerr), gcry_strerror(gcryerr)); +        syslog(KSS_LOG_ERR, "key derivation \
failed: code 0x%0x: %s/%s", +            gcryerr, gcry_strsource(gcryerr), \
gcry_strerror(gcryerr));  return FALSE;
     }
 
     memcpy(encryption_key, keys, keySize);
     memcpy(mac_key, keys + keySize, keySize);
-    syslog(KSS_LOG_INFO, "successuflly generated ksecrets keys from user password.");
+    syslog(KSS_LOG_INFO,
+        "successuflly generated ksecrets keys from user password.");
 
     return TRUE;
 }
 
-int kss_store_keys(const char* encryption_key, const char* mac_key, size_t keySize)
+int kss_store_keys(
+    const char* encryption_key, const char* mac_key, size_t keySize)
 {
     key_serial_t ks;
     const char* key_name = get_keyname_encrypting();
-    ks = add_key("user", key_name, encryption_key, keySize, KEY_SPEC_SESSION_KEYRING);
+    ks = add_key(
+        "user", key_name, encryption_key, keySize, KEY_SPEC_SESSION_KEYRING);
     if (-1 == ks) {
-        syslog(KSS_LOG_ERR, "ksecrets: cannot store encryption key in kernel keyring: errno=%d \
(%m)", errno); +        syslog(KSS_LOG_ERR, "ksecrets: cannot store encryption key in kernel "
+                            "keyring: errno=%d (%m)",
+            errno);
         return FALSE;
     }
-    syslog(KSS_LOG_DEBUG, "ksecrets: encrpyting key now in kernel keyring with id %d and desc \
%s", ks, key_name); +    syslog(KSS_LOG_DEBUG, "ksecrets: encrpyting key now in kernel keyring \
" +                          "with id %d and desc %s",
+        ks, key_name);
 
     key_name = get_keyname_mac();
-    ks = add_key("user", key_name, mac_key, keySize, KEY_SPEC_SESSION_KEYRING);
+    ks = add_key(
+        "user", key_name, mac_key, keySize, KEY_SPEC_SESSION_KEYRING);
     if (-1 == ks) {
-        syslog(KSS_LOG_ERR, "ksecrets: cannot store mac key in kernel keyring: errno=%d (%m)", \
errno); +        syslog(KSS_LOG_ERR,
+            "ksecrets: cannot store mac key in kernel keyring: errno=%d (%m)",
+            errno);
         return FALSE;
     }
-    syslog(KSS_LOG_DEBUG, "ksecrets: mac key now in kernel keyring with id %d and desc %s", \
ks, key_name); +    syslog(KSS_LOG_DEBUG,
+        "ksecrets: mac key now in kernel keyring with id %d and desc %s", ks,
+        key_name);
     return TRUE;
 }
 
-int kss_set_credentials(const std::string &password, const char *salt)
+int kss_set_credentials(const std::string& password, const char* salt)
 {
-    // FIXME this should be adjusted on platforms where kernel keyring is not available and \
store the keys elsewhere +    // FIXME this should be adjusted on platforms where kernel \
keyring is not +    // available and store the keys elsewhere
     char encryption_key[KSECRETS_KEYSIZE];
     char mac_key[KSECRETS_KEYSIZE];
-    auto res = kss_derive_keys(salt, password.c_str(), encryption_key, mac_key, \
                KSECRETS_KEYSIZE);
-    if (res) return res;
+    auto res = kss_derive_keys(
+        salt, password.c_str(), encryption_key, mac_key, KSECRETS_KEYSIZE);
+    if (res)
+        return res;
 
     return kss_store_keys(encryption_key, mac_key, KSECRETS_KEYSIZE);
 }
@@ -120,26 +142,32 @@ int kss_set_credentials(const std::string &password, const char *salt)
 int kss_keys_already_there()
 {
     key_serial_t key;
-    key = request_key("user", get_keyname_encrypting(), 0, KEY_SPEC_SESSION_KEYRING);
+    key = request_key(
+        "user", get_keyname_encrypting(), 0, KEY_SPEC_SESSION_KEYRING);
     if (-1 == key) {
-        syslog(KSS_LOG_DEBUG, "request_key failed with errno %d (%m), so assuming ksecrets not \
yet loaded", errno); +        syslog(KSS_LOG_DEBUG, "request_key failed with errno %d (%m), so \
" +                              "assuming ksecrets not yet loaded",
+            errno);
         return FALSE;
     }
     syslog(KSS_LOG_DEBUG, "ksecrets: keys already in keyring");
     return TRUE;
 }
 
-long kss_read_key(const char *keyName, char* buffer, size_t bufferSize)
+long kss_read_key(const char* keyName, char* buffer, size_t bufferSize)
 {
     key_serial_t key;
     key = request_key("user", keyName, 0, KEY_SPEC_SESSION_KEYRING);
     if (-1 == key) {
-        syslog(KSS_LOG_DEBUG, "request_key failed with errno %d (%m) when reading MAC key %s", \
errno, keyName); +        syslog(KSS_LOG_DEBUG,
+            "request_key failed with errno %d (%m) when reading MAC key %s",
+            errno, keyName);
         return -1;
     }
     auto bytes = keyctl_read(key, buffer, bufferSize);
     if (bytes == -1) {
-        syslog(KSS_LOG_ERR, "error reading key %s contents from the keyring", keyName);
+        syslog(KSS_LOG_ERR, "error reading key %s contents from the keyring",
+            keyName);
         return -1;
     }
     if ((size_t)bytes > bufferSize) {
@@ -148,13 +176,114 @@ long kss_read_key(const char *keyName, char* buffer, size_t bufferSize)
     return 0; // key contents correctly transffered into the buffer
 }
 
-long kss_read_mac_key(char *buffer, size_t bufferSize)
+long kss_read_mac_key(char* buffer, size_t bufferSize)
 {
     return kss_read_key(get_keyname_mac(), buffer, bufferSize);
 }
 
-long kss_read_encrypting_key(char *buffer, size_t bufferSize)
+long kss_read_encrypting_key(char* buffer, size_t bufferSize)
 {
     return kss_read_key(get_keyname_encrypting(), buffer, bufferSize);
 }
 
+#define ERRNO(cryres) gcry_err_code_to_errno(gcry_err_code(cryres))
+
+long kss_cipher_setup(gcry_cipher_hd_t* hd, const void* iv, size_t liv)
+{
+    // FIXME perhaps all this initialization stuff could only be done once,
+    // when password is setup
+    auto cryres
+        = gcry_cipher_open(hd, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CBC, 0);
+    if (cryres) {
+        syslog(KSS_LOG_ERR, "ksecrets: gcry_cipher_open returned error %d",
+            cryres);
+        return ERRNO(cryres);
+    }
+    cryres = gcry_cipher_setiv(*hd, iv, liv);
+    if (cryres) {
+        syslog(KSS_LOG_ERR, "ksecrets: gcry_cipher_setif returned error %d",
+            cryres);
+        return ERRNO(cryres);
+    }
+    char encryptingKey[KSECRETS_KEYSIZE];
+    auto keyres = kss_read_encrypting_key(
+        encryptingKey, sizeof(encryptingKey) / sizeof(encryptingKey[0]));
+    if (!keyres) {
+        syslog(
+            KSS_LOG_ERR, "ksecrets: encrypting key not found in the keyring");
+        return keyres;
+    }
+    cryres = gcry_cipher_setkey(
+        *hd, encryptingKey, sizeof(encryptingKey) / sizeof(encryptingKey[0]));
+    if (cryres) {
+        syslog(
+            KSS_LOG_ERR, "ksecrets: gcry_cipher_setkey returned %d", cryres);
+        return ERRNO(cryres);
+    }
+    return 0;
+}
+
+long kss_encrypt_buffer(unsigned char* out, size_t lout, const void* iv,
+    size_t liv, const unsigned char* in, size_t lin)
+{
+    gcry_cipher_hd_t hd;
+    auto cryres = kss_cipher_setup(&hd, iv, liv);
+    if (cryres)
+        return cryres; // error already logged
+    cryres = gcry_cipher_encrypt(hd, out, lout, in, lin);
+    if (cryres) {
+        syslog(KSS_LOG_ERR, "ksecrets: gcry_cipher_encrypt returned %ld",
+            cryres);
+        return ERRNO(cryres);
+    }
+    return 0;
+}
+
+long kss_decrypt_buffer(unsigned char* out, size_t lout, const void* iv,
+    size_t liv, const unsigned char* in, size_t lin)
+{
+    gcry_cipher_hd_t hd;
+    auto cryres = kss_cipher_setup(&hd, iv, liv);
+    if (cryres)
+        return cryres; // error already logged
+    cryres = gcry_cipher_decrypt(hd, out, lout, in, lin);
+    if (cryres) {
+        syslog(KSS_LOG_ERR, "ksecrets: gcry_cipher_decrypt returned %ld",
+            cryres);
+        return ERRNO(cryres);
+    }
+    return 0;
+}
+
+bool CryptBuffer::resize(size_t rlen)
+{
+    if (rlen <= len_) {
+        return false; // if less bytes, we cannot copy; if same, why resize?
+    }
+    CryptBuffer oldBuffer(std::move(*this));
+    len_ = (rlen / cipherBlockLen_ + 1) * cipherBlockLen_;
+    data_ = new unsigned char[len_];
+    if (data_ && oldBuffer.len_) {
+        memmove(data_, oldBuffer.data_, oldBuffer.len_);
+        return true;
+    }
+    return false;
+}
+
+void CryptBuffer::empty()
+{
+    delete[] data_, data_ = nullptr;
+    len_ = 0;
+}
+
+bool CryptBuffer::allocate(size_t rlen)
+{
+    empty();
+    data_ = new unsigned char[rlen];
+    if (data_ == nullptr) {
+        return false;
+    }
+    len_ = rlen;
+    return true;
+}
+// vim: tw=220:ts=4
diff --git a/src/runtime/ksecrets_store/ksecrets_data.cpp \
b/src/runtime/ksecrets_store/ksecrets_data.cpp index 4998fcd..61a8f64 100644
--- a/src/runtime/ksecrets_store/ksecrets_data.cpp
+++ b/src/runtime/ksecrets_store/ksecrets_data.cpp
@@ -23,32 +23,66 @@
 
 #include <unistd.h>
 
+long kss_encrypt_buffer(unsigned char* out, size_t lout, const void* iv,
+    size_t liv, const unsigned char* in, size_t lin);
+long kss_decrypt_buffer(unsigned char* out, size_t lout, const void* iv,
+    size_t liv, const unsigned char* in, size_t lin);
+char* kss_alloc_crypt_buffer(size_t rlen);
+
 SecretsEntity::SecretsEntity()
-    : size_(0)
-    , state_(Empty)
-    , encrypted_(nullptr)
-    , unencrypted_(nullptr)
+    : state_(State::Empty)
 {
 }
-SecretsEntity::~SecretsEntity()
+
+SecretsEntity::~SecretsEntity() {}
+
+// TODO refactor this when encrypting plugins will be put in place
+// the KSecretsFile should place the IV in the plugin structure instead of
+// this class
+const char* iv = nullptr;
+size_t liv = KSecretsFile::IV_SIZE;
+
+bool SecretsEntity::read(KSecretsFile& file)
 {
-    if (encrypted_) {
-        ::free(encrypted_);
-        encrypted_ = nullptr;
+    if (iv == nullptr) {
+        iv = file.iv();
     }
-    if (unencrypted_) {
-        ::free(unencrypted_);
-        unencrypted_ = nullptr;
+
+    size_t s;
+    if (!file.read(s)) {
+        return false;
     }
-}
-bool SecretsEntity::read(KSecretsFile& file)
-{
-    if (!file.read(size_)) {
+    if (!encrypted_.allocate(s)) {
         return false;
     }
 
-    encrypted_ = ::malloc(res);
-    if (encrypted_ == nullptr) {
+    return file.read(encrypted_.data_,
+        encrypted_.len_); // beware not to specify encrypted.size_ here
+}
+
+bool SecretsEntity::decrypt()
+{
+    if (isEmpty())
         return false;
-    }
+    if (unencrypted_.len_ > 0)
+        return true; // already decrpyted
+    if (encrypted_.len_ == 0)
+        return false; // what to decrypt?
+    unencrypted_.allocate(encrypted_.len_);
+    auto dres = kss_decrypt_buffer(unencrypted_.data_, unencrypted_.len_, iv,
+        liv, encrypted_.data_, encrypted_.len_);
+    return dres == 0;
+}
+
+bool SecretsEntity::encrypt()
+{
+    // TODO
+    return false;
+}
+
+bool SecretsEntity::write(KSecretsFile &) const
+{
+    // TODO
+    return false;
 }
+// vim: tw=220:ts=4
diff --git a/src/runtime/ksecrets_store/ksecrets_data.h \
b/src/runtime/ksecrets_store/ksecrets_data.h index e5b8964..dca51f7 100644
--- a/src/runtime/ksecrets_store/ksecrets_data.h
+++ b/src/runtime/ksecrets_store/ksecrets_data.h
@@ -21,33 +21,48 @@
 #ifndef KSECRETS_DATA_H
 #define KSECRETS_DATA_H
 
+#include "ksecrets_crypt.h"
+
 #include <cstdint>
 #include <sys/types.h>
 
 class KSecretsFile;
 
+/**
+ * @brief Elementary secret element
+ *
+ * TODO this class uses routines from ksecrets_crypt.cpp file to handle
+ * encrypting and decrypting of the files. It would be better to define some
+ * plugin architecture, allowing users specify different encryption methods.
+ */
 struct SecretsEntity {
     SecretsEntity();
     SecretsEntity(const SecretsEntity&) = delete;
     SecretsEntity(SecretsEntity&&) = delete;
     virtual ~SecretsEntity();
 
-    enum class State: std::uint8_t {
+    enum class State : std::uint8_t {
         Empty = 0,
         Encrypted = 0x01,
         Decrypted = 0x02
     };
 
-    size_t size_;
-    State state_;
-    char* encrypted_;
-    char* unencrypted_;
+    bool isEmpty() const noexcept { return state_ == State::Empty; }
+    bool isDecrypted() const noexcept
+    {
+        return (static_cast<std::uint8_t>(state_)
+                   & static_cast<std::uint8_t>(State::Decrypted)) != 0;
+    }
 
-    bool decrypt() noexcept;
-    bool encrypt() noexcept;
+    virtual bool decrypt() noexcept;
+    virtual bool encrypt() noexcept;
 
-    bool read(KSecretsFile&) noexcept;
-    bool write(KSecretsFile&) const noexcept;
+    virtual bool read(KSecretsFile&) noexcept;
+    virtual bool write(KSecretsFile&) const noexcept;
+
+    State state_;
+    CryptBuffer encrypted_;
+    CryptBuffer unencrypted_;
 };
 
 struct SecretsCollection : public SecretsEntity {
diff --git a/src/runtime/ksecrets_store/ksecrets_file.cpp \
b/src/runtime/ksecrets_store/ksecrets_file.cpp index 7d91de3..c3dc275 100644
--- a/src/runtime/ksecrets_store/ksecrets_file.cpp
+++ b/src/runtime/ksecrets_store/ksecrets_file.cpp
@@ -37,6 +37,7 @@ KSecretsFile::KSecretsFile()
     : file_(-1)
     , locked_(false)
     , empty_(true)
+    , eof_(false)
 {
     memset(&fileHead_, 0, sizeof(fileHead_));
 }
@@ -105,6 +106,7 @@ bool KSecretsFile::readHeader()
     if (eoftest == 0) {
         // we are at EOF already, so file is empty
         empty_ = true;
+        eof_ = true;
     }
     else {
         if (-1 == lseek(file_, -bytes, SEEK_CUR)) {
@@ -139,8 +141,47 @@ int KSecretsFile::checkMAC() const
     return -1;
 }
 
-KSecretsFile::DirCollectionResult KSecretsFile::dirCollections() {
-    return DirCollectionResult();
+bool KSecretsFile::read(size_t& s) { return read(&s, sizeof(s)); }
+
+bool KSecretsFile::read(void* buf, size_t len)
+{
+    if (eof_) return false;
+    auto rres = ::read(file_, buf, len);
+    if (rres < 0)
+        return setFailState(errno);
+    if (static_cast<size_t>(rres) < len)
+        return setEOF(); // are we @ EOF?
+    return true;
+}
+
+KSecretsFile::DirCollectionResult KSecretsFile::dirCollections()
+{
+    DirCollectionResult res;
+    res.first = false;
+    res.second = nullptr;
+
+    if (empty_) {
+        return res;
+    }
+
+    readDirectory();
+    decryptEntity(directory_);
+
+    if (directory_.isDecrypted()) {
+        res.first = true;
+        res.second = &directory_;
+    }
+
+    return res;
 }
 
+bool KSecretsFile::readDirectory()
+{
+    if (empty_)
+        return false; // file is empty, don't event attempt read
+    return directory_.read(*this);
+}
+
+bool KSecretsFile::decryptEntity(SecretsEntity& entity) { return entity.decrypt(); }
+
 // vim: tw=220:ts=4
diff --git a/src/runtime/ksecrets_store/ksecrets_file.h \
b/src/runtime/ksecrets_store/ksecrets_file.h index a929cca..5ed8c79 100644
--- a/src/runtime/ksecrets_store/ksecrets_file.h
+++ b/src/runtime/ksecrets_store/ksecrets_file.h
@@ -26,6 +26,9 @@
 #include <memory>
 #include <list>
 
+/**
+ * @brief This is the secrets file format handling class
+ */
 class KSecretsFile {
 public:
     KSecretsFile();
@@ -46,6 +49,7 @@ public:
     bool readHeader() noexcept;
     bool checkMagic() noexcept;
     const char* salt() const noexcept { return fileHead_.salt_; }
+    const char* iv() const noexcept { return fileHead_.iv_; }
     int checkMAC() const noexcept;
     bool read(void* buf, size_t count);
     bool read(size_t&);
@@ -54,11 +58,18 @@ public:
     DirCollectionResult dirCollections() noexcept;
 
 private:
-    bool setFailState(int err, bool retval = false)
+    bool setFailState(int err, bool retval = false) noexcept
     {
         errno_ = err;
         return retval; // wo do like this so this function could end other methods with an \
elegant return setFailState(errno);  }
+    bool setEOF() noexcept {
+        eof_ = true;
+        return false; // this work the same as setFailState
+    }
+    bool readDirectory() noexcept;
+    bool decryptEntity(SecretsEntity&) noexcept;
+
     using Entities = std::list<SecretsEntity*>;
 
     std::string filePath_;
@@ -69,7 +80,9 @@ private:
     bool empty_;
     char fileMAC_[64];
     Entities entities_;
+    CollectionDirectory directory_;
     int errno_;
+    bool eof_;
 };
 
 #endif
diff --git a/src/runtime/ksecrets_store/ksecrets_store.cpp \
b/src/runtime/ksecrets_store/ksecrets_store.cpp index 9e3967b..8e4bf00 100644
--- a/src/runtime/ksecrets_store/ksecrets_store.cpp
+++ b/src/runtime/ksecrets_store/ksecrets_store.cpp
@@ -165,10 +165,16 @@ KSecretsStore::DirCollectionsResult \
KSecretsStorePrivate::dirCollections()  return res;
 }
 
-KSecretsStore::CreateCollectionResult KSecretsStore::createCollection(const char*) noexcept
+KSecretsStore::CreateCollectionResult KSecretsStore::createCollection(const char* collName) \
noexcept  {
-    // TODO
-    return CreateCollectionResult();
+    return d->createCollection(collName);
+}
+
+KSecretsStore::CreateCollectionResult KSecretsStorePrivate::createCollection(const std::string \
&collName) noexcept +{
+    KSecretsStore::CreateCollectionResult res;
+
+    return res;
 }
 
 KSecretsStore::ReadCollectionResult KSecretsStore::readCollection(const char*) const noexcept
diff --git a/src/runtime/ksecrets_store/ksecrets_store_p.h \
b/src/runtime/ksecrets_store/ksecrets_store_p.h index 8dee77c..c8f8aaa 100644
--- a/src/runtime/ksecrets_store/ksecrets_store_p.h
+++ b/src/runtime/ksecrets_store/ksecrets_store_p.h
@@ -64,6 +64,7 @@ public:
     KSecretsStore::SetupResult open(bool) noexcept;
     int createFile(const std::string&) noexcept;
     const char* salt() const noexcept;
+    KSecretsStore::CreateCollectionResult createCollection(const std::string&) noexcept;
     KSecretsStore::DirCollectionsResult dirCollections() noexcept;
 
     template <typename S> S setStoreStatus(S s) noexcept


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

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