f465add8f18b4c7d3de516ae6a412b407d8f98fd
[platform/core/security/key-manager.git] / src / manager / service / db-crypto.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 /*
17  * @file        db-crypto.cpp
18  * @author      Zofia Abramowska (z.abramowska@samsung.com)
19  * @version     1.0
20  * @brief       Implementation of encrypted db access layer
21  */
22
23 #include <db-crypto.h>
24 #include <dpl/db/sql_connection.h>
25 #include <dpl/log/log.h>
26 #include <ckm/ckm-error.h>
27
28 #pragma GCC diagnostic push
29 #pragma GCC diagnostic warning "-Wdeprecated-declarations"
30
31 namespace {
32     const char *TABLE_NAME          = "NAME_TABLE";
33     const char *TABLE_OBJECT        = "OBJECT_TABLE";
34     const char *TABLE_KEY           = "KEY_TABLE";
35     const char *TABLE_PERMISSION    = "PERMISSION_TABLE";
36
37     const char *DB_CMD_NAME_CREATE =
38             "CREATE TABLE NAME_TABLE("
39             "   name TEXT NOT NULL,"
40             "   label TEXT NOT NULL,"
41             "   idx INTEGER PRIMARY KEY AUTOINCREMENT,"
42             "   UNIQUE(name, label)"
43             "); CREATE INDEX name_index_idx ON NAME_TABLE(idx);";
44
45     const char *DB_CMD_NAME_INSERT =
46             "INSERT INTO NAME_TABLE("
47             "   name, label) "
48             "   VALUES(?101, ?102);";
49
50     // any idea how to do it using constexpr ?
51     #define DB_CMD_NAME_LOOKUP_IDX \
52             "(SELECT idx FROM NAME_TABLE WHERE name=?101 and label=?102)"
53
54     const char *DB_CMD_NAME_COUNT_ROWS =
55             "SELECT COUNT(idx) FROM NAME_TABLE WHERE name=?101 AND label=?102;";
56
57     const char *DB_CMD_NAME_DELETE =
58             "DELETE FROM NAME_TABLE WHERE name=?101 AND label=?102;";
59
60     const char *DB_CMD_NAME_DELETE_BY_LABEL =
61             "DELETE FROM NAME_TABLE WHERE label=?102;";
62
63     const char *DB_CMD_OBJECT_CREATE =
64             "CREATE TABLE OBJECT_TABLE("
65             "   exportable INTEGER NOT NULL,"
66             "   dataType INTEGER NOT NULL,"
67             "   algorithmType INTEGER NOT NULL,"
68             "   encryptionScheme INTEGER NOT NULL,"
69             "   iv BLOB NOT NULL,"
70             "   dataSize INTEGER NOT NULL,"
71             "   data BLOB NOT NULL,"
72             "   tag BLOB NOT NULL,"
73             "   idx INTEGER NOT NULL,"
74             "   FOREIGN KEY(idx) REFERENCES NAME_TABLE(idx) ON DELETE CASCADE,"
75             "   PRIMARY KEY(idx, dataType)"
76             ");"; // TODO: index and performance tests
77
78     const char *DB_CMD_OBJECT_INSERT =
79             "INSERT INTO OBJECT_TABLE("
80             "   exportable, dataType,"
81             "   algorithmType, encryptionScheme,"
82             "   iv, dataSize, data, tag, idx) "
83             "   VALUES(?001, ?002, ?003, ?004, ?005, "
84             "          ?006, ?007, ?008, " DB_CMD_NAME_LOOKUP_IDX ");";
85
86     const char *DB_CMD_OBJECT_SELECT_BY_NAME_AND_LABEL =
87             "SELECT * FROM OBJECT_TABLE WHERE "
88             " (dataType BETWEEN ?001 AND ?002) AND "
89             " idx=" DB_CMD_NAME_LOOKUP_IDX ";";
90
91
92     const char *DB_CMD_KEY_CREATE =
93             "CREATE TABLE KEY_TABLE("
94             "   label TEXT PRIMARY KEY,"
95             "   key BLOB NOT NULL"
96             ");";
97
98     const char *DB_CMD_KEY_INSERT =
99             "INSERT INTO KEY_TABLE(label, key) VALUES (?, ?);";
100     const char *DB_CMD_KEY_SELECT =
101             "SELECT key FROM KEY_TABLE WHERE label=?;";
102     const char *DB_CMD_KEY_DELETE =
103             "DELETE FROM KEY_TABLE WHERE label=?";
104
105
106     const char *DB_CMD_PERMISSION_CREATE =
107             "CREATE TABLE PERMISSION_TABLE("
108             "   label TEXT NOT NULL,"
109             "   accessFlags TEXT NOT NULL,"
110             "   idx INTEGER NOT NULL,"
111             "   FOREIGN KEY(idx) REFERENCES NAME_TABLE(idx) ON DELETE CASCADE,"
112             "   PRIMARY KEY(label, idx)"
113             "); CREATE INDEX perm_index_idx ON PERMISSION_TABLE(idx);"; // based on ANALYZE and performance test result
114
115     const char *DB_CMD_PERMISSION_SET =
116             "REPLACE INTO PERMISSION_TABLE(label, accessFlags, idx) "
117             " VALUES (?001, ?002, " DB_CMD_NAME_LOOKUP_IDX ");";
118
119     const char *DB_CMD_PERMISSION_SELECT =
120             "SELECT accessFlags FROM PERMISSION_TABLE WHERE label=?001 AND "
121             " idx=" DB_CMD_NAME_LOOKUP_IDX ";";
122
123     const char *DB_CMD_PERMISSION_DELETE =
124             "DELETE FROM PERMISSION_TABLE WHERE label=?001 AND "
125             " idx=" DB_CMD_NAME_LOOKUP_IDX ";";
126
127
128     /*
129      * GROUP BY is necessary because of the following case:
130      * -There are several permissions to L1, N1 (label, name) from other accessors. When listing
131      *  objects accessible by L1 the query will produce one result (L1, N1) for each allowed
132      *  accessor but GROUP BY will reduce them to one so L1 will have (L1, N1) on its list only once
133      */
134     const char *DB_CMD_NAME_SELECT_BY_TYPE_AND_PERMISSION =
135             "SELECT N.label, N.name FROM NAME_TABLE AS N "
136             " JOIN OBJECT_TABLE AS O ON O.idx=N.idx "
137             " LEFT JOIN PERMISSION_TABLE AS P ON P.idx=N.idx "
138             " WHERE O.dataType>=?001 AND O.dataType<=?002 AND "
139             " ((N.label=?003) OR (P.label=?003 AND P.accessFlags IS NOT NULL)) GROUP BY N.idx;";
140 }
141
142 namespace CKM {
143 using namespace DB;
144     DBCrypto::DBCrypto(const std::string& path, const RawBuffer &rawPass)
145     {
146         m_connection = NULL;
147         m_inUserTransaction = false;
148         Try {
149             m_connection = new SqlConnection(path, SqlConnection::Flag::Option::CRW);
150             m_connection->SetKey(rawPass);
151             m_connection->ExecCommand("VACUUM;");
152             initDatabase();
153         } Catch(SqlConnection::Exception::ConnectionBroken) {
154             LogError("Couldn't connect to database: " << path);
155             ReThrow(DBCrypto::Exception::InternalError);
156         } Catch(SqlConnection::Exception::InvalidArguments) {
157             LogError("Couldn't set the key for database");
158             ReThrow(DBCrypto::Exception::InternalError);
159         } Catch(SqlConnection::Exception::SyntaxError) {
160             LogError("Couldn't initiate the database");
161             ReThrow(DBCrypto::Exception::InternalError);
162         } Catch(SqlConnection::Exception::InternalError) {
163             LogError("Couldn't create the database");
164             ReThrow(DBCrypto::Exception::InternalError);
165         }
166     }
167
168     DBCrypto::DBCrypto(DBCrypto &&other) :
169             m_connection(other.m_connection),
170             m_inUserTransaction(other.m_inUserTransaction)
171     {
172         other.m_connection = NULL;
173         other.m_inUserTransaction = false;
174     }
175
176     DBCrypto::~DBCrypto() {
177         delete m_connection;
178     }
179
180     DBCrypto& DBCrypto::operator=(DBCrypto&& other) {
181         if (this == &other)
182             return *this;
183         delete m_connection;
184
185         m_connection = other.m_connection;
186         other.m_connection = NULL;
187
188         m_inUserTransaction = other.m_inUserTransaction;
189         other.m_inUserTransaction = false;
190
191         return *this;
192     }
193
194     void DBCrypto::createTable(
195             const char* create_cmd,
196             const char *table_name)
197     {
198         Try {
199             m_connection->ExecCommand(create_cmd);
200         } Catch(SqlConnection::Exception::SyntaxError) {
201             LogError("Couldn't create table : " << table_name << "!");
202             throw;
203         } Catch(SqlConnection::Exception::InternalError) {
204             LogError("Sqlite got into infinite busy state");
205             throw;
206         }
207     }
208
209     void DBCrypto::initDatabase() {
210         Transaction transaction(this);
211         if(!m_connection->CheckTableExist(TABLE_NAME)) {
212             createTable(DB_CMD_NAME_CREATE, TABLE_NAME);
213         }
214         if(!m_connection->CheckTableExist(TABLE_OBJECT)) {
215             createTable(DB_CMD_OBJECT_CREATE, TABLE_OBJECT);
216         }
217         if(!m_connection->CheckTableExist(TABLE_KEY)) {
218             createTable(DB_CMD_KEY_CREATE, TABLE_KEY);
219         }
220         if(!m_connection->CheckTableExist(TABLE_PERMISSION)) {
221             createTable(DB_CMD_PERMISSION_CREATE, TABLE_PERMISSION);
222         }
223         transaction.commit();
224     }
225
226     bool DBCrypto::isNameLabelPresent(const Name &name, const Label &owner) const {
227         Try {
228             NameTable nameTable(this->m_connection);
229             return nameTable.isPresent(name, owner);
230         } Catch(SqlConnection::Exception::SyntaxError) {
231             LogError("Couldn't prepare insert statement");
232         } Catch(SqlConnection::Exception::InternalError) {
233             LogError("Couldn't execute insert statement");
234         }
235         ThrowMsg(DBCrypto::Exception::InternalError,
236                 "Couldn't check if name and label pair is present");
237     }
238
239     void DBCrypto::saveDBRow(const DBRow &row){
240         Try {
241             // transaction is present in the layer above
242             NameTable nameTable(this->m_connection);
243             ObjectTable objectTable(this->m_connection);
244             nameTable.addRow(row.name, row.ownerLabel);
245             objectTable.addRow(row);
246             return;
247         } Catch(SqlConnection::Exception::SyntaxError) {
248             LogError("Couldn't prepare insert statement");
249         } Catch(SqlConnection::Exception::InternalError) {
250             LogError("Couldn't execute insert statement");
251         }
252         ThrowMsg(DBCrypto::Exception::InternalError,
253                 "Couldn't save DBRow");
254     }
255
256     bool DBCrypto::deleteDBRow(
257             const Name &name,
258             const Label &ownerLabel)
259     {
260         Try {
261             // transaction is present in the layer above
262             NameTable nameTable(this->m_connection);
263             if(nameTable.isPresent(name, ownerLabel))
264             {
265                 nameTable.deleteRow(name, ownerLabel);
266                 return true;
267             }
268             return false;
269         } Catch (SqlConnection::Exception::SyntaxError) {
270             LogError("Couldn't prepare delete statement");
271         } Catch (SqlConnection::Exception::InternalError) {
272             LogError("Couldn't execute delete statement");
273         }
274         ThrowMsg(DBCrypto::Exception::InternalError,
275                 "Couldn't delete DBRow for name " << name << " using ownerLabel " << ownerLabel);
276     }
277
278     DBRow DBCrypto::getRow(
279             const Name &name,
280             const Label &ownerLabel,
281             const SqlConnection::DataCommandUniquePtr &selectCommand) const {
282         DBRow row;
283         row.name = name;
284         row.ownerLabel = ownerLabel;
285         row.exportable = selectCommand->GetColumnInteger(0);
286         row.dataType = DBDataType(selectCommand->GetColumnInteger(1));
287         row.algorithmType = static_cast<DBCMAlgType>(selectCommand->GetColumnInteger(2));
288         row.encryptionScheme = selectCommand->GetColumnInteger(3);
289         row.iv = selectCommand->GetColumnBlob(4);
290         row.dataSize = selectCommand->GetColumnInteger(5);
291         row.data = selectCommand->GetColumnBlob(6);
292         row.tag = selectCommand->GetColumnBlob(7);
293         return row;
294     }
295
296     PermissionOptional DBCrypto::getPermissionRow(
297         const Name &name,
298         const Label &ownerLabel,
299         const Label &accessorLabel) const
300     {
301         Try {
302             PermissionTable permissionTable(this->m_connection);
303             return permissionTable.getPermissionRow(name, ownerLabel, accessorLabel);
304         } Catch (SqlConnection::Exception::InvalidColumn) {
305             LogError("Select statement invalid column error");
306         } Catch (SqlConnection::Exception::SyntaxError) {
307             LogError("Couldn't prepare select statement");
308         } Catch (SqlConnection::Exception::InternalError) {
309             LogError("Couldn't execute select statement");
310         }
311         return PermissionOptional();
312     }
313
314     DBCrypto::DBRowOptional DBCrypto::getDBRow(
315         const Name &name,
316         const Label &ownerLabel,
317         DBDataType type)
318     {
319         return getDBRow(name, ownerLabel, type, type);
320     }
321
322     DBCrypto::DBRowOptional DBCrypto::getDBRow(
323         const Name &name,
324         const Label &ownerLabel,
325         DBDataType typeRangeStart,
326         DBDataType typeRangeStop)
327     {
328         Try {
329             SqlConnection::DataCommandUniquePtr selectCommand =
330                     m_connection->PrepareDataCommand(DB_CMD_OBJECT_SELECT_BY_NAME_AND_LABEL);
331             selectCommand->BindInteger(1, typeRangeStart);
332             selectCommand->BindInteger(2, typeRangeStop);
333
334             // name table reference
335             selectCommand->BindString (101, name.c_str());
336             selectCommand->BindString (102, ownerLabel.c_str());
337
338             if(selectCommand->Step())
339             {
340                 // extract data
341                 DBRow current_row = getRow(name, ownerLabel, selectCommand);
342
343                 // all okay, proceed
344                 return DBRowOptional(current_row);
345             } else {
346                 return DBRowOptional();
347             }
348         } Catch (SqlConnection::Exception::InvalidColumn) {
349             LogError("Select statement invalid column error");
350         } Catch (SqlConnection::Exception::SyntaxError) {
351             LogError("Couldn't prepare select statement");
352         } Catch (SqlConnection::Exception::InternalError) {
353             LogError("Couldn't execute select statement");
354         }
355         ThrowMsg(DBCrypto::Exception::InternalError,
356                 "Couldn't get row of type <" <<
357                 static_cast<int>(typeRangeStart) << "," <<
358                 static_cast<int>(typeRangeStop)  << ">" <<
359                 " name " << name << " with owner label " << ownerLabel);
360     }
361
362     void DBCrypto::listNames(
363         const Label &smackLabel,
364         LabelNameVector& labelNameVector,
365         DBDataType type)
366     {
367         listNames(smackLabel, labelNameVector, type, type);
368     }
369
370     void DBCrypto::listNames(
371         const Label &smackLabel,
372         LabelNameVector& labelNameVector,
373         DBDataType typeRangeStart,
374         DBDataType typeRangeStop)
375     {
376         Try{
377             Transaction transaction(this);
378             SqlConnection::DataCommandUniquePtr selectCommand =
379                             m_connection->PrepareDataCommand(DB_CMD_NAME_SELECT_BY_TYPE_AND_PERMISSION);
380             selectCommand->BindInteger(1, static_cast<int>(typeRangeStart));
381             selectCommand->BindInteger(2, static_cast<int>(typeRangeStop));
382             selectCommand->BindString(3, smackLabel.c_str());
383
384             while(selectCommand->Step()) {
385                 Label ownerLabel = selectCommand->GetColumnString(0);
386                 Name name = selectCommand->GetColumnString(1);
387                 labelNameVector.push_back(std::make_pair(ownerLabel, name));
388             }
389             return;
390         } Catch (SqlConnection::Exception::InvalidColumn) {
391             LogError("Select statement invalid column error");
392         } Catch (SqlConnection::Exception::SyntaxError) {
393             LogError("Couldn't prepare select statement");
394         } Catch (SqlConnection::Exception::InternalError) {
395             LogError("Couldn't execute select statement");
396         }
397         ThrowMsg(DBCrypto::Exception::InternalError,
398                 "Couldn't list names of type <" <<
399                 static_cast<int>(typeRangeStart) << "," <<
400                 static_cast<int>(typeRangeStop)  << ">" <<
401                 " accessible to client label " << smackLabel);
402     }
403
404
405
406     void DBCrypto::saveKey(
407             const Label& label,
408             const RawBuffer &key)
409     {
410         Try {
411             SqlConnection::DataCommandUniquePtr insertCommand =
412                     m_connection->PrepareDataCommand(DB_CMD_KEY_INSERT);
413             insertCommand->BindString(1, label.c_str());
414             insertCommand->BindBlob(2, key);
415             insertCommand->Step();
416             return;
417         } Catch (SqlConnection::Exception::SyntaxError) {
418             LogError("Couldn't prepare insert key statement");
419         } Catch (SqlConnection::Exception::InternalError) {
420             LogError("Couldn't execute insert statement");
421         }
422         ThrowMsg(DBCrypto::Exception::InternalError,
423                 "Couldn't save key for label " << label);
424     }
425
426     DBCrypto::RawBufferOptional DBCrypto::getKey(const Label& label)
427     {
428         Try {
429             SqlConnection::DataCommandUniquePtr selectCommand =
430                     m_connection->PrepareDataCommand(DB_CMD_KEY_SELECT);
431             selectCommand->BindString(1, label.c_str());
432
433             if (selectCommand->Step()) {
434                 return RawBufferOptional(
435                         selectCommand->GetColumnBlob(0));
436             } else {
437                 return RawBufferOptional();
438             }
439
440         } Catch (SqlConnection::Exception::InvalidColumn) {
441             LogError("Select statement invalid column error");
442         } Catch (SqlConnection::Exception::SyntaxError) {
443             LogError("Couldn't prepare insert key statement");
444         } Catch (SqlConnection::Exception::InternalError) {
445             LogError("Couldn't execute insert statement");
446         }
447         ThrowMsg(DBCrypto::Exception::InternalError,
448                 "Couldn't get key for label " << label);
449     }
450
451     void DBCrypto::deleteKey(const Label& label) {
452         Try {
453             Transaction transaction(this);
454
455             SqlConnection::DataCommandUniquePtr deleteCommand =
456                     m_connection->PrepareDataCommand(DB_CMD_KEY_DELETE);
457             deleteCommand->BindString(1, label.c_str());
458             deleteCommand->Step();
459
460             NameTable nameTable(this->m_connection);
461             nameTable.deleteAllRows(label);
462
463             transaction.commit();
464             return;
465         } Catch (SqlConnection::Exception::SyntaxError) {
466             LogError("Couldn't prepare insert key statement");
467         } Catch (SqlConnection::Exception::InternalError) {
468             LogError("Couldn't execute insert statement");
469         }
470         ThrowMsg(DBCrypto::Exception::InternalError,
471                 "Couldn't delete key for label " << label);
472     }
473
474     void DBCrypto::setPermission(
475             const Name &name,
476             const Label& ownerLabel,
477             const Label& accessorLabel,
478             const Permission permissions)
479     {
480         Try {
481             PermissionTable permissionTable(this->m_connection);
482             permissionTable.setPermission(name, ownerLabel, accessorLabel, permissions);
483             return;
484         } Catch (SqlConnection::Exception::SyntaxError) {
485             LogError("Couldn't prepare set statement");
486         } Catch (SqlConnection::Exception::InternalError) {
487             LogError("Couldn't execute set statement");
488         }
489         ThrowMsg(DBCrypto::Exception::InternalError,
490                 "Couldn't set permissions for name " << name );
491     }
492
493     void DBCrypto::PermissionTable::setPermission(
494             const Name &name,
495             const Label& ownerLabel,
496             const Label& accessorLabel,
497             const Permission permissions)
498     {
499         if(permissions == Permission::NONE)
500         {
501             // clear permissions
502             SqlConnection::DataCommandUniquePtr deletePermissionCommand =
503                 m_connection->PrepareDataCommand(DB_CMD_PERMISSION_DELETE);
504             deletePermissionCommand->BindString(1, accessorLabel.c_str());
505             deletePermissionCommand->BindString(101, name.c_str());
506             deletePermissionCommand->BindString(102, ownerLabel.c_str());
507             deletePermissionCommand->Step();
508         }
509         else
510         {
511             // add new permissions
512             SqlConnection::DataCommandUniquePtr setPermissionCommand =
513                 m_connection->PrepareDataCommand(DB_CMD_PERMISSION_SET);
514             setPermissionCommand->BindString(1, accessorLabel.c_str());
515             setPermissionCommand->BindString(2, toDBPermission(permissions));
516             setPermissionCommand->BindString(101, name.c_str());
517             setPermissionCommand->BindString(102, ownerLabel.c_str());
518             setPermissionCommand->Step();
519         }
520     }
521
522     PermissionOptional DBCrypto::PermissionTable::getPermissionRow(
523             const Name &name,
524             const Label &ownerLabel,
525             const Label &accessorLabel) const
526     {
527         SqlConnection::DataCommandUniquePtr selectCommand =
528                 m_connection->PrepareDataCommand(DB_CMD_PERMISSION_SELECT);
529         selectCommand->BindString(1, accessorLabel.c_str());
530
531         // name table reference
532         selectCommand->BindString(101, name.c_str());
533         selectCommand->BindString(102, ownerLabel.c_str());
534
535         if(selectCommand->Step())
536         {
537             // there is entry for the <name, ownerLabel> pair
538             return PermissionOptional(toPermission(selectCommand->GetColumnString(0)));
539         }
540         return PermissionOptional();
541     }
542
543     void DBCrypto::NameTable::addRow(
544             const Name &name,
545             const Label &ownerLabel)
546     {
547         // insert NAME_TABLE item
548         SqlConnection::DataCommandUniquePtr insertNameCommand =
549                 m_connection->PrepareDataCommand(DB_CMD_NAME_INSERT);
550         insertNameCommand->BindString (101, name.c_str());
551         insertNameCommand->BindString (102, ownerLabel.c_str());
552         insertNameCommand->Step();
553     }
554
555     void DBCrypto::NameTable::deleteRow(
556             const Name &name,
557             const Label &ownerLabel)
558     {
559         SqlConnection::DataCommandUniquePtr deleteCommand =
560                 m_connection->PrepareDataCommand(DB_CMD_NAME_DELETE);
561         deleteCommand->BindString(101, name.c_str());
562         deleteCommand->BindString(102, ownerLabel.c_str());
563
564         // Step() result code does not provide information whether
565         // anything was removed.
566         deleteCommand->Step();
567     }
568
569     void DBCrypto::NameTable::deleteAllRows(const Label &ownerLabel)
570     {
571         SqlConnection::DataCommandUniquePtr deleteData =
572                 m_connection->PrepareDataCommand(DB_CMD_NAME_DELETE_BY_LABEL);
573         deleteData->BindString(102, ownerLabel.c_str());
574
575         // Step() result code does not provide information whether
576         // anything was removed.
577         deleteData->Step();
578     }
579
580     bool DBCrypto::NameTable::isPresent(const Name &name, const Label &ownerLabel) const
581     {
582         SqlConnection::DataCommandUniquePtr checkCmd =
583                 m_connection->PrepareDataCommand(DB_CMD_NAME_COUNT_ROWS);
584         checkCmd->BindString(101, name.c_str());
585         checkCmd->BindString(102, ownerLabel.c_str());
586         if(checkCmd->Step()) {
587             int element_count = checkCmd->GetColumnInteger(0);
588             LogDebug("Item name: " << name  << " ownerLabel: " << ownerLabel <<
589                      " hit count: " << element_count);
590             if(element_count > 0)
591                 return true;
592         }
593         return false;
594     }
595
596     void DBCrypto::ObjectTable::addRow(const DBRow &row)
597     {
598         SqlConnection::DataCommandUniquePtr insertObjectCommand =
599                 m_connection->PrepareDataCommand(DB_CMD_OBJECT_INSERT);
600         insertObjectCommand->BindInteger(1, row.exportable);
601         insertObjectCommand->BindInteger(2, static_cast<int>(row.dataType));
602         insertObjectCommand->BindInteger(3, static_cast<int>(row.algorithmType));
603         insertObjectCommand->BindInteger(4, row.encryptionScheme);
604         insertObjectCommand->BindBlob   (5, row.iv);
605         insertObjectCommand->BindInteger(6, row.dataSize);
606         insertObjectCommand->BindBlob   (7, row.data);
607         insertObjectCommand->BindBlob   (8, row.tag);
608
609         // name table reference
610         insertObjectCommand->BindString (101, row.name.c_str());
611         insertObjectCommand->BindString (102, row.ownerLabel.c_str());
612
613         insertObjectCommand->Step();
614     }
615 } // namespace CKM
616
617 #pragma GCC diagnostic pop