sqlce 0.1.0__tar.gz → 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. {sqlce-0.1.0 → sqlce-0.1.1}/PKG-INFO +6 -5
  2. {sqlce-0.1.0 → sqlce-0.1.1}/README.md +5 -4
  3. {sqlce-0.1.0 → sqlce-0.1.1}/bindings/src/module.cpp +39 -0
  4. {sqlce-0.1.0 → sqlce-0.1.1}/core/CMakeLists.txt +5 -1
  5. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/application/SdfDatabase.hpp +6 -0
  6. sqlce-0.1.1/core/include/sdf/domain/EncryptionMode.hpp +48 -0
  7. sqlce-0.1.1/core/include/sdf/domain/IPageCipher.hpp +23 -0
  8. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/domain/PageLayout.hpp +8 -0
  9. sqlce-0.1.1/core/include/sdf/infrastructure/AesCbc.hpp +30 -0
  10. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/infrastructure/FileStorage.hpp +7 -0
  11. sqlce-0.1.1/core/include/sdf/infrastructure/Sha256.hpp +16 -0
  12. sqlce-0.1.1/core/include/sdf/infrastructure/Sha512.hpp +16 -0
  13. sqlce-0.1.1/core/include/sdf/parsing/SdfPageCipher.hpp +41 -0
  14. {sqlce-0.1.0/core/app → sqlce-0.1.1/core}/main.cpp +67 -27
  15. {sqlce-0.1.0 → sqlce-0.1.1}/core/src/application/SdfDatabase.cpp +23 -2
  16. sqlce-0.1.1/core/src/infrastructure/AesCbc.cpp +230 -0
  17. {sqlce-0.1.0 → sqlce-0.1.1}/core/src/infrastructure/FileStorage.cpp +43 -0
  18. sqlce-0.1.1/core/src/infrastructure/Sha256.cpp +126 -0
  19. sqlce-0.1.1/core/src/infrastructure/Sha512.cpp +144 -0
  20. sqlce-0.1.1/core/src/parsing/SdfPageCipher.cpp +151 -0
  21. {sqlce-0.1.0 → sqlce-0.1.1}/docs/build.txt +5 -2
  22. sqlce-0.1.1/docs/en/cpp.md +47 -0
  23. sqlce-0.1.1/docs/en/python.md +51 -0
  24. sqlce-0.1.1/docs/ru/cpp.md +47 -0
  25. sqlce-0.1.1/docs/ru/python.md +51 -0
  26. sqlce-0.1.1/main.py +112 -0
  27. {sqlce-0.1.0 → sqlce-0.1.1}/pyproject.toml +9 -2
  28. sqlce-0.1.1/python/sqlce/__init__.py +6 -0
  29. sqlce-0.1.1/python/sqlce/__init__.pyi +7 -0
  30. {sqlce-0.1.0 → sqlce-0.1.1}/python/sqlce/_sdf_native.pyi +13 -0
  31. sqlce-0.1.1/research/sdf_d3.py +90 -0
  32. sqlce-0.1.1/research/sdf_d4.py +94 -0
  33. sqlce-0.1.0/docs/4.0/ru/00-overview.md +0 -50
  34. sqlce-0.1.0/docs/4.0/ru/01-rows.md +0 -119
  35. sqlce-0.1.0/docs/4.0/ru/02-lob.md +0 -69
  36. sqlce-0.1.0/docs/4.0/ru/03-column-types.md +0 -80
  37. sqlce-0.1.0/docs/4.0/ru/04-catalog.md +0 -66
  38. sqlce-0.1.0/docs/4.0/ru/05-limitations.md +0 -50
  39. sqlce-0.1.0/docs/4.0/ru/catalog/00-schema.md +0 -94
  40. sqlce-0.1.0/docs/4.0/ru/catalog/01-row-layout.md +0 -107
  41. sqlce-0.1.0/docs/4.0/ru/catalog/02-classification.md +0 -32
  42. sqlce-0.1.0/docs/4.0/ru/catalog/03-status.md +0 -72
  43. sqlce-0.1.0/main.py +0 -76
  44. sqlce-0.1.0/python/sqlce/__init__.py +0 -4
  45. sqlce-0.1.0/python/sqlce/__init__.pyi +0 -5
  46. {sqlce-0.1.0 → sqlce-0.1.1}/.github/workflows/release.yml +0 -0
  47. {sqlce-0.1.0 → sqlce-0.1.1}/.gitignore +0 -0
  48. {sqlce-0.1.0 → sqlce-0.1.1}/CMakeLists.txt +0 -0
  49. {sqlce-0.1.0 → sqlce-0.1.1}/LICENSE +0 -0
  50. {sqlce-0.1.0 → sqlce-0.1.1}/bindings/CMakeLists.txt +0 -0
  51. {sqlce-0.1.0 → sqlce-0.1.1}/bindings/src/value_convert.cpp +0 -0
  52. {sqlce-0.1.0 → sqlce-0.1.1}/bindings/src/value_convert.hpp +0 -0
  53. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/application/ColumnSchema.hpp +0 -0
  54. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/domain/ColumnDef.hpp +0 -0
  55. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/domain/ColumnType.hpp +0 -0
  56. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/domain/ColumnValue.hpp +0 -0
  57. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/domain/DateTimeValue.hpp +0 -0
  58. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/domain/Guid.hpp +0 -0
  59. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/domain/IPageStorage.hpp +0 -0
  60. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/domain/NumericValue.hpp +0 -0
  61. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/domain/Row.hpp +0 -0
  62. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/domain/TableDef.hpp +0 -0
  63. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/infrastructure/BinaryReader.hpp +0 -0
  64. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/infrastructure/PageView.hpp +0 -0
  65. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/parsing/CatalogPageScanner.hpp +0 -0
  66. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/parsing/CatalogRow.hpp +0 -0
  67. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/parsing/CatalogRowDecoder.hpp +0 -0
  68. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/parsing/CatalogSchema.hpp +0 -0
  69. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/parsing/ICatalogPageScanner.hpp +0 -0
  70. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/parsing/ICatalogRowDecoder.hpp +0 -0
  71. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/parsing/ILobChainRegistry.hpp +0 -0
  72. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/parsing/IRowDecoder.hpp +0 -0
  73. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/parsing/ITableCatalogBuilder.hpp +0 -0
  74. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/parsing/LobChainRegistry.hpp +0 -0
  75. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/parsing/RowDecoder.hpp +0 -0
  76. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/parsing/TableCatalogBuilder.hpp +0 -0
  77. {sqlce-0.1.0 → sqlce-0.1.1}/core/include/sdf/parsing/TextDecoder.hpp +0 -0
  78. {sqlce-0.1.0 → sqlce-0.1.1}/core/src/domain/ColumnDef.cpp +0 -0
  79. {sqlce-0.1.0 → sqlce-0.1.1}/core/src/domain/ColumnType.cpp +0 -0
  80. {sqlce-0.1.0 → sqlce-0.1.1}/core/src/domain/ColumnValue.cpp +0 -0
  81. {sqlce-0.1.0 → sqlce-0.1.1}/core/src/domain/DateTimeValue.cpp +0 -0
  82. {sqlce-0.1.0 → sqlce-0.1.1}/core/src/domain/Guid.cpp +0 -0
  83. {sqlce-0.1.0 → sqlce-0.1.1}/core/src/domain/NumericValue.cpp +0 -0
  84. {sqlce-0.1.0 → sqlce-0.1.1}/core/src/domain/Row.cpp +0 -0
  85. {sqlce-0.1.0 → sqlce-0.1.1}/core/src/domain/TableDef.cpp +0 -0
  86. {sqlce-0.1.0 → sqlce-0.1.1}/core/src/infrastructure/BinaryReader.cpp +0 -0
  87. {sqlce-0.1.0 → sqlce-0.1.1}/core/src/infrastructure/PageView.cpp +0 -0
  88. {sqlce-0.1.0 → sqlce-0.1.1}/core/src/parsing/CatalogPageScanner.cpp +0 -0
  89. {sqlce-0.1.0 → sqlce-0.1.1}/core/src/parsing/CatalogRow.cpp +0 -0
  90. {sqlce-0.1.0 → sqlce-0.1.1}/core/src/parsing/CatalogRowDecoder.cpp +0 -0
  91. {sqlce-0.1.0 → sqlce-0.1.1}/core/src/parsing/CatalogSchema.cpp +0 -0
  92. {sqlce-0.1.0 → sqlce-0.1.1}/core/src/parsing/LobChainRegistry.cpp +0 -0
  93. {sqlce-0.1.0 → sqlce-0.1.1}/core/src/parsing/RowDecoder.cpp +0 -0
  94. {sqlce-0.1.0 → sqlce-0.1.1}/core/src/parsing/TableCatalogBuilder.cpp +0 -0
  95. {sqlce-0.1.0 → sqlce-0.1.1}/core/src/parsing/TextDecoder.cpp +0 -0
  96. {sqlce-0.1.0 → sqlce-0.1.1}/core/tests/CMakeLists.txt +0 -0
  97. {sqlce-0.1.0 → sqlce-0.1.1}/core/tests/count_test.cpp +0 -0
  98. {sqlce-0.1.0 → sqlce-0.1.1}/docs/CONVENTIONS.md +0 -0
  99. {sqlce-0.1.0 → sqlce-0.1.1}/requirements.txt +0 -0
  100. {sqlce-0.1.0 → sqlce-0.1.1}/research/catalog_parse/sdf_pages.py +0 -0
  101. {sqlce-0.1.0 → sqlce-0.1.1}/research/catalog_parse/syscatalog.py +0 -0
  102. {sqlce-0.1.0 → sqlce-0.1.1}/research/sdf_reader.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: sqlce
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Cross-platform lib for reading SQL Server CE (.sdf)
5
5
  Author-Email: boykopovar <boykopovar@gmail.com>
6
6
  License: MIT
@@ -27,21 +27,22 @@ Description-Content-Type: text/markdown
27
27
 
28
28
  Анализ внутреннего устройства формата по декомпилированным компонентам SQL CE. Цель - предоставить кроссплатформенную и независимую от рантайма реализацию чтения `.sdf` и документацию формата.
29
29
 
30
- ## Структура
31
30
 
32
31
  - `core/` - реализация парсера на C++
33
32
  - `bindings/` - Биндинги для C++ библиотеки к другим языкам
34
33
  - `python/sqlce/` - Python API
35
34
  - `research/` - Экспериментальные скрипты
36
35
 
37
- ## Установка
38
36
 
39
37
  ```bash
40
38
  pip install sqlce
41
39
  ```
42
40
 
43
- [Документирование формата](docs/4.0/ru/00-overview.md)
44
41
 
45
- Текущая [реализация](main.py) поддерживает чтение списка таблиц, структуры таблиц, содержимого записей. Использует c++.
42
+ Текущая [реализация](main.py) поддерживает чтение (поддерживается передача пароля) списка таблиц, структуры таблиц, содержимого записей. Использует c++.
43
+
44
+ [Документация](docs/ru/python.md)
45
+
46
+ [Documentation](docs/en/python.md)
46
47
 
47
48
  [Соглашения по написанию кода](docs/CONVENTIONS.md)
@@ -4,21 +4,22 @@
4
4
 
5
5
  Анализ внутреннего устройства формата по декомпилированным компонентам SQL CE. Цель - предоставить кроссплатформенную и независимую от рантайма реализацию чтения `.sdf` и документацию формата.
6
6
 
7
- ## Структура
8
7
 
9
8
  - `core/` - реализация парсера на C++
10
9
  - `bindings/` - Биндинги для C++ библиотеки к другим языкам
11
10
  - `python/sqlce/` - Python API
12
11
  - `research/` - Экспериментальные скрипты
13
12
 
14
- ## Установка
15
13
 
16
14
  ```bash
17
15
  pip install sqlce
18
16
  ```
19
17
 
20
- [Документирование формата](docs/4.0/ru/00-overview.md)
21
18
 
22
- Текущая [реализация](main.py) поддерживает чтение списка таблиц, структуры таблиц, содержимого записей. Использует c++.
19
+ Текущая [реализация](main.py) поддерживает чтение (поддерживается передача пароля) списка таблиц, структуры таблиц, содержимого записей. Использует c++.
20
+
21
+ [Документация](docs/ru/python.md)
22
+
23
+ [Documentation](docs/en/python.md)
23
24
 
24
25
  [Соглашения по написанию кода](docs/CONVENTIONS.md)
@@ -9,6 +9,7 @@
9
9
 
10
10
  #include "sdf/application/ColumnSchema.hpp"
11
11
  #include "sdf/application/SdfDatabase.hpp"
12
+ #include "sdf/domain/EncryptionMode.hpp"
12
13
  #include "sdf/domain/Row.hpp"
13
14
 
14
15
  #include "value_convert.hpp"
@@ -26,7 +27,9 @@ constexpr char ModuleDoc[] = "Native reader for SQL Server Compact (.sdf) databa
26
27
  constexpr char DatabaseClassName[] = "SdfDatabase";
27
28
  constexpr char DatabaseDoc[] = "Reads tables, schemas and rows from a .sdf database file.";
28
29
  constexpr char InitDoc[] = "Open a .sdf file at the given path.";
30
+ constexpr char InitWithPasswordDoc[] = "Open a password-protected .sdf file, auto-detecting its encryption mode.";
29
31
  constexpr char PathArgName[] = "path";
32
+ constexpr char PasswordArgName[] = "password";
30
33
  constexpr char ListTablesName[] = "list_tables";
31
34
  constexpr char ListTablesDoc[] = "Return the names of all tables in the database.";
32
35
  constexpr char TableSchemaName[] = "table_schema";
@@ -44,6 +47,9 @@ constexpr char DeclaredSizeAttrName[] = "declared_size";
44
47
  constexpr char PrecisionAttrName[] = "precision";
45
48
  constexpr char ScaleAttrName[] = "scale";
46
49
 
50
+ constexpr char UnsupportedEncryptionModeErrorName[] = "UnsupportedEncryptionModeError";
51
+ constexpr char InvalidPasswordErrorName[] = "InvalidPasswordError";
52
+
47
53
  py::dict RowToDict(const domain::Row& row)
48
54
  {
49
55
  py::dict result;
@@ -75,12 +81,40 @@ std::optional<int> OptionalByteToInt(std::optional<std::uint8_t> value)
75
81
  return static_cast<int>(*value);
76
82
  }
77
83
 
84
+ void RegisterExceptions(py::module_& module)
85
+ {
86
+ static const py::exception<domain::UnsupportedEncryptionModeException> unsupportedModeError(
87
+ module, UnsupportedEncryptionModeErrorName, PyExc_ValueError);
88
+ static const py::exception<domain::InvalidPasswordException> invalidPasswordError(
89
+ module, InvalidPasswordErrorName, PyExc_ValueError);
90
+
91
+ py::register_exception_translator([](std::exception_ptr lastException) {
92
+ try
93
+ {
94
+ if (lastException)
95
+ {
96
+ std::rethrow_exception(lastException);
97
+ }
98
+ }
99
+ catch (const domain::UnsupportedEncryptionModeException& error)
100
+ {
101
+ PyErr_SetString(unsupportedModeError.ptr(), error.what());
102
+ }
103
+ catch (const domain::InvalidPasswordException& error)
104
+ {
105
+ PyErr_SetString(invalidPasswordError.ptr(), error.what());
106
+ }
107
+ });
108
+ }
109
+
78
110
  }
79
111
 
80
112
  PYBIND11_MODULE(_sdf_native, module)
81
113
  {
82
114
  module.doc() = ModuleDoc;
83
115
 
116
+ RegisterExceptions(module);
117
+
84
118
  py::class_<application::ColumnSchema>(module, SchemaClassName, SchemaDoc)
85
119
  .def_property_readonly(
86
120
  OrdinalAttrName,
@@ -103,6 +137,11 @@ PYBIND11_MODULE(_sdf_native, module)
103
137
 
104
138
  py::class_<application::SdfDatabase>(module, DatabaseClassName, DatabaseDoc)
105
139
  .def(py::init<const std::string&>(), py::arg(PathArgName), InitDoc)
140
+ .def(
141
+ py::init<const std::string&, const std::string&>(),
142
+ py::arg(PathArgName),
143
+ py::arg(PasswordArgName),
144
+ InitWithPasswordDoc)
106
145
  .def(ListTablesName, &application::SdfDatabase::ListTables, ListTablesDoc)
107
146
  .def(
108
147
  TableSchemaName,
@@ -18,15 +18,19 @@ add_library(libsdf
18
18
  src/domain/NumericValue.cpp
19
19
  src/domain/Row.cpp
20
20
  src/domain/TableDef.cpp
21
+ src/infrastructure/AesCbc.cpp
21
22
  src/infrastructure/BinaryReader.cpp
22
23
  src/infrastructure/FileStorage.cpp
23
24
  src/infrastructure/PageView.cpp
25
+ src/infrastructure/Sha256.cpp
26
+ src/infrastructure/Sha512.cpp
24
27
  src/parsing/CatalogPageScanner.cpp
25
28
  src/parsing/CatalogRow.cpp
26
29
  src/parsing/CatalogRowDecoder.cpp
27
30
  src/parsing/CatalogSchema.cpp
28
31
  src/parsing/LobChainRegistry.cpp
29
32
  src/parsing/RowDecoder.cpp
33
+ src/parsing/SdfPageCipher.cpp
30
34
  src/parsing/TableCatalogBuilder.cpp
31
35
  src/parsing/TextDecoder.cpp
32
36
  src/application/SdfDatabase.cpp
@@ -45,7 +49,7 @@ else()
45
49
  target_compile_options(libsdf PRIVATE -Wall -Wextra)
46
50
  endif()
47
51
 
48
- add_executable(sdf_dump app/main.cpp)
52
+ add_executable(sdf_dump main.cpp)
49
53
  target_link_libraries(sdf_dump PRIVATE libsdf)
50
54
 
51
55
  if(MSVC)
@@ -7,6 +7,7 @@
7
7
  #include <vector>
8
8
 
9
9
  #include "sdf/application/ColumnSchema.hpp"
10
+ #include "sdf/domain/IPageCipher.hpp"
10
11
  #include "sdf/domain/IPageStorage.hpp"
11
12
  #include "sdf/domain/Row.hpp"
12
13
  #include "sdf/domain/TableDef.hpp"
@@ -22,6 +23,7 @@ class SdfDatabase
22
23
  {
23
24
  public:
24
25
  explicit SdfDatabase(const std::string& path);
26
+ SdfDatabase(const std::string& path, const std::string& password);
25
27
 
26
28
  std::vector<std::string> ListTables() const;
27
29
  std::vector<ColumnSchema> TableSchema(const std::string& tableName) const;
@@ -36,6 +38,10 @@ private:
36
38
 
37
39
  std::map<std::string, domain::TableDef> _tables;
38
40
 
41
+ explicit SdfDatabase(std::unique_ptr<domain::IPageStorage> storage);
42
+
43
+ static std::unique_ptr<domain::IPageStorage> OpenStorage(const std::string& path, const std::string& password);
44
+
39
45
  void AssignDataPages();
40
46
  const domain::TableDef& RequireTable(const std::string& tableName) const;
41
47
  };
@@ -0,0 +1,48 @@
1
+ #ifndef SDF_DOMAIN_ENCRYPTION_MODE_HPP
2
+ #define SDF_DOMAIN_ENCRYPTION_MODE_HPP
3
+
4
+ #include <cstdint>
5
+ #include <stdexcept>
6
+ #include <string>
7
+
8
+ namespace sdf::domain
9
+ {
10
+
11
+ enum class EncryptionMode : std::uint32_t
12
+ {
13
+ None = 0,
14
+ Rc4Sha1 = 1,
15
+ Aes128Sha1 = 2,
16
+ Aes128Sha256 = 3,
17
+ Aes256Sha512 = 4
18
+ };
19
+
20
+ class UnsupportedEncryptionModeException final : public std::runtime_error
21
+ {
22
+ public:
23
+ explicit UnsupportedEncryptionModeException(EncryptionMode mode)
24
+ : std::runtime_error("unsupported .sdf encryption mode: " + std::to_string(static_cast<std::uint32_t>(mode)))
25
+ , _mode(mode)
26
+ {
27
+ }
28
+
29
+ EncryptionMode Mode() const
30
+ {
31
+ return _mode;
32
+ }
33
+
34
+ private:
35
+ EncryptionMode _mode;
36
+ };
37
+
38
+ class InvalidPasswordException final : public std::runtime_error
39
+ {
40
+ public:
41
+ InvalidPasswordException() : std::runtime_error("password verification failed")
42
+ {
43
+ }
44
+ };
45
+
46
+ }
47
+
48
+ #endif
@@ -0,0 +1,23 @@
1
+ #ifndef SDF_DOMAIN_I_PAGE_CIPHER_HPP
2
+ #define SDF_DOMAIN_I_PAGE_CIPHER_HPP
3
+
4
+ #include <cstddef>
5
+ #include <cstdint>
6
+ #include <span>
7
+ #include <vector>
8
+
9
+ namespace sdf::domain
10
+ {
11
+
12
+ class IPageCipher
13
+ {
14
+ public:
15
+ virtual ~IPageCipher() = default;
16
+
17
+ virtual bool VerifyPassword() const = 0;
18
+ virtual std::vector<std::uint8_t> DecryptPage(std::size_t pageNumber, std::span<const std::uint8_t> page) const = 0;
19
+ };
20
+
21
+ }
22
+
23
+ #endif
@@ -14,6 +14,14 @@ constexpr std::size_t LobPageHeaderLength = 16;
14
14
  constexpr std::size_t SlotArrayBaseIndex = 1017;
15
15
  constexpr std::size_t RowHeaderLength = 28;
16
16
 
17
+ constexpr std::size_t PlaintextHeaderLength = 16;
18
+ constexpr std::size_t Page0ModeOffset = 184;
19
+ constexpr std::size_t Page0VerifierOffset = 76;
20
+ constexpr std::size_t Page0VerifierLength = 96;
21
+ constexpr std::size_t Page0KeyParamOffset = 188;
22
+ constexpr std::size_t Page0KeyParamLength = 4;
23
+ constexpr std::size_t MaxPasswordLength = 40;
24
+
17
25
  }
18
26
 
19
27
  #endif
@@ -0,0 +1,30 @@
1
+ #ifndef SDF_INFRASTRUCTURE_AES_CBC_HPP
2
+ #define SDF_INFRASTRUCTURE_AES_CBC_HPP
3
+
4
+ #include <array>
5
+ #include <cstddef>
6
+ #include <cstdint>
7
+ #include <span>
8
+ #include <vector>
9
+
10
+ namespace sdf::infrastructure
11
+ {
12
+
13
+ class AesCbcDecryptor
14
+ {
15
+ public:
16
+ explicit AesCbcDecryptor(std::span<const std::uint8_t> key, std::span<const std::uint8_t, 16> iv);
17
+
18
+ std::vector<std::uint8_t> Decrypt(std::span<const std::uint8_t> ciphertext) const;
19
+
20
+ private:
21
+ std::vector<std::uint32_t> _roundKeys;
22
+ std::size_t _roundCount;
23
+ std::array<std::uint8_t, 16> _iv;
24
+
25
+ void DecryptBlock(const std::uint8_t* in, std::uint8_t* out) const;
26
+ };
27
+
28
+ }
29
+
30
+ #endif
@@ -2,18 +2,23 @@
2
2
  #define SDF_INFRASTRUCTURE_FILE_STORAGE_HPP
3
3
 
4
4
  #include <cstdint>
5
+ #include <optional>
5
6
  #include <string>
6
7
  #include <vector>
7
8
 
9
+ #include "sdf/domain/IPageCipher.hpp"
8
10
  #include "sdf/domain/IPageStorage.hpp"
9
11
 
10
12
  namespace sdf::infrastructure
11
13
  {
12
14
 
15
+ std::vector<std::uint8_t> ReadFirstPageRaw(const std::string& path);
16
+
13
17
  class FileStorage final : public domain::IPageStorage
14
18
  {
15
19
  public:
16
20
  explicit FileStorage(const std::string& path);
21
+ FileStorage(const std::string& path, const domain::IPageCipher& cipher);
17
22
 
18
23
  std::size_t PageCount() const override;
19
24
  std::span<const std::uint8_t> PageBytes(std::size_t pageNumber) const override;
@@ -21,6 +26,8 @@ public:
21
26
  private:
22
27
  std::vector<std::uint8_t> _fileBytes;
23
28
  std::size_t _pageCount;
29
+
30
+ void Load(const std::string& path);
24
31
  };
25
32
 
26
33
  }
@@ -0,0 +1,16 @@
1
+ #ifndef SDF_INFRASTRUCTURE_SHA256_HPP
2
+ #define SDF_INFRASTRUCTURE_SHA256_HPP
3
+
4
+ #include <array>
5
+ #include <cstddef>
6
+ #include <cstdint>
7
+ #include <span>
8
+
9
+ namespace sdf::infrastructure
10
+ {
11
+
12
+ std::array<std::uint8_t, 32> Sha256(std::span<const std::uint8_t> message);
13
+
14
+ }
15
+
16
+ #endif
@@ -0,0 +1,16 @@
1
+ #ifndef SDF_INFRASTRUCTURE_SHA512_HPP
2
+ #define SDF_INFRASTRUCTURE_SHA512_HPP
3
+
4
+ #include <array>
5
+ #include <cstddef>
6
+ #include <cstdint>
7
+ #include <span>
8
+
9
+ namespace sdf::infrastructure
10
+ {
11
+
12
+ std::array<std::uint8_t, 64> Sha512(std::span<const std::uint8_t> message);
13
+
14
+ }
15
+
16
+ #endif
@@ -0,0 +1,41 @@
1
+ #ifndef SDF_PARSING_SDF_PAGE_CIPHER_HPP
2
+ #define SDF_PARSING_SDF_PAGE_CIPHER_HPP
3
+
4
+ #include <array>
5
+ #include <cstddef>
6
+ #include <cstdint>
7
+ #include <span>
8
+ #include <string>
9
+ #include <vector>
10
+
11
+ #include "sdf/domain/EncryptionMode.hpp"
12
+ #include "sdf/domain/IPageCipher.hpp"
13
+
14
+ namespace sdf::parsing
15
+ {
16
+
17
+ class SdfPageCipher final : public domain::IPageCipher
18
+ {
19
+ public:
20
+ SdfPageCipher(std::span<const std::uint8_t> page0, std::string password);
21
+
22
+ static domain::EncryptionMode ReadMode(std::span<const std::uint8_t> page0);
23
+
24
+ bool VerifyPassword() const override;
25
+ std::vector<std::uint8_t> DecryptPage(std::size_t pageNumber, std::span<const std::uint8_t> page) const override;
26
+
27
+ private:
28
+ domain::EncryptionMode _mode;
29
+ std::string _password;
30
+ std::vector<std::uint8_t> _passwordUtf16Le;
31
+ std::vector<std::uint8_t> _page0Ciphertext;
32
+ std::array<std::uint8_t, 4> _page0KeyParam;
33
+
34
+ std::vector<std::uint8_t> DecryptPage0(std::span<const std::uint8_t> page0) const;
35
+ std::vector<std::uint8_t> DeriveKey(std::span<const std::uint8_t> keyParam) const;
36
+ std::vector<std::uint8_t> HashPasswordWith(std::span<const std::uint8_t> keyParam) const;
37
+ };
38
+
39
+ }
40
+
41
+ #endif
@@ -1,9 +1,11 @@
1
1
  #include <algorithm>
2
2
  #include <filesystem>
3
3
  #include <iostream>
4
+ #include <memory>
4
5
  #include <vector>
5
6
 
6
- #include "sdf/application/SdfDatabase.hpp"
7
+ #include "include/sdf/application/SdfDatabase.hpp"
8
+ #include "include/sdf/domain/EncryptionMode.hpp"
7
9
 
8
10
  namespace
9
11
  {
@@ -71,12 +73,28 @@ void PrintTableRows(const std::vector<std::string>& columnNames, const std::vect
71
73
  }
72
74
  }
73
75
 
74
- void DumpFile(const std::filesystem::path& sdfPath)
76
+ void DumpFile(const std::filesystem::path& sdfPath, const std::string& password)
75
77
  {
76
78
  std::cout << "\n" << sdfPath.filename().string() << "\n";
77
79
 
78
- sdf::application::SdfDatabase db(sdfPath.string());
79
- std::vector<std::string> tableNames = db.ListTables();
80
+ std::unique_ptr<sdf::application::SdfDatabase> db;
81
+ try
82
+ {
83
+ db = password.empty() ? std::make_unique<sdf::application::SdfDatabase>(sdfPath.string())
84
+ : std::make_unique<sdf::application::SdfDatabase>(sdfPath.string(), password);
85
+ }
86
+ catch (const sdf::domain::UnsupportedEncryptionModeException& error)
87
+ {
88
+ std::cout << "skipped: " << error.what() << "\n";
89
+ return;
90
+ }
91
+ catch (const sdf::domain::InvalidPasswordException& error)
92
+ {
93
+ std::cout << "skipped: " << error.what() << "\n";
94
+ return;
95
+ }
96
+
97
+ std::vector<std::string> tableNames = db->ListTables();
80
98
 
81
99
  if (tableNames.empty())
82
100
  {
@@ -88,7 +106,7 @@ void DumpFile(const std::filesystem::path& sdfPath)
88
106
  {
89
107
  std::cout << "\ntable: " << tableName << "\n";
90
108
 
91
- for (const sdf::application::ColumnSchema& column : db.TableSchema(tableName))
109
+ for (const sdf::application::ColumnSchema& column : db->TableSchema(tableName))
92
110
  {
93
111
  std::cout << " " << column.ordinal << ". " << column.name << " " << column.typeName
94
112
  << " size=" << column.declaredSize;
@@ -100,7 +118,7 @@ void DumpFile(const std::filesystem::path& sdfPath)
100
118
  std::cout << "\n";
101
119
  }
102
120
 
103
- const std::vector<sdf::domain::Row> rows = db.ReadTable(tableName);
121
+ const std::vector<sdf::domain::Row> rows = db->ReadTable(tableName);
104
122
  if (rows.empty())
105
123
  {
106
124
  std::cout << " (no rows)\n";
@@ -120,36 +138,58 @@ void DumpFile(const std::filesystem::path& sdfPath)
120
138
 
121
139
  int main(int argc, char** argv)
122
140
  {
123
- const std::filesystem::path directory = argc > 1 ? std::filesystem::path(argv[1]) : std::filesystem::path("research/raw/examples");
141
+ std::filesystem::path directory("research/raw/examples");
142
+ std::string password;
124
143
 
125
- std::cout << "current working directory: " << std::filesystem::current_path() << "\n";
126
- std::cout << "looking for: " << std::filesystem::absolute(directory) << "\n";
127
-
128
- if (!std::filesystem::is_directory(directory))
144
+ for (int i = 1; i < argc; ++i)
129
145
  {
130
- std::cerr << "directory not found: " << directory << "\n";
131
- return 1;
146
+ const std::string argument = argv[i];
147
+ if (argument == "--password" && i + 1 < argc)
148
+ {
149
+ password = argv[++i];
150
+ }
151
+ else
152
+ {
153
+ directory = std::filesystem::path(argument);
154
+ }
132
155
  }
133
156
 
134
- std::vector<std::filesystem::path> sdfFiles;
135
- for (const std::filesystem::directory_entry& entry : std::filesystem::directory_iterator(directory))
157
+ std::cout << "current working directory: " << std::filesystem::current_path() << "\n";
158
+
159
+ std::vector<std::pair<std::filesystem::path, std::string>> directoriesToScan;
160
+ directoriesToScan.emplace_back(directory, password);
161
+ directoriesToScan.emplace_back(directory / "protected", password.empty() ? "secret123" : password);
162
+
163
+ for (const auto& [scanDirectory, scanPassword] : directoriesToScan)
136
164
  {
137
- if (entry.path().extension() == ".sdf")
165
+ std::cout << "looking for: " << std::filesystem::absolute(scanDirectory) << "\n";
166
+
167
+ if (!std::filesystem::is_directory(scanDirectory))
138
168
  {
139
- sdfFiles.push_back(entry.path());
169
+ std::cerr << "directory not found: " << scanDirectory << "\n";
170
+ continue;
140
171
  }
141
- }
142
- std::sort(sdfFiles.begin(), sdfFiles.end());
143
172
 
144
- if (sdfFiles.empty())
145
- {
146
- std::cout << "no .sdf files found in " << directory << "\n";
147
- return 0;
148
- }
173
+ std::vector<std::filesystem::path> sdfFiles;
174
+ for (const std::filesystem::directory_entry& entry : std::filesystem::directory_iterator(scanDirectory))
175
+ {
176
+ if (entry.path().extension() == ".sdf")
177
+ {
178
+ sdfFiles.push_back(entry.path());
179
+ }
180
+ }
181
+ std::sort(sdfFiles.begin(), sdfFiles.end());
149
182
 
150
- for (const std::filesystem::path& sdfFile : sdfFiles)
151
- {
152
- DumpFile(sdfFile);
183
+ if (sdfFiles.empty())
184
+ {
185
+ std::cout << "no .sdf files found in " << scanDirectory << "\n";
186
+ continue;
187
+ }
188
+
189
+ for (const std::filesystem::path& sdfFile : sdfFiles)
190
+ {
191
+ DumpFile(sdfFile, scanPassword);
192
+ }
153
193
  }
154
194
 
155
195
  return 0;
@@ -8,13 +8,34 @@
8
8
  #include "sdf/parsing/CatalogRowDecoder.hpp"
9
9
  #include "sdf/parsing/LobChainRegistry.hpp"
10
10
  #include "sdf/parsing/RowDecoder.hpp"
11
+ #include "sdf/parsing/SdfPageCipher.hpp"
11
12
  #include "sdf/parsing/TableCatalogBuilder.hpp"
12
13
 
13
14
  namespace sdf::application
14
15
  {
15
16
 
16
- SdfDatabase::SdfDatabase(const std::string& path)
17
- : _storage(std::make_unique<infrastructure::FileStorage>(path))
17
+ SdfDatabase::SdfDatabase(const std::string& path) : SdfDatabase(OpenStorage(path, std::string()))
18
+ {
19
+ }
20
+
21
+ SdfDatabase::SdfDatabase(const std::string& path, const std::string& password) : SdfDatabase(OpenStorage(path, password))
22
+ {
23
+ }
24
+
25
+ std::unique_ptr<domain::IPageStorage> SdfDatabase::OpenStorage(const std::string& path, const std::string& password)
26
+ {
27
+ if (password.empty())
28
+ {
29
+ return std::make_unique<infrastructure::FileStorage>(path);
30
+ }
31
+
32
+ const std::vector<std::uint8_t> firstPage = infrastructure::ReadFirstPageRaw(path);
33
+ const parsing::SdfPageCipher cipher(firstPage, password);
34
+ return std::make_unique<infrastructure::FileStorage>(path, cipher);
35
+ }
36
+
37
+ SdfDatabase::SdfDatabase(std::unique_ptr<domain::IPageStorage> storage)
38
+ : _storage(std::move(storage))
18
39
  , _pageScanner(std::make_shared<parsing::CatalogPageScanner>())
19
40
  , _tableCatalogBuilder(
20
41
  std::make_shared<parsing::TableCatalogBuilder>(_pageScanner, std::make_shared<parsing::CatalogRowDecoder>()))