Modify encryption scheme 41/48541/19
authorKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Mon, 7 Sep 2015 11:19:54 +0000 (13:19 +0200)
committerKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Mon, 26 Oct 2015 11:34:39 +0000 (12:34 +0100)
[Problem] Current encryption scheme makes it impossible to remove an entry
protected with custom user password from database.
[Solution] Encryption scheme modified. Store is responsible for encrypting data
with user password. Service encrypts it with app key. Data encrypted with old
scheme that is being read will be automatically reencrypted with new scheme.

[Verification] Run tests from upcoming commit:
ckm-tests-internal -t ENCRYPTION_SCHEME_TEST

Change-Id: I8ed514290d9e75bbc89d74b006939e3cbb0b8bd2

16 files changed:
src/manager/crypto/generic-backend/gobj.h
src/manager/crypto/generic-backend/gstore.h
src/manager/crypto/sw-backend/internals.cpp
src/manager/crypto/sw-backend/internals.h
src/manager/crypto/sw-backend/obj.cpp
src/manager/crypto/sw-backend/obj.h
src/manager/crypto/sw-backend/store.cpp
src/manager/crypto/sw-backend/store.h
src/manager/crypto/tz-backend/store.cpp
src/manager/crypto/tz-backend/store.h
src/manager/service/ckm-logic.cpp
src/manager/service/ckm-logic.h
src/manager/service/crypto-logic.cpp
src/manager/service/crypto-logic.h
src/manager/service/db-crypto.cpp
src/manager/service/db-crypto.h

index 0857a0e..1e6598f 100644 (file)
@@ -20,6 +20,7 @@
  */
 #pragma once
 #include <memory>
+#include <vector>
 
 #include <ckm/ckm-raw-buffer.h>
 #include <ckm/ckm-type.h>
@@ -58,6 +59,7 @@ public:
 
 typedef std::unique_ptr<GObj> GObjUPtr;
 typedef std::shared_ptr<GObj> GObjShPtr;
+typedef std::vector<GObjUPtr> GObjUPtrVector;
 
 } // namespace Crypto
 } // namespace CKM
index 30e177d..bcb3a57 100644 (file)
@@ -33,11 +33,21 @@ namespace Crypto {
 
 class GStore {
 public:
-    virtual GObjUPtr getObject(const Token &) { ThrowErr(Exc::Crypto::OperationNotSupported); }
-    virtual TokenPair generateAKey(const CryptoAlgorithm &) { ThrowErr(Exc::Crypto::OperationNotSupported); }
-    virtual Token generateSKey(const CryptoAlgorithm &) { ThrowErr(Exc::Crypto::OperationNotSupported); }
-    virtual Token import(DataType, const RawBuffer &) { ThrowErr(Exc::Crypto::OperationNotSupported); }
-    virtual void destroy(const Token &) { ThrowErr(Exc::Crypto::OperationNotSupported); }
+    virtual GObjUPtr getObject(const Token &, const Password &) {
+        ThrowErr(Exc::Crypto::OperationNotSupported);
+    }
+    virtual TokenPair generateAKey(const CryptoAlgorithm &, const Password &, const Password &) {
+        ThrowErr(Exc::Crypto::OperationNotSupported);
+    }
+    virtual Token generateSKey(const CryptoAlgorithm &, const Password &) {
+        ThrowErr(Exc::Crypto::OperationNotSupported);
+    }
+    virtual Token import(DataType, const RawBuffer &, const Password &) {
+        ThrowErr(Exc::Crypto::OperationNotSupported);
+    }
+    virtual void destroy(const Token &) {
+        ThrowErr(Exc::Crypto::OperationNotSupported);
+    }
     virtual ~GStore() {}
 
 protected:
index d5dfa23..6239c66 100644 (file)
@@ -368,7 +368,7 @@ int getRsaPadding(const RSAPaddingAlgorithm padAlgo) {
     return rsa_padding;
 }
 
-TokenPair createKeyPairRSA(CryptoBackend backendId, const int size)
+DataPair createKeyPairRSA(const int size)
 {
     EvpPkeyUPtr pkey;
 
@@ -396,12 +396,13 @@ TokenPair createKeyPairRSA(CryptoBackend backendId, const int size)
     }
     pkey = EvpPkeyUPtr(pkeyTmp, EVP_PKEY_free);
 
-    return std::make_pair<Token, Token>(Token(backendId, DataType(KeyType::KEY_RSA_PRIVATE), i2d(i2d_PrivateKey_bio, pkey.get())),
-                                        Token(backendId, DataType(KeyType::KEY_RSA_PUBLIC), i2d(i2d_PUBKEY_bio, pkey.get())));
+    return std::make_pair<Data, Data>(
+            {DataType(KeyType::KEY_RSA_PRIVATE), i2d(i2d_PrivateKey_bio, pkey.get())},
+            {DataType(KeyType::KEY_RSA_PUBLIC), i2d(i2d_PUBKEY_bio, pkey.get())});
 }
 
 
-TokenPair createKeyPairDSA(CryptoBackend backendId, const int size)
+DataPair createKeyPairDSA(const int size)
 {
     EvpPkeyUPtr pkey;
     EvpPkeyUPtr pparam;
@@ -449,11 +450,12 @@ TokenPair createKeyPairDSA(CryptoBackend backendId, const int size)
     }
     pkey = EvpPkeyUPtr(pkeyTmp, EVP_PKEY_free);
 
-    return std::make_pair<Token, Token>(Token(backendId, DataType(KeyType::KEY_DSA_PRIVATE), i2d(i2d_PrivateKey_bio, pkey.get())),
-                                        Token(backendId, DataType(KeyType::KEY_DSA_PUBLIC), i2d(i2d_PUBKEY_bio, pkey.get())));
+    return std::make_pair<Data, Data>(
+            {DataType(KeyType::KEY_DSA_PRIVATE), i2d(i2d_PrivateKey_bio, pkey.get())},
+            {DataType(KeyType::KEY_DSA_PUBLIC), i2d(i2d_PUBKEY_bio, pkey.get())});
 }
 
-TokenPair createKeyPairECDSA(CryptoBackend backendId, ElipticCurve type)
+DataPair createKeyPairECDSA(ElipticCurve type)
 {
     int ecCurve = NOT_DEFINED;
     EvpPkeyUPtr pkey;
@@ -511,11 +513,12 @@ TokenPair createKeyPairECDSA(CryptoBackend backendId, ElipticCurve type)
     }
     pkey = EvpPkeyUPtr(pkeyTmp, EVP_PKEY_free);
 
-    return std::make_pair<Token, Token>(Token(backendId, DataType(KeyType::KEY_ECDSA_PRIVATE), i2d(i2d_PrivateKey_bio, pkey.get())),
-                                        Token(backendId, DataType(KeyType::KEY_ECDSA_PUBLIC), i2d(i2d_PUBKEY_bio, pkey.get())));
+    return std::make_pair<Data, Data>(
+            {DataType(KeyType::KEY_ECDSA_PRIVATE), i2d(i2d_PrivateKey_bio, pkey.get())},
+            {DataType(KeyType::KEY_ECDSA_PUBLIC), i2d(i2d_PUBKEY_bio, pkey.get())});
 }
 
-Token createKeyAES(CryptoBackend backendId, const int sizeBits)
+Data createKeyAES(const int sizeBits)
 {
     // check the parameters of functions
     if(sizeBits!=128 && sizeBits!=192 && sizeBits!=256) {
@@ -530,10 +533,10 @@ Token createKeyAES(CryptoBackend backendId, const int sizeBits)
         ThrowMsg(Exc::Crypto::InternalError, "Error in AES key generation");
     }
 
-    return Token(backendId, DataType(KeyType::KEY_AES), CKM::RawBuffer(key, key+sizeBytes));
+    return { DataType(KeyType::KEY_AES), CKM::RawBuffer(key, key+sizeBytes)};
 }
 
-TokenPair generateAKey(CryptoBackend backendId, const CryptoAlgorithm &algorithm)
+DataPair generateAKey(const CryptoAlgorithm &algorithm)
 {
     validateParams<IsAsymGeneration>(algorithm);
 
@@ -542,23 +545,23 @@ TokenPair generateAKey(CryptoBackend backendId, const CryptoAlgorithm &algorithm
     {
         int keyLength = unpack<int>(algorithm, ParamName::GEN_KEY_LEN);
         if(keyType == AlgoType::RSA_GEN)
-            return createKeyPairRSA(backendId, keyLength);
+            return createKeyPairRSA(keyLength);
         else
-            return createKeyPairDSA(backendId, keyLength);
+            return createKeyPairDSA(keyLength);
     }
     else // AlgoType::ECDSA_GEN
     {
         ElipticCurve ecType = unpack<ElipticCurve>(algorithm, ParamName::GEN_EC);
-        return createKeyPairECDSA(backendId, ecType);
+        return createKeyPairECDSA(ecType);
     }
 }
 
-Token generateSKey(CryptoBackend backendId, const CryptoAlgorithm &algorithm)
+Data generateSKey(const CryptoAlgorithm &algorithm)
 {
     validateParams<IsSymGeneration>(algorithm);
 
     int keySizeBits = unpack<int>(algorithm, ParamName::GEN_KEY_LEN);
-    return createKeyAES(backendId, keySizeBits);
+    return createKeyAES(keySizeBits);
 }
 
 RawBuffer encryptDataAes(
index 3b54394..ecf52d4 100644 (file)
@@ -24,7 +24,6 @@
 #include <certificate-impl.h>
 #include <ckm/ckm-type.h>
 #include <openssl/evp.h>
-#include <token.h>
 #include <sw-backend/obj.h>
 
 #define EVP_SUCCESS 1  // DO NOTCHANGE THIS VALUE
@@ -40,13 +39,21 @@ namespace Crypto {
 namespace SW {
 namespace Internals {
 
-TokenPair createKeyPairRSA(CryptoBackend backendId, const int size);
-TokenPair createKeyPairDSA(CryptoBackend backendId, const int size);
-TokenPair createKeyPairECDSA(CryptoBackend backendId, ElipticCurve type1);
-Token     createKeyAES(CryptoBackend backendId, const int sizeBits);
+// TODO replace it with DataContainer
+struct Data {
+    DataType type;
+    RawBuffer buffer;
+};
 
-TokenPair generateAKey(CryptoBackend backendId, const CryptoAlgorithm &algorithm);
-Token generateSKey(CryptoBackend backendId, const CryptoAlgorithm &algorithm);
+typedef std::pair<Data,Data> DataPair;
+
+DataPair createKeyPairRSA(const int size);
+DataPair createKeyPairDSA(const int size);
+DataPair createKeyPairECDSA(ElipticCurve type1);
+Data     createKeyAES(const int sizeBits);
+
+DataPair generateAKey(const CryptoAlgorithm &algorithm);
+Data generateSKey(const CryptoAlgorithm &algorithm);
 
 RawBuffer symmetricEncrypt(const RawBuffer &key,
                            const CryptoAlgorithm &alg,
index 0b602a3..2a70736 100644 (file)
@@ -59,10 +59,6 @@ AlgoType key2algo(DataType type) {
 
 typedef std::unique_ptr<BIO, std::function<void(BIO*)>> BioUniquePtr;
 
-RawBuffer BData::getBinary() const {
-    return m_raw;
-}
-
 RawBuffer SKey::encrypt(const CryptoAlgorithm &alg, const RawBuffer &data)
 {
     return Internals::symmetricEncrypt(getBinary(), alg, data);
index c56354f..f8c3cfe 100644 (file)
@@ -40,7 +40,7 @@ public:
       , m_type(keyType)
     {}
 
-    virtual RawBuffer getBinary() const;
+    virtual RawBuffer getBinary() const { return m_raw; }
 protected:
     RawBuffer m_raw;
     DataType m_type;
index 0c6669a..4e944ad 100644 (file)
  */
 #include <memory>
 
+#include <openssl/rand.h>
+#include <openssl/evp.h>
+
 #include <generic-backend/exception.h>
 #include <sw-backend/obj.h>
 #include <sw-backend/store.h>
 #include <sw-backend/internals.h>
 
+#include <message-buffer.h>
+
+namespace CKM {
+namespace Crypto {
+namespace SW {
+
 namespace {
 
+const int ITERATIONS = 1024;
+const int KEY_LENGTH = 16; // length of AES key derived from password
+const int STORE_AES_GCM_TAG_SIZE = 16; // length of AES GCM tag
+
+// internal SW encryption scheme flags
+enum EncryptionScheme {
+    NONE = 0,
+    PASSWORD = 1 << 0
+};
+
 template <typename T, typename ...Args>
 std::unique_ptr<T> make_unique(Args&& ...args) {
     return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
 }
 
-} // namespace anonymous
+RawBuffer generateRandIV() {
+    RawBuffer civ(EVP_MAX_IV_LENGTH);
 
-namespace CKM {
-namespace Crypto {
-namespace SW {
+    if (1 != RAND_bytes(civ.data(), civ.size()))
+        ThrowErr(Exc::Crypto::InternalError, "RAND_bytes failed to generate IV.");
+    return civ;
+}
+
+RawBuffer passwordToKey(const Password &password, const RawBuffer &salt, size_t keySize)
+{
+    RawBuffer result(keySize);
+
+    if (1 != PKCS5_PBKDF2_HMAC_SHA1(
+                password.c_str(),
+                password.size(),
+                salt.data(),
+                salt.size(),
+                ITERATIONS,
+                result.size(),
+                result.data()))
+    {
+        ThrowErr(Exc::InternalError, "PCKS5_PKKDF2_HMAC_SHA1 failed.");
+    }
+
+    return result;
+}
+
+RawBuffer unpack(const RawBuffer& packed, const Password& pass)
+{
+    MessageBuffer buffer;
+    buffer.Push(packed);
+    int encryptionScheme = 0;
+    RawBuffer data;
+    buffer.Deserialize(encryptionScheme, data);
+
+    if (encryptionScheme == 0)
+        return data;
+
+    MessageBuffer internalBuffer;
+    internalBuffer.Push(data);
+    RawBuffer encrypted;
+    RawBuffer iv;
+    RawBuffer tag;
+
+    // serialization exceptions will be catched as CKM::Exception and will cause
+    // CKM_API_ERROR_SERVER_ERROR
+    internalBuffer.Deserialize(encrypted, iv, tag);
+
+    /*
+     * AES GCM will check data integrity and handle cases where:
+     * - wrong password is used
+     * - password is empty when it shouldn't be
+     * - password is not empty when it should be
+     */
+    RawBuffer key = passwordToKey(pass, iv, KEY_LENGTH);
+
+    RawBuffer ret;
+    try {
+        ret = Crypto::SW::Internals::decryptDataAesGcm(key, encrypted, iv, tag);
+    } catch( const Exc::Crypto::InternalError& e) {
+        ThrowErr(Exc::AuthenticationFailed, "Decryption with custom password failed");
+    }
+    return ret;
+}
+
+RawBuffer pack(const RawBuffer& data, const Password& pass)
+{
+    int scheme = EncryptionScheme::NONE;
+    RawBuffer packed = data;
+    if (!pass.empty()) {
+        RawBuffer iv = generateRandIV();
+        RawBuffer key = passwordToKey(pass, iv, KEY_LENGTH);
+
+        std::pair<RawBuffer, RawBuffer> ret;
+        try {
+            ret = Crypto::SW::Internals::encryptDataAesGcm(key, data, iv, STORE_AES_GCM_TAG_SIZE);
+        } catch( const Exc::Crypto::InternalError& e) {
+            ThrowErr(Exc::AuthenticationFailed, "Encryption with custom password failed");
+        }
+        scheme |= EncryptionScheme::PASSWORD;
+
+        // serialization exceptions will be catched as CKM::Exception and will cause
+        // CKM_API_ERROR_SERVER_ERROR
+        packed = MessageBuffer::Serialize(ret.first, iv, ret.second).Pop();
+    }
+    // encryption scheme + internal buffer
+    return MessageBuffer::Serialize(scheme, packed).Pop();
+}
+
+} // namespace anonymous
 
 Store::Store(CryptoBackend backendId)
   : GStore(backendId)
 {
 }
 
-GObjUPtr Store::getObject(const Token &token) {
+GObjUPtr Store::getObject(const Token &token, const Password &pass) {
     if (token.backendId != m_backendId) {
         ThrowErr(Exc::Crypto::WrongBackend, "Decider choose wrong backend!");
     }
 
+    RawBuffer data = unpack(token.data, pass);
+
     if (token.dataType.isKeyPrivate() || token.dataType.isKeyPublic()) {
-         return make_unique<AKey>(token.data, token.dataType);
+         return make_unique<AKey>(data, token.dataType);
     }
 
     if (token.dataType == DataType(DataType::KEY_AES)) {
-         return make_unique<SKey>(token.data, token.dataType);
+         return make_unique<SKey>(data, token.dataType);
     }
 
-    if (token.dataType.isCertificate()) {
-        return make_unique<Cert>(token.data, token.dataType);
+    if (token.dataType.isCertificate() || token.dataType.isChainCert()) {
+        return make_unique<Cert>(data, token.dataType);
     }
 
     if (token.dataType.isBinaryData()) {
-        return make_unique<BData>(token.data, token.dataType);
+        return make_unique<BData>(data, token.dataType);
     }
 
     ThrowErr(Exc::Crypto::DataTypeNotSupported,
         "This type of data is not supported by openssl backend: ", (int)token.dataType);
 }
 
-TokenPair Store::generateAKey(const CryptoAlgorithm &algorithm)
+TokenPair Store::generateAKey(const CryptoAlgorithm &algorithm,
+                              const Password &prvPass,
+                              const Password &pubPass)
 {
-    return Internals::generateAKey(m_backendId, algorithm);
+    Internals::DataPair ret = Internals::generateAKey(algorithm);
+    return std::make_pair<Token,Token>(
+            Token(m_backendId, ret.first.type, pack(ret.first.buffer, prvPass)),
+            Token(m_backendId, ret.second.type, pack(ret.second.buffer, pubPass)));
 }
 
-Token Store::generateSKey(const CryptoAlgorithm &algorithm)
+Token Store::generateSKey(const CryptoAlgorithm &algorithm, const Password &pass)
 {
-    return Internals::generateSKey(m_backendId, algorithm);
+    Internals::Data ret = Internals::generateSKey(algorithm);
+    return Token(m_backendId, ret.type, pack(ret.buffer, pass));
 }
 
-Token Store::import(DataType dataType, const RawBuffer &buffer) {
-    return Token(m_backendId, dataType, buffer);
+Token Store::import(DataType dataType, const RawBuffer &input, const Password &pass) {
+
+    RawBuffer data = pack(input, pass);
+    return Token(m_backendId, dataType, std::move(data));
 }
 
 } // namespace SW
index 552f5bc..f39d0c5 100644 (file)
@@ -31,10 +31,10 @@ class Store : public GStore {
 public:
     explicit Store(CryptoBackend backendId);
 
-    virtual GObjUPtr getObject(const Token &token);
-    virtual TokenPair generateAKey(const CryptoAlgorithm &);
-    virtual Token generateSKey(const CryptoAlgorithm &);
-    virtual Token import(DataType dataType, const RawBuffer &buffer);
+    virtual GObjUPtr getObject(const Token &, const Password &);
+    virtual TokenPair generateAKey(const CryptoAlgorithm &, const Password &, const Password &);
+    virtual Token generateSKey(const CryptoAlgorithm &, const Password &);
+    virtual Token import(DataType, const RawBuffer &, const Password &);
     virtual void destroy(const Token &){}
 };
 
index fe3fae7..1c5d58a 100644 (file)
@@ -30,15 +30,15 @@ Store::Store(CryptoBackend backendId)
   : GStore(backendId)
 {}
 
-GObjUPtr Store::getObject(const Token &) {
+GObjUPtr Store::getObject(const Token &, const  Password &) {
     ThrowErr(Exc::Crypto::OperationNotSupported, "Trust zone backend is not implemented!");
 }
 
-TokenPair Store::generateAKey(const CryptoAlgorithm &) {
+TokenPair Store::generateAKey(const CryptoAlgorithm &, const Password &, const Password &) {
     ThrowErr(Exc::Crypto::OperationNotSupported, "Trust zone backend is not implemented!");
 }
 
-Token Store::import(DataType, const RawBuffer &) {
+Token Store::import(DataType, const RawBuffer &, const Password &) {
     ThrowErr(Exc::Crypto::OperationNotSupported, "Trust zone backend is not implemented!");
 }
 
index be3595c..85e193d 100644 (file)
@@ -31,9 +31,9 @@ class Store : public GStore {
 public:
     explicit Store(CryptoBackend backendId);
 
-    virtual GObjUPtr getObject(const Token &token);
-    virtual TokenPair generateAKey(const CryptoAlgorithm &);
-    virtual Token import(DataType dataType, const RawBuffer &buffer);
+    virtual GObjUPtr getObject(const Token &, const Password &);
+    virtual TokenPair generateAKey(const CryptoAlgorithm &, const Password &, const Password &);
+    virtual Token import(DataType dataType, const RawBuffer &buffer, const Password &);
     virtual void destroy(const Token &){}
 };
 
index 728bfc5..8736fd5 100644 (file)
@@ -418,16 +418,11 @@ DB::Row CKMLogic::createEncryptedRow(
     const Policy &policy) const
 {
     Crypto::GStore& store = m_decider.getStore(dataType, policy.extractable);
-    Token token = store.import(dataType, data);
-
-    DB::Row row(std::move(token), name, label, static_cast<int>(policy.extractable));
 
     // do not encrypt data with password during cc_mode on
-    if(m_accessControl.isCCMode()) {
-        crypto.encryptRow("", row);
-    } else {
-        crypto.encryptRow(policy.password, row);
-    }
+    Token token = store.import(dataType, data, m_accessControl.isCCMode() ? "" : policy.password);
+    DB::Row row(std::move(token), name, label, static_cast<int>(policy.extractable));
+    crypto.encryptRow(row);
     return row;
 }
 
@@ -515,9 +510,10 @@ int CKMLogic::getKeyForService(
     DB::Row row;
     try {
         // Key is for internal service use. It won't be exported to the client
-        int retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST, name, label, pass, row);
+        Crypto::GObjUPtr obj;
+        int retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST, name, label, pass, obj);
         if (retCode == CKM_API_SUCCESS)
-            key = m_decider.getStore(row).getObject(row);
+            key = std::move(obj);
         return retCode;
     } catch (const DB::Crypto::Exception::Base &e) {
         LogError("DB::Crypto failed with message: " << e.GetMessage());
@@ -666,17 +662,12 @@ int CKMLogic::removeDataHelper(
 
     // destroy it in store
     for(auto& r : rows) {
-        /*
-         * TODO: If row is encrypted with user password we won't be able to decrypt it (tz id).
-         * Encryption/decryption with user password and with app key should both be done inside the
-         * store (import, getKey and generateXKey).
-         */
         try {
             handler.crypto.decryptRow(Password(), r);
+            m_decider.getStore(r).destroy(r);
         } catch (const Exc::AuthenticationFailed&) {
             LogDebug("Authentication failed when removing data. Ignored.");
         }
-        m_decider.getStore(r.dataType, r.exportable).destroy(r);
     }
 
     // delete row in db
@@ -803,6 +794,45 @@ int CKMLogic::checkDataPermissionsHelper(const Credentials &cred,
     return m_accessControl.canRead(cred, PermissionForLabel(accessorLabel, permissionRowOpt));
 }
 
+Crypto::GObjUPtr CKMLogic::rowToObject(
+    UserData& handler,
+    DB::Row row,
+    const Password& password)
+{
+    Crypto::GStore& store = m_decider.getStore(row);
+
+    Password pass = m_accessControl.isCCMode() ? "" : password;
+
+    // decrypt row
+    Crypto::GObjUPtr obj;
+    if(CryptoLogic::getSchemeVersion(row.encryptionScheme) == CryptoLogic::ENCRYPTION_V2) {
+        handler.crypto.decryptRow(Password(), row);
+
+        obj = store.getObject(row, pass);
+    } else {
+        // decrypt entirely with old scheme: b64(pass(appkey(data))) -> data
+        handler.crypto.decryptRow(pass, row);
+        // destroy it in store
+        store.destroy(row);
+
+        // import it to store with new scheme: data -> pass(data)
+        Token token = store.import(row.dataType,row.data, pass);
+
+        // get it from the store (it can be different than the data we imported into store)
+        obj = store.getObject(token, pass);
+
+        // update row with new token
+        *static_cast<Token*>(&row) = std::move(token);
+
+        // encrypt it with app key: pass(data) -> b64(appkey(pass(data))
+        handler.crypto.encryptRow(row);
+
+        // update it in db
+        handler.database.updateRow(row);
+    }
+    return obj;
+}
+
 int CKMLogic::readDataHelper(
     bool exportFlag,
     const Credentials &cred,
@@ -810,7 +840,7 @@ int CKMLogic::readDataHelper(
     const Name &name,
     const Label &label,
     const Password &password,
-    DB::RowVector &rows)
+    Crypto::GObjUPtrVector &objs)
 {
     auto &handler = selectDatabase(cred, label);
 
@@ -822,6 +852,7 @@ int CKMLogic::readDataHelper(
 
     // read rows
     DB::Crypto::Transaction transaction(&handler.database);
+    DB::RowVector rows;
     int retCode = readMultiRow(name, ownerLabel, dataType, handler.database, rows);
     if(CKM_API_SUCCESS != retCode)
         return retCode;
@@ -841,7 +872,9 @@ int CKMLogic::readDataHelper(
 
     // decrypt row
     for(auto &row : rows)
-        handler.crypto.decryptRow(password, row);
+        objs.push_back(rowToObject(handler, std::move(row), password));
+    // rowToObject may modify db
+    transaction.commit();
 
     return CKM_API_SUCCESS;
 }
@@ -853,7 +886,21 @@ int CKMLogic::readDataHelper(
     const Name &name,
     const Label &label,
     const Password &password,
-    DB::Row &row)
+    Crypto::GObjUPtr &obj)
+{
+    DataType objDataType;
+    return readDataHelper(exportFlag, cred, dataType, name, label, password, obj, objDataType);
+}
+
+int CKMLogic::readDataHelper(
+    bool exportFlag,
+    const Credentials &cred,
+    DataType dataType,
+    const Name &name,
+    const Label &label,
+    const Password &password,
+    Crypto::GObjUPtr &obj,
+    DataType& objDataType)
 {
     auto &handler = selectDatabase(cred, label);
 
@@ -865,10 +912,13 @@ int CKMLogic::readDataHelper(
 
     // read row
     DB::Crypto::Transaction transaction(&handler.database);
+    DB::Row row;
     int retCode = readSingleRow(name, ownerLabel, dataType, handler.database, row);
     if(CKM_API_SUCCESS != retCode)
         return retCode;
 
+    objDataType = row.dataType;
+
     // check access rights
     retCode = checkDataPermissionsHelper(cred, name, ownerLabel, cred.smackLabel, row, exportFlag, handler.database);
     if(CKM_API_SUCCESS != retCode)
@@ -879,8 +929,9 @@ int CKMLogic::readDataHelper(
     if(CKM_API_SUCCESS != retCode)
         return retCode;
 
-    // decrypt row
-    handler.crypto.decryptRow(password, row);
+    obj = rowToObject(handler, std::move(row), password);
+    // rowToObject may modify db
+    transaction.commit();
 
     return CKM_API_SUCCESS;
 }
@@ -895,9 +946,13 @@ RawBuffer CKMLogic::getData(
 {
     int retCode = CKM_API_SUCCESS;
     DB::Row row;
+    DataType objDataType;
 
     try {
-        retCode = readDataHelper(true, cred, dataType, name, label, password, row);
+        Crypto::GObjUPtr obj;
+        retCode = readDataHelper(true, cred, dataType, name, label, password, obj, objDataType);
+        if(retCode == CKM_API_SUCCESS)
+            row.data = std::move(obj->getBinary());
     } catch (const DB::Crypto::Exception::Base &e) {
         LogError("DB::Crypto failed with message: " << e.GetMessage());
         retCode = CKM_API_ERROR_DB_ERROR;
@@ -916,7 +971,7 @@ RawBuffer CKMLogic::getData(
     auto response = MessageBuffer::Serialize(static_cast<int>(LogicCommand::GET),
                                              commandId,
                                              retCode,
-                                             static_cast<int>(row.dataType),
+                                             static_cast<int>(objDataType),
                                              row.data);
     return response.Pop();
 }
@@ -934,27 +989,27 @@ int CKMLogic::getPKCS12Helper(
     int retCode;
 
     // read private key (mandatory)
-    DB::Row privKeyRow;
-    retCode = readDataHelper(true, cred, DataType::DB_KEY_FIRST, name, label, keyPassword, privKeyRow);
+    Crypto::GObjUPtr keyObj;
+    retCode = readDataHelper(true, cred, DataType::DB_KEY_FIRST, name, label, keyPassword, keyObj);
     if(retCode != CKM_API_SUCCESS)
         return retCode;
-    privKey = CKM::Key::create(privKeyRow.data);
+    privKey = CKM::Key::create(keyObj->getBinary());
 
     // read certificate (mandatory)
-    DB::Row certRow;
-    retCode = readDataHelper(true, cred, DataType::CERTIFICATE, name, label, certPassword, certRow);
+    Crypto::GObjUPtr certObj;
+    retCode = readDataHelper(true, cred, DataType::CERTIFICATE, name, label, certPassword, certObj);
     if(retCode != CKM_API_SUCCESS)
         return retCode;
-    cert = CKM::Certificate::create(certRow.data, DataFormat::FORM_DER);
+    cert = CKM::Certificate::create(certObj->getBinary(), DataFormat::FORM_DER);
 
     // read CA cert chain (optional)
-    DB::RowVector rawCaChain;
-    retCode = readDataHelper(true, cred, DataType::DB_CHAIN_FIRST, name, label, certPassword, rawCaChain);
+    Crypto::GObjUPtrVector caChainObjs;
+    retCode = readDataHelper(true, cred, DataType::DB_CHAIN_FIRST, name, label, certPassword, caChainObjs);
     if(retCode != CKM_API_SUCCESS &&
        retCode != CKM_API_ERROR_DB_ALIAS_UNKNOWN)
         return retCode;
-    for(auto &rawCaCert : rawCaChain)
-        caChain.push_back(CKM::Certificate::create(rawCaCert.data, DataFormat::FORM_DER));
+    for(auto &caCertObj : caChainObjs)
+        caChain.push_back(CKM::Certificate::create(caCertObj->getBinary(), DataFormat::FORM_DER));
 
     // if anything found, return it
     if(privKey || cert || caChain.size()>0)
@@ -1157,17 +1212,33 @@ int CKMLogic::createKeyAESHelper(
     const Label &label,
     const PolicySerializable &policy)
 {
+    auto &handler = selectDatabase(cred, label);
+
+    // use client label if not explicitly provided
+    const Label &ownerLabel = label.empty() ? cred.smackLabel : label;
+    if( m_accessControl.isSystemService(cred) && ownerLabel.compare(OWNER_ID_SYSTEM)!=0)
+        return CKM_API_ERROR_INPUT_PARAM;
+
+    // check if save is possible
+    DB::Crypto::Transaction transaction(&handler.database);
+    int retCode = checkSaveConditions(cred, handler, name, ownerLabel);
+    if(retCode != CKM_API_SUCCESS)
+        return retCode;
+
+    // create key in store
     CryptoAlgorithm keyGenAlgorithm;
     keyGenAlgorithm.setParam(ParamName::ALGO_TYPE, AlgoType::AES_GEN);
     keyGenAlgorithm.setParam(ParamName::GEN_KEY_LEN, size);
-    Token key = m_decider.getStore(DataType::KEY_AES, policy.extractable).generateSKey(keyGenAlgorithm);
-
-    return saveDataHelper(cred,
-                          name,
-                          label,
-                          DataType::KEY_AES,
-                          key.data,
-                          policy);
+    Token key = m_decider.getStore(DataType::KEY_AES, policy.extractable).generateSKey(keyGenAlgorithm, policy.password);
+
+    // save the data
+    DB::Row row(std::move(key), name, ownerLabel, static_cast<int>(policy.extractable));
+    handler.crypto.encryptRow(row);
+
+    handler.database.saveRow(row);
+
+    transaction.commit();
+    return CKM_API_SUCCESS;
 }
 
 
@@ -1191,32 +1262,41 @@ int CKMLogic::createKeyPairHelper(
     if(!dt.isKey())
         ThrowErr(Exc::InputParam, "Error, parameter ALGO_TYPE with wrong value.");
 
+    // use client label if not explicitly provided
+    const Label &ownerLabelPrv = labelPrivate.empty() ? cred.smackLabel : labelPrivate;
+    if( m_accessControl.isSystemService(cred) && ownerLabelPrv.compare(OWNER_ID_SYSTEM)!=0)
+        return CKM_API_ERROR_INPUT_PARAM;
+    const Label &ownerLabelPub = labelPublic.empty() ? cred.smackLabel : labelPublic;
+    if( m_accessControl.isSystemService(cred) && ownerLabelPub.compare(OWNER_ID_SYSTEM)!=0)
+        return CKM_API_ERROR_INPUT_PARAM;
+
     bool exportable = policyPrivate.extractable || policyPublic.extractable;
-    TokenPair keys = m_decider.getStore(dt, exportable).generateAKey(keyGenParams);
+    TokenPair keys = m_decider.getStore(dt, exportable).generateAKey(keyGenParams,
+                                                                     policyPrivate.password,
+                                                                     policyPublic.password);
 
     DB::Crypto::Transaction transactionPriv(&handlerPriv.database);
     // in case the same database is used for private and public - the second
     // transaction will not be executed
     DB::Crypto::Transaction transactionPub(&handlerPub.database);
 
-    int retCode = saveDataHelper(cred,
-                             namePrivate,
-                             labelPrivate,
-                             keys.first.dataType,
-                             keys.first.data,
-                             policyPrivate);
-    if (CKM_API_SUCCESS != retCode)
+    int retCode;
+    retCode = checkSaveConditions(cred, handlerPriv, namePrivate, ownerLabelPrv);
+    if(retCode != CKM_API_SUCCESS)
         return retCode;
-
-    retCode = saveDataHelper(cred,
-                             namePublic,
-                             labelPublic,
-                             keys.second.dataType,
-                             keys.second.data,
-                             policyPublic);
-    if (CKM_API_SUCCESS != retCode)
+    retCode = checkSaveConditions(cred, handlerPub, namePrivate, ownerLabelPub);
+    if(retCode != CKM_API_SUCCESS)
         return retCode;
 
+    // save the data
+    DB::Row rowPrv(std::move(keys.first), namePrivate, ownerLabelPrv, static_cast<int>(policyPrivate.extractable));
+    handlerPriv.crypto.encryptRow(rowPrv);
+    handlerPriv.database.saveRow(rowPrv);
+
+    DB::Row rowPub(std::move(keys.second), namePublic, ownerLabelPub, static_cast<int>(policyPublic.extractable));
+    handlerPub.crypto.encryptRow(rowPub);
+    handlerPub.database.saveRow(rowPub);
+
     transactionPub.commit();
     transactionPriv.commit();
     return CKM_API_SUCCESS;
@@ -1301,18 +1381,21 @@ int CKMLogic::readCertificateHelper(
 {
     DB::Row row;
     for (auto &i: labelNameVector) {
-        int ec = readDataHelper(false, cred, DataType::CERTIFICATE, i.second, i.first, Password(), row);
+        // certificates can't be protected with custom user password
+        Crypto::GObjUPtr obj;
+        int ec = readDataHelper(false, cred, DataType::CERTIFICATE, i.second, i.first, Password(), obj);
         if (ec != CKM_API_SUCCESS)
             return ec;
-        certVector.push_back(CertificateImpl(row.data, DataFormat::FORM_DER));
+
+        certVector.emplace_back(obj->getBinary(), DataFormat::FORM_DER);
 
         // try to read chain certificates (if present)
-        DB::RowVector rawCaChain;
-        ec = readDataHelper(false, cred, DataType::DB_CHAIN_FIRST, i.second, i.first, CKM::Password(), rawCaChain);
+        Crypto::GObjUPtrVector caChainObjs;
+        ec = readDataHelper(false, cred, DataType::DB_CHAIN_FIRST, i.second, i.first, CKM::Password(), caChainObjs);
         if(ec != CKM_API_SUCCESS && ec != CKM_API_ERROR_DB_ALIAS_UNKNOWN)
             return ec;
-        for(auto &rawCaCert : rawCaChain)
-            certVector.push_back(CertificateImpl(rawCaCert.data, DataFormat::FORM_DER));
+        for(auto &caCertObj : caChainObjs)
+            certVector.emplace_back(caCertObj->getBinary(), DataFormat::FORM_DER);
     }
     return CKM_API_SUCCESS;
 }
@@ -1490,9 +1573,10 @@ RawBuffer CKMLogic::createSignature(
     int retCode = CKM_API_SUCCESS;
 
     try {
-        retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST, privateKeyName, ownerLabel, password, row);
+        Crypto::GObjUPtr obj;
+        retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST, privateKeyName, ownerLabel, password, obj);
         if(retCode == CKM_API_SUCCESS) {
-            signature = m_decider.getStore(row).getObject(row)->sign(cryptoAlg, message);
+            signature = obj->sign(cryptoAlg, message);
         }
     } catch (const DB::Crypto::Exception::Base &e) {
         LogError("DB::Crypto failed with message: " << e.GetMessage());
@@ -1534,13 +1618,14 @@ RawBuffer CKMLogic::verifySignature(
         // try certificate first - looking for a public key.
         // in case of PKCS, pub key from certificate will be found first
         // rather than private key from the same PKCS.
-        retCode = readDataHelper(false, cred, DataType::CERTIFICATE, publicKeyOrCertName, ownerLabel, password, row);
+        Crypto::GObjUPtr obj;
+        retCode = readDataHelper(false, cred, DataType::CERTIFICATE, publicKeyOrCertName, ownerLabel, password, obj);
         if (retCode == CKM_API_ERROR_DB_ALIAS_UNKNOWN) {
-            retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST, publicKeyOrCertName, ownerLabel, password, row);
+            retCode = readDataHelper(false, cred, DataType::DB_KEY_FIRST, publicKeyOrCertName, ownerLabel, password, obj);
         }
 
         if (retCode == CKM_API_SUCCESS) {
-            retCode = m_decider.getStore(row).getObject(row)->verify(params, message, signature);
+            retCode = obj->verify(params, message, signature);
         }
     } catch (const Exc::Exception &e) {
         retCode = e.error();
index c07225b..d3f0c40 100644 (file)
@@ -314,6 +314,11 @@ private:
         bool exportFlag,
         DB::Crypto & database);
 
+    Crypto::GObjUPtr rowToObject(
+        UserData& handler,
+        DB::Row row,
+        const Password& password);
+
     int readDataHelper(
         bool exportFlag,
         const Credentials &cred,
@@ -321,7 +326,17 @@ private:
         const Name &name,
         const Label &label,
         const Password &password,
-        DB::Row &row);
+        Crypto::GObjUPtr &obj);
+
+    int readDataHelper(
+        bool exportFlag,
+        const Credentials &cred,
+        DataType dataType,
+        const Name &name,
+        const Label &label,
+        const Password &password,
+        Crypto::GObjUPtr &obj,
+        DataType& objDataType);
 
     int readDataHelper(
         bool exportFlag,
@@ -330,7 +345,7 @@ private:
         const Name &name,
         const Label &label,
         const Password &password,
-        DB::RowVector &rows);
+        Crypto::GObjUPtrVector &objs);
 
     int createKeyAESHelper(
         const Credentials &cred,
index 9f663c8..c7b8786 100644 (file)
@@ -23,6 +23,7 @@
 #include <iostream>
 #include <fstream>
 #include <utility>
+#include <climits>
 
 #include <stdio.h>
 #include <string.h>
 #include <generic-backend/exception.h>
 #include <sw-backend/internals.h>
 
+namespace CKM {
+
 namespace {
 
 const static int AES_CBC_KEY_SIZE = 32;
 const static int AES_GCM_TAG_SIZE = 16;
 
-} // anonymous namespace
+// Encryption scheme flags (enable/disable specific encryption type, multiple choice)
+const int ENCR_BASE64 =   1 << 0;
+const int ENCR_APPKEY =   1 << 1;
+const int ENCR_PASSWORD = 1 << 2;
 
-namespace CKM {
+// Encryption order flags (single choice)
+const int ENCR_ORDER_OFFSET = 24;
+const int ENCR_ORDER_FILTER = INT_MAX << ENCR_ORDER_OFFSET; // 0xff000000
+const int ENCR_ORDER_CLEAR = ~ENCR_ORDER_FILTER; // 0x00ffffff
+/*
+ * ENCR_ORDER_V1 - v1 encryption order. Token returned from store is encrypted with app key and
+ * optionally by custom user password. In such form it is stored in db.
+ */
+const int ENCR_ORDER_V1 = CryptoLogic::ENCRYPTION_V1 << ENCR_ORDER_OFFSET;
+/*
+ * ENCR_ORDER_V2 - v2 encryption order. Stored data is optionally encrypted by store with
+ * user password. Returned token is encrypted with app key and stored in db.
+ */
+const int ENCR_ORDER_V2 = CryptoLogic::ENCRYPTION_V2 << ENCR_ORDER_OFFSET;
+
+} // anonymous namespace
 
 CryptoLogic::CryptoLogic() {}
 
@@ -122,7 +143,7 @@ RawBuffer CryptoLogic::generateRandIV() const {
     return civ;
 }
 
-void CryptoLogic::encryptRow(const Password &password, DB::Row &row)
+void CryptoLogic::encryptRow(DB::Row &row)
 {
     try {
         DB::Row crow = row;
@@ -154,17 +175,13 @@ void CryptoLogic::encryptRow(const Password &password, DB::Row &row)
 
         crow.tag = dataPair.second;
 
-        if (!password.empty()) {
-            key = passwordToKey(password, crow.iv, AES_CBC_KEY_SIZE);
-
-            crow.data = Crypto::SW::Internals::encryptDataAes(AlgoType::AES_CBC, key, crow.data, crow.iv);
-            crow.encryptionScheme |= ENCR_PASSWORD;
-        }
-
         encBase64(crow.data);
         crow.encryptionScheme |= ENCR_BASE64;
         encBase64(crow.iv);
 
+        crow.encryptionScheme &= ENCR_ORDER_CLEAR;
+        crow.encryptionScheme |= ENCR_ORDER_V2;
+
         row = std::move(crow);
     } catch(const CKM::Base64Encoder::Exception::Base &e) {
         ThrowErr(Exc::InternalError, e.GetMessage());
@@ -173,6 +190,11 @@ void CryptoLogic::encryptRow(const Password &password, DB::Row &row)
     }
 }
 
+int CryptoLogic::getSchemeVersion(int encryptionScheme)
+{
+    return encryptionScheme >> ENCR_ORDER_OFFSET;
+}
+
 void CryptoLogic::decryptRow(const Password &password, DB::Row &row)
 {
     try {
@@ -200,16 +222,22 @@ void CryptoLogic::decryptRow(const Password &password, DB::Row &row)
             decBase64(crow.data);
         }
 
-        if (crow.encryptionScheme & ENCR_PASSWORD) {
-            key = passwordToKey(password, crow.iv, AES_CBC_KEY_SIZE);
-            crow.data = Crypto::SW::Internals::decryptDataAes(AlgoType::AES_CBC, key, crow.data, crow.iv);
+        if((crow.encryptionScheme >> ENCR_ORDER_OFFSET) == ENCR_ORDER_V2) {
+            if (crow.encryptionScheme & ENCR_APPKEY) {
+                key = m_keyMap[crow.ownerLabel];
+                crow.data = Crypto::SW::Internals::decryptDataAesGcm(key, crow.data, crow.iv, crow.tag);
+            }
+        } else {
+            if (crow.encryptionScheme & ENCR_PASSWORD) {
+                key = passwordToKey(password, crow.iv, AES_CBC_KEY_SIZE);
+                crow.data = Crypto::SW::Internals::decryptDataAes(AlgoType::AES_CBC, key, crow.data, crow.iv);
+            }
+
+            if (crow.encryptionScheme & ENCR_APPKEY) {
+                key = m_keyMap[crow.ownerLabel];
+                crow.data = Crypto::SW::Internals::decryptDataAesGcm(key, crow.data, crow.iv, crow.tag);
+            }
         }
-
-        if (crow.encryptionScheme & ENCR_APPKEY) {
-            key = m_keyMap[crow.ownerLabel];
-            crow.data = Crypto::SW::Internals::decryptDataAesGcm(key, crow.data, crow.iv, crow.tag);
-        }
-
         if (static_cast<int>(crow.data.size()) < crow.dataSize) {
             ThrowErr(Exc::AuthenticationFailed, "Decrypted row size mismatch");
         }
index b734bb4..61c582e 100644 (file)
@@ -38,18 +38,38 @@ public:
     virtual ~CryptoLogic(){}
 
     void decryptRow(const Password &password, DB::Row &row);
-    void encryptRow(const Password &password, DB::Row &row);
+    void encryptRow(DB::Row &row);
+
+    static int getSchemeVersion(int encryptionScheme);
 
     bool haveKey(const Label &smackLabel);
     void pushKey(const Label &smackLabel,
                  const RawBuffer &applicationKey);
     void removeKey(const Label &smackLabel);
 
+    static const int ENCRYPTION_V1 = 0;
+    static const int ENCRYPTION_V2 = 1;
+
 private:
+    // Encryption scheme flags (enable/disable specific encryption type, multiple choice)
     static const int ENCR_BASE64 =   1 << 0;
     static const int ENCR_APPKEY =   1 << 1;
     static const int ENCR_PASSWORD = 1 << 2;
 
+    // Encryption order flags (single choice)
+    static const int ENCR_ORDER_CLEAR = 0x00ffffff;
+    static const int ENCR_ORDER_FILTER = ~ENCR_ORDER_CLEAR;
+    /*
+     * ENCR_ORDER_V1 - v1 encryption order. Token returned from store is encrypted with app key and
+     * optionally by custom user password. Is such form it is stored in db.
+     */
+    static const int ENCR_ORDER_V1 = ENCR_ORDER_CLEAR + 0;
+    /*
+     * ENCR_ORDER_V2 - v2 encryption order. Stored data is optionally encrypted by store with
+     * user password. Returned token is encrypted with app key and stored in db.
+     */
+    static const int ENCR_ORDER_V2 = ENCR_ORDER_CLEAR + 1;
+
     std::map<Label, RawBuffer> m_keyMap;
 
     RawBuffer generateRandIV() const;
index 8a5b57b..99c0155 100644 (file)
@@ -94,6 +94,17 @@ namespace {
             "          ?009"
             "         );";
 
+    const char *DB_CMD_OBJECT_UPDATE =
+            "UPDATE OR FAIL OBJECTS SET"
+            "   algorithmType = ?003,"
+            "   encryptionScheme = ?004,"
+            "   iv = ?005,"
+            "   dataSize = ?006,"
+            "   data = ?007,"
+            "   tag = ?008"
+            "   WHERE idx IN (SELECT idx FROM NAMES WHERE name=?101 and label=?102)"
+            "   AND dataType = ?002;";
+
     const char *DB_CMD_OBJECT_SELECT_BY_NAME_AND_LABEL =
             "SELECT * FROM [join_name_object_tables] "
             " WHERE (dataType BETWEEN ?001 AND ?002) "
@@ -388,6 +399,21 @@ namespace DB {
                 "Couldn't save Row");
     }
 
+    void Crypto::updateRow(const Row &row) {
+        Try {
+            // transaction is present in the layer above
+            ObjectTable objectTable(this->m_connection);
+            objectTable.updateRow(row);
+            return;
+        } Catch(SqlConnection::Exception::SyntaxError) {
+            LogError("Couldn't prepare update statement");
+        } Catch(SqlConnection::Exception::InternalError) {
+            LogError("Couldn't execute update statement");
+        }
+        ThrowMsg(Crypto::Exception::InternalError,
+                "Couldn't update Row");
+    }
+
     bool Crypto::deleteRow(
             const Name &name,
             const Label &ownerLabel)
@@ -826,6 +852,25 @@ namespace DB {
 
         insertObjectCommand->Step();
     }
+
+    void Crypto::ObjectTable::updateRow(const Row &row)
+    {
+        SqlConnection::DataCommandUniquePtr updateObjectCommand =
+                m_connection->PrepareDataCommand(DB_CMD_OBJECT_UPDATE);
+        updateObjectCommand->BindInteger(2, static_cast<int>(row.dataType));
+        updateObjectCommand->BindInteger(3, static_cast<int>(row.algorithmType));
+        updateObjectCommand->BindInteger(4, row.encryptionScheme);
+        updateObjectCommand->BindBlob   (5, row.iv);
+        updateObjectCommand->BindInteger(6, row.dataSize);
+        updateObjectCommand->BindBlob   (7, row.data);
+        updateObjectCommand->BindBlob   (8, row.tag);
+
+        // name table reference
+        updateObjectCommand->BindString (101, row.name.c_str());
+        updateObjectCommand->BindString (102, row.ownerLabel.c_str());
+
+        updateObjectCommand->Step();
+    }
 } // namespace DB
 } // namespace CKM
 
index c83b1e1..dae0031 100644 (file)
@@ -72,6 +72,9 @@ namespace DB {
                     const Label &owner,
                     const RowVector &rows);
 
+            void updateRow(
+                    const Row &row);
+
             bool isNameLabelPresent(
                     const Name &name,
                     const Label &owner) const;
@@ -277,6 +280,8 @@ namespace DB {
 
                 void addRow(
                         const Row &row);
+                void updateRow(
+                        const Row &row);
 
             private:
                 SqlConnection* m_connection;