NS-3 based Named Data Networking (NDN) simulator
ndnSIM 2.5: NDN, CCN, CCNx, content centric networks
API Documentation
pib-sqlite3.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2013-2022 Regents of the University of California.
4  *
5  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6  *
7  * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8  * terms of the GNU Lesser General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later version.
10  *
11  * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13  * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14  *
15  * You should have received copies of the GNU General Public License and GNU Lesser
16  * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17  * <http://www.gnu.org/licenses/>.
18  *
19  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20  */
21 
26 
27 #include <sqlite3.h>
28 
29 #include <boost/filesystem/operations.hpp>
30 #include <boost/filesystem/path.hpp>
31 
32 namespace ndn {
33 namespace security {
34 namespace pib {
35 
36 using util::Sqlite3Statement;
37 
38 static const std::string INITIALIZATION = R"SQL(
39 CREATE TABLE IF NOT EXISTS
40  tpmInfo(
41  tpm_locator BLOB
42  );
43 
44 CREATE TABLE IF NOT EXISTS
45  identities(
46  id INTEGER PRIMARY KEY,
47  identity BLOB NOT NULL,
48  is_default INTEGER DEFAULT 0
49  );
50 
51 CREATE UNIQUE INDEX IF NOT EXISTS
52  identityIndex ON identities(identity);
53 
54 CREATE TRIGGER IF NOT EXISTS
55  identity_default_before_insert_trigger
56  BEFORE INSERT ON identities
57  FOR EACH ROW
58  WHEN NEW.is_default=1
59  BEGIN
60  UPDATE identities SET is_default=0;
61  END;
62 
63 CREATE TRIGGER IF NOT EXISTS
64  identity_default_after_insert_trigger
65  AFTER INSERT ON identities
66  FOR EACH ROW
67  WHEN NOT EXISTS
68  (SELECT id
69  FROM identities
70  WHERE is_default=1)
71  BEGIN
72  UPDATE identities
73  SET is_default=1
74  WHERE identity=NEW.identity;
75  END;
76 
77 CREATE TRIGGER IF NOT EXISTS
78  identity_default_update_trigger
79  BEFORE UPDATE ON identities
80  FOR EACH ROW
81  WHEN NEW.is_default=1 AND OLD.is_default=0
82  BEGIN
83  UPDATE identities SET is_default=0;
84  END;
85 
86 CREATE TABLE IF NOT EXISTS
87  keys(
88  id INTEGER PRIMARY KEY,
89  identity_id INTEGER NOT NULL,
90  key_name BLOB NOT NULL,
91  key_bits BLOB NOT NULL,
92  is_default INTEGER DEFAULT 0,
93  FOREIGN KEY(identity_id)
94  REFERENCES identities(id)
95  ON DELETE CASCADE
96  ON UPDATE CASCADE
97  );
98 
99 CREATE UNIQUE INDEX IF NOT EXISTS
100  keyIndex ON keys(key_name);
101 
102 CREATE TRIGGER IF NOT EXISTS
103  key_default_before_insert_trigger
104  BEFORE INSERT ON keys
105  FOR EACH ROW
106  WHEN NEW.is_default=1
107  BEGIN
108  UPDATE keys
109  SET is_default=0
110  WHERE identity_id=NEW.identity_id;
111  END;
112 
113 CREATE TRIGGER IF NOT EXISTS
114  key_default_after_insert_trigger
115  AFTER INSERT ON keys
116  FOR EACH ROW
117  WHEN NOT EXISTS
118  (SELECT id
119  FROM keys
120  WHERE is_default=1
121  AND identity_id=NEW.identity_id)
122  BEGIN
123  UPDATE keys
124  SET is_default=1
125  WHERE key_name=NEW.key_name;
126  END;
127 
128 CREATE TRIGGER IF NOT EXISTS
129  key_default_update_trigger
130  BEFORE UPDATE ON keys
131  FOR EACH ROW
132  WHEN NEW.is_default=1 AND OLD.is_default=0
133  BEGIN
134  UPDATE keys
135  SET is_default=0
136  WHERE identity_id=NEW.identity_id;
137  END;
138 
139 
140 CREATE TABLE IF NOT EXISTS
141  certificates(
142  id INTEGER PRIMARY KEY,
143  key_id INTEGER NOT NULL,
144  certificate_name BLOB NOT NULL,
145  certificate_data BLOB NOT NULL,
146  is_default INTEGER DEFAULT 0,
147  FOREIGN KEY(key_id)
148  REFERENCES keys(id)
149  ON DELETE CASCADE
150  ON UPDATE CASCADE
151  );
152 
153 CREATE UNIQUE INDEX IF NOT EXISTS
154  certIndex ON certificates(certificate_name);
155 
156 CREATE TRIGGER IF NOT EXISTS
157  cert_default_before_insert_trigger
158  BEFORE INSERT ON certificates
159  FOR EACH ROW
160  WHEN NEW.is_default=1
161  BEGIN
162  UPDATE certificates
163  SET is_default=0
164  WHERE key_id=NEW.key_id;
165  END;
166 
167 CREATE TRIGGER IF NOT EXISTS
168  cert_default_after_insert_trigger
169  AFTER INSERT ON certificates
170  FOR EACH ROW
171  WHEN NOT EXISTS
172  (SELECT id
173  FROM certificates
174  WHERE is_default=1
175  AND key_id=NEW.key_id)
176  BEGIN
177  UPDATE certificates
178  SET is_default=1
179  WHERE certificate_name=NEW.certificate_name;
180  END;
181 
182 CREATE TRIGGER IF NOT EXISTS
183  cert_default_update_trigger
184  BEFORE UPDATE ON certificates
185  FOR EACH ROW
186  WHEN NEW.is_default=1 AND OLD.is_default=0
187  BEGIN
188  UPDATE certificates
189  SET is_default=0
190  WHERE key_id=NEW.key_id;
191  END;
192 )SQL";
193 
194 PibSqlite3::PibSqlite3(const std::string& location)
195 {
196  // Determine the path of PIB DB
197  boost::filesystem::path dbDir;
198  if (!location.empty()) {
199  dbDir = boost::filesystem::path(location);
200  }
201 #ifdef NDN_CXX_HAVE_TESTS
202  else if (getenv("TEST_HOME") != nullptr) {
203  dbDir = boost::filesystem::path(getenv("TEST_HOME")) / ".ndn";
204  }
205 #endif // NDN_CXX_HAVE_TESTS
206  else if (getenv("HOME") != nullptr) {
207  dbDir = boost::filesystem::path(getenv("HOME")) / ".ndn";
208  }
209  else {
210  dbDir = boost::filesystem::current_path() / ".ndn";
211  }
212  boost::filesystem::create_directories(dbDir);
213 
214  // Open PIB
215  int result = sqlite3_open_v2((dbDir / "pib.db").c_str(), &m_database,
216  SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
217 #ifdef NDN_CXX_DISABLE_SQLITE3_FS_LOCKING
218  "unix-dotfile"
219 #else
220  nullptr
221 #endif
222  );
223 
224  if (result != SQLITE_OK) {
225  NDN_THROW(PibImpl::Error("PIB database cannot be opened/created in " + dbDir.string()));
226  }
227 
228  // enable foreign key
229  sqlite3_exec(m_database, "PRAGMA foreign_keys=ON", nullptr, nullptr, nullptr);
230 
231  // initialize PIB tables
232  char* errmsg = nullptr;
233  result = sqlite3_exec(m_database, INITIALIZATION.c_str(), nullptr, nullptr, &errmsg);
234  if (result != SQLITE_OK && errmsg != nullptr) {
235  std::string what = "PIB database cannot be initialized: "s + errmsg;
236  sqlite3_free(errmsg);
237  NDN_THROW(PibImpl::Error(what));
238  }
239 }
240 
242 {
243  sqlite3_close(m_database);
244 }
245 
246 const std::string&
248 {
249  static std::string scheme = "pib-sqlite3";
250  return scheme;
251 }
252 
253 void
254 PibSqlite3::setTpmLocator(const std::string& tpmLocator)
255 {
256  Sqlite3Statement statement(m_database, "UPDATE tpmInfo SET tpm_locator=?");
257  statement.bind(1, tpmLocator, SQLITE_TRANSIENT);
258  statement.step();
259 
260  if (sqlite3_changes(m_database) == 0) {
261  // no row is updated, tpm_locator does not exist, insert it directly
262  Sqlite3Statement insertStatement(m_database, "INSERT INTO tpmInfo (tpm_locator) values (?)");
263  insertStatement.bind(1, tpmLocator, SQLITE_TRANSIENT);
264  insertStatement.step();
265  }
266 }
267 
268 std::string
270 {
271  Sqlite3Statement statement(m_database, "SELECT tpm_locator FROM tpmInfo");
272  int res = statement.step();
273  if (res == SQLITE_ROW)
274  return statement.getString(0);
275  else
276  return "";
277 }
278 
279 bool
280 PibSqlite3::hasIdentity(const Name& identity) const
281 {
282  Sqlite3Statement statement(m_database, "SELECT id FROM identities WHERE identity=?");
283  statement.bind(1, identity.wireEncode(), SQLITE_TRANSIENT);
284  return statement.step() == SQLITE_ROW;
285 }
286 
287 void
289 {
290  if (!hasIdentity(identity)) {
291  Sqlite3Statement statement(m_database, "INSERT INTO identities (identity) values (?)");
292  statement.bind(1, identity.wireEncode(), SQLITE_TRANSIENT);
293  statement.step();
294  }
295 
296  if (!hasDefaultIdentity()) {
297  setDefaultIdentity(identity);
298  }
299 }
300 
301 void
303 {
304  Sqlite3Statement statement(m_database, "DELETE FROM identities WHERE identity=?");
305  statement.bind(1, identity.wireEncode(), SQLITE_TRANSIENT);
306  statement.step();
307 }
308 
309 void
311 {
312  Sqlite3Statement statement(m_database, "DELETE FROM identities");
313  statement.step();
314 }
315 
316 std::set<Name>
318 {
319  std::set<Name> identities;
320  Sqlite3Statement statement(m_database, "SELECT identity FROM identities");
321 
322  while (statement.step() == SQLITE_ROW)
323  identities.insert(Name(statement.getBlock(0)));
324 
325  return identities;
326 }
327 
328 void
330 {
331  if (!hasIdentity(identityName)) {
332  NDN_THROW(Pib::Error("Cannot set non-existing identity `" + identityName.toUri() + "` as default"));
333  }
334  Sqlite3Statement statement(m_database, "UPDATE identities SET is_default=1 WHERE identity=?");
335  statement.bind(1, identityName.wireEncode(), SQLITE_TRANSIENT);
336  statement.step();
337 }
338 
339 Name
341 {
342  Sqlite3Statement statement(m_database, "SELECT identity FROM identities WHERE is_default=1");
343 
344  if (statement.step() == SQLITE_ROW)
345  return Name(statement.getBlock(0));
346  else
347  NDN_THROW(Pib::Error("No default identity"));
348 }
349 
350 bool
351 PibSqlite3::hasDefaultIdentity() const
352 {
353  Sqlite3Statement statement(m_database, "SELECT identity FROM identities WHERE is_default=1");
354  return (statement.step() == SQLITE_ROW);
355 }
356 
357 bool
358 PibSqlite3::hasKey(const Name& keyName) const
359 {
360  Sqlite3Statement statement(m_database, "SELECT id FROM keys WHERE key_name=?");
361  statement.bind(1, keyName.wireEncode(), SQLITE_TRANSIENT);
362 
363  return (statement.step() == SQLITE_ROW);
364 }
365 
366 void
367 PibSqlite3::addKey(const Name& identity, const Name& keyName, span<const uint8_t> key)
368 {
369  // ensure identity exists
370  addIdentity(identity);
371 
372  if (!hasKey(keyName)) {
373  Sqlite3Statement statement(m_database,
374  "INSERT INTO keys (identity_id, key_name, key_bits) "
375  "VALUES ((SELECT id FROM identities WHERE identity=?), ?, ?)");
376  statement.bind(1, identity.wireEncode(), SQLITE_TRANSIENT);
377  statement.bind(2, keyName.wireEncode(), SQLITE_TRANSIENT);
378  statement.bind(3, key.data(), key.size(), SQLITE_STATIC);
379  statement.step();
380  }
381  else {
382  Sqlite3Statement statement(m_database,
383  "UPDATE keys SET key_bits=? WHERE key_name=?");
384  statement.bind(1, key.data(), key.size(), SQLITE_STATIC);
385  statement.bind(2, keyName.wireEncode(), SQLITE_TRANSIENT);
386  statement.step();
387  }
388 
389  if (!hasDefaultKeyOfIdentity(identity)) {
390  setDefaultKeyOfIdentity(identity, keyName);
391  }
392 }
393 
394 void
396 {
397  Sqlite3Statement statement(m_database, "DELETE FROM keys WHERE key_name=?");
398  statement.bind(1, keyName.wireEncode(), SQLITE_TRANSIENT);
399  statement.step();
400 }
401 
402 Buffer
403 PibSqlite3::getKeyBits(const Name& keyName) const
404 {
405  Sqlite3Statement statement(m_database, "SELECT key_bits FROM keys WHERE key_name=?");
406  statement.bind(1, keyName.wireEncode(), SQLITE_TRANSIENT);
407 
408  if (statement.step() == SQLITE_ROW)
409  return Buffer(statement.getBlob(0), statement.getSize(0));
410  else
411  NDN_THROW(Pib::Error("Key `" + keyName.toUri() + "` does not exist"));
412 }
413 
414 std::set<Name>
415 PibSqlite3::getKeysOfIdentity(const Name& identity) const
416 {
417  std::set<Name> keyNames;
418 
419  Sqlite3Statement statement(m_database,
420  "SELECT key_name "
421  "FROM keys JOIN identities ON keys.identity_id=identities.id "
422  "WHERE identities.identity=?");
423  statement.bind(1, identity.wireEncode(), SQLITE_TRANSIENT);
424 
425  while (statement.step() == SQLITE_ROW) {
426  keyNames.insert(Name(statement.getBlock(0)));
427  }
428 
429  return keyNames;
430 }
431 
432 void
433 PibSqlite3::setDefaultKeyOfIdentity(const Name& identity, const Name& keyName)
434 {
435  if (!hasKey(keyName)) {
436  NDN_THROW(Pib::Error("Key `" + keyName.toUri() + "` does not exist"));
437  }
438 
439  Sqlite3Statement statement(m_database, "UPDATE keys SET is_default=1 WHERE key_name=?");
440  statement.bind(1, keyName.wireEncode(), SQLITE_TRANSIENT);
441  statement.step();
442 }
443 
444 Name
446 {
447  if (!hasIdentity(identity)) {
448  NDN_THROW(Pib::Error("Identity `" + identity.toUri() + "` does not exist"));
449  }
450 
451  Sqlite3Statement statement(m_database,
452  "SELECT key_name "
453  "FROM keys JOIN identities ON keys.identity_id=identities.id "
454  "WHERE identities.identity=? AND keys.is_default=1");
455  statement.bind(1, identity.wireEncode(), SQLITE_TRANSIENT);
456 
457  if (statement.step() == SQLITE_ROW) {
458  return Name(statement.getBlock(0));
459  }
460  else
461  NDN_THROW(Pib::Error("No default key for identity `" + identity.toUri() + "`"));
462 }
463 
464 bool
465 PibSqlite3::hasDefaultKeyOfIdentity(const Name& identity) const
466 {
467  Sqlite3Statement statement(m_database,
468  "SELECT key_name "
469  "FROM keys JOIN identities ON keys.identity_id=identities.id "
470  "WHERE identities.identity=? AND keys.is_default=1");
471  statement.bind(1, identity.wireEncode(), SQLITE_TRANSIENT);
472 
473  return (statement.step() == SQLITE_ROW);
474 }
475 
476 bool
477 PibSqlite3::hasCertificate(const Name& certName) const
478 {
479  Sqlite3Statement statement(m_database, "SELECT id FROM certificates WHERE certificate_name=?");
480  statement.bind(1, certName.wireEncode(), SQLITE_TRANSIENT);
481  return (statement.step() == SQLITE_ROW);
482 }
483 
484 void
485 PibSqlite3::addCertificate(const Certificate& certificate)
486 {
487  // ensure key exists
488  addKey(certificate.getIdentity(), certificate.getKeyName(), certificate.getContent().value_bytes());
489 
490  if (!hasCertificate(certificate.getName())) {
491  Sqlite3Statement statement(m_database,
492  "INSERT INTO certificates "
493  "(key_id, certificate_name, certificate_data) "
494  "VALUES ((SELECT id FROM keys WHERE key_name=?), ?, ?)");
495  statement.bind(1, certificate.getKeyName().wireEncode(), SQLITE_TRANSIENT);
496  statement.bind(2, certificate.getName().wireEncode(), SQLITE_TRANSIENT);
497  statement.bind(3, certificate.wireEncode(), SQLITE_STATIC);
498  statement.step();
499  }
500  else {
501  Sqlite3Statement statement(m_database,
502  "UPDATE certificates SET certificate_data=? WHERE certificate_name=?");
503  statement.bind(1, certificate.wireEncode(), SQLITE_STATIC);
504  statement.bind(2, certificate.getName().wireEncode(), SQLITE_TRANSIENT);
505  statement.step();
506  }
507 
508  if (!hasDefaultCertificateOfKey(certificate.getKeyName())) {
509  setDefaultCertificateOfKey(certificate.getKeyName(), certificate.getName());
510  }
511 }
512 
513 void
515 {
516  Sqlite3Statement statement(m_database, "DELETE FROM certificates WHERE certificate_name=?");
517  statement.bind(1, certName.wireEncode(), SQLITE_TRANSIENT);
518  statement.step();
519 }
520 
521 Certificate
522 PibSqlite3::getCertificate(const Name& certName) const
523 {
524  Sqlite3Statement statement(m_database,
525  "SELECT certificate_data FROM certificates WHERE certificate_name=?");
526  statement.bind(1, certName.wireEncode(), SQLITE_TRANSIENT);
527 
528  if (statement.step() == SQLITE_ROW)
529  return Certificate(statement.getBlock(0));
530  else
531  NDN_THROW(Pib::Error("Certificate `" + certName.toUri() + "` does not exit"));
532 }
533 
534 std::set<Name>
536 {
537  std::set<Name> certNames;
538 
539  Sqlite3Statement statement(m_database,
540  "SELECT certificate_name "
541  "FROM certificates JOIN keys ON certificates.key_id=keys.id "
542  "WHERE keys.key_name=?");
543  statement.bind(1, keyName.wireEncode(), SQLITE_TRANSIENT);
544 
545  while (statement.step() == SQLITE_ROW)
546  certNames.insert(Name(statement.getBlock(0)));
547 
548  return certNames;
549 }
550 
551 void
552 PibSqlite3::setDefaultCertificateOfKey(const Name& keyName, const Name& certName)
553 {
554  if (!hasCertificate(certName)) {
555  NDN_THROW(Pib::Error("Certificate `" + certName.toUri() + "` does not exist"));
556  }
557 
558  Sqlite3Statement statement(m_database,
559  "UPDATE certificates SET is_default=1 WHERE certificate_name=?");
560  statement.bind(1, certName.wireEncode(), SQLITE_TRANSIENT);
561  statement.step();
562 }
563 
564 Certificate
566 {
567  Sqlite3Statement statement(m_database,
568  "SELECT certificate_data "
569  "FROM certificates JOIN keys ON certificates.key_id=keys.id "
570  "WHERE certificates.is_default=1 AND keys.key_name=?");
571  statement.bind(1, keyName.wireEncode(), SQLITE_TRANSIENT);
572 
573  if (statement.step() == SQLITE_ROW)
574  return Certificate(statement.getBlock(0));
575  else
576  NDN_THROW(Pib::Error("No default certificate for key `" + keyName.toUri() + "`"));
577 }
578 
579 bool
580 PibSqlite3::hasDefaultCertificateOfKey(const Name& keyName) const
581 {
582  Sqlite3Statement statement(m_database,
583  "SELECT certificate_data "
584  "FROM certificates JOIN keys ON certificates.key_id=keys.id "
585  "WHERE certificates.is_default=1 AND keys.key_name=?");
586  statement.bind(1, keyName.wireEncode(), SQLITE_TRANSIENT);
587 
588  return statement.step() == SQLITE_ROW;
589 }
590 
591 } // namespace pib
592 } // namespace security
593 } // namespace ndn
represents a non-semantic error
Definition: pib-impl.hpp:49
void removeCertificate(const Name &certName) final
Remove a certificate with name certName.
Copyright (c) 2011-2015 Regents of the University of California.
Name getDefaultKeyOfIdentity(const Name &identity) const final
represents a semantic error
Definition: pib.hpp:56
int getSize(int column)
get the size of column.
std::set< Name > getCertificatesOfKey(const Name &keyName) const final
Get a list of certificate names of a key with id keyName.
void clearIdentities() final
Erasing all certificates, keys, and identities.
Certificate getDefaultCertificateOfKey(const Name &keyName) const final
Certificate getCertificate(const Name &certName) const final
Get a certificate with name certName.
std::set< Name > getIdentities() const final
Get the name of all the identities.
void setTpmLocator(const std::string &tpmLocator) final
Set the corresponding TPM information to tpmLocator.
#define NDN_THROW(e)
Definition: exception.hpp:61
Block getBlock(int column)
get a block from column.
std::string getTpmLocator() const final
Get TPM Locator.
void addKey(const Name &identity, const Name &keyName, span< const uint8_t > key) final
Add a key.
Buffer getKeyBits(const Name &keyName) const final
Get the key bits of a key with name keyName.
static const std::string & getScheme()
void removeIdentity(const Name &identity) final
Remove an identity and related keys and certificates.
const uint8_t * getBlob(int column)
get a pointer of byte blob from column.
void setDefaultIdentity(const Name &identityName) final
Set an identity with name identityName as the default identity.
void setDefaultCertificateOfKey(const Name &keyName, const Name &certName) final
Set a cert with name certName as the default of a key with keyName.
int bind(int index, const char *value, size_t size, void(*destructor)(void *))
bind a string to the statement
Name getDefaultIdentity() const final
Get the default identity.
Represents an absolute name.
Definition: name.hpp:41
PibSqlite3(const std::string &location="")
Create sqlite3-based PIB backed.
void addIdentity(const Name &identity) final
Add an identity.
std::string getString(int column)
get a string from column.
int step()
wrapper of sqlite3_step
bool hasKey(const Name &keyName) const final
Check the existence of a key with keyName.
wrap an SQLite3 prepared statement
~PibSqlite3() final
Destruct and cleanup internal state.
void addCertificate(const Certificate &certificate) final
Add a certificate.
void removeKey(const Name &keyName) final
Remove a key with keyName and related certificates.
void setDefaultKeyOfIdentity(const Name &identity, const Name &keyName) final
Set an key with keyName as the default key of an identity with name identity.
void toUri(std::ostream &os, name::UriFormat format=name::UriFormat::DEFAULT) const
Write URI representation of the name to the output stream.
Definition: name.cpp:349
size_t wireEncode(EncodingImpl< TAG > &encoder) const
Fast encoding or block size estimation.
Definition: name.cpp:117
std::set< Name > getKeysOfIdentity(const Name &identity) const final
Get all the key names of an identity with name identity.
static const std::string INITIALIZATION
Definition: pib-sqlite3.cpp:38
bool hasIdentity(const Name &identity) const final
Check the existence of an identity.
General-purpose automatically managed/resized buffer.
Definition: buffer.hpp:41
bool hasCertificate(const Name &certName) const final
Check the existence of a certificate with name certName.