| | | 1 | | using LOCKnet.Core.DataAbstractions; |
| | | 2 | | using Microsoft.Data.Sqlite; |
| | | 3 | | |
| | | 4 | | namespace LOCKnet.Data.Repositories; |
| | | 5 | | |
| | | 6 | | /// <summary> |
| | | 7 | | /// SQLite-Implementierung von <see cref="IMasterKeyRepository"/>. |
| | | 8 | | /// Persistiert einen einzelnen Vault-Header in der bestehenden MasterKey-Tabelle (Id = 1). |
| | | 9 | | /// </summary> |
| | | 10 | | public class MasterKeyRepository : RepositoryBase, IMasterKeyRepository |
| | | 11 | | { |
| | | 12 | | /// <summary>Initialisiert eine neue Instanz von <see cref="MasterKeyRepository"/>.</summary> |
| | | 13 | | /// <param name="connectionString">Der vollständige SQLite-Connection-String.</param> |
| | 99 | 14 | | public MasterKeyRepository(string connectionString) : base(connectionString) { } |
| | | 15 | | |
| | | 16 | | /// <summary>Initialisiert eine neue Instanz von <see cref="MasterKeyRepository"/>.</summary> |
| | | 17 | | /// <param name="connectionFactory">Factory fuer Storage-spezifische SQLite-Verbindungen.</param> |
| | 282 | 18 | | public MasterKeyRepository(ISqliteConnectionFactory connectionFactory) : base(connectionFactory) { } |
| | | 19 | | |
| | | 20 | | #region IMasterKeyRepository |
| | | 21 | | |
| | | 22 | | /// <inheritdoc/> |
| | | 23 | | /// <exception cref="InvalidOperationException">Vault-Header existiert bereits.</exception> |
| | | 24 | | public void Create(VaultHeader header) |
| | 85 | 25 | | { |
| | 85 | 26 | | if (Get() != null) |
| | 1 | 27 | | throw new InvalidOperationException("Vault header already exists."); |
| | | 28 | | |
| | 84 | 29 | | using var conn = GetConnection(); |
| | 84 | 30 | | using var cmd = conn.CreateCommand(); |
| | 84 | 31 | | cmd.CommandText = @" |
| | 84 | 32 | | INSERT INTO MasterKey (Id, PasswordHash, FormatVersion, KdfIdentifier, KdfParameters, Salt, WrappedVaultKe |
| | 84 | 33 | | VALUES (1, $hash, $formatVersion, $kdfIdentifier, $kdfParameters, $salt, $wrappedVaultKey, $usesLegacyKeyM |
| | 84 | 34 | | cmd.Parameters.AddWithValue("$hash", header.LegacyPasswordHash); |
| | 84 | 35 | | cmd.Parameters.AddWithValue("$formatVersion", header.FormatVersion); |
| | 84 | 36 | | cmd.Parameters.AddWithValue("$kdfIdentifier", header.KdfIdentifier); |
| | 84 | 37 | | cmd.Parameters.AddWithValue("$kdfParameters", header.KdfParameters.Serialize()); |
| | 84 | 38 | | cmd.Parameters.AddWithValue("$salt", header.Salt); |
| | 84 | 39 | | cmd.Parameters.AddWithValue("$wrappedVaultKey", (object?)header.WrappedVaultKey ?? DBNull.Value); |
| | 84 | 40 | | cmd.Parameters.AddWithValue("$usesLegacyKeyMaterial", header.UsesLegacyKeyMaterial ? 1 : 0); |
| | 84 | 41 | | cmd.Parameters.AddWithValue("$requiresStorageCompaction", header.RequiresStorageCompaction ? 1 : 0); |
| | 84 | 42 | | cmd.Parameters.AddWithValue("$lastStorageCompactionAttemptUtc", (object?)header.LastStorageCompactionAttemptUtc?.ToS |
| | 84 | 43 | | cmd.Parameters.AddWithValue("$lastStorageCompactionFailureKind", (int)header.LastStorageCompactionFailureKind); |
| | 84 | 44 | | cmd.Parameters.AddWithValue("$lastStorageCompactionError", (object?)header.LastStorageCompactionError ?? DBNull.Valu |
| | 84 | 45 | | cmd.Parameters.AddWithValue("$storageMigrationState", (int)header.StorageMigrationState); |
| | 84 | 46 | | cmd.Parameters.AddWithValue("$storageMigrationTargetMode", (int)header.StorageMigrationTargetMode); |
| | 84 | 47 | | cmd.Parameters.AddWithValue("$lastStorageMigrationAttemptUtc", (object?)header.LastStorageMigrationAttemptUtc?.ToStr |
| | 84 | 48 | | cmd.Parameters.AddWithValue("$lastStorageMigrationError", (object?)header.LastStorageMigrationError ?? DBNull.Value) |
| | 84 | 49 | | cmd.ExecuteNonQuery(); |
| | 168 | 50 | | } |
| | | 51 | | |
| | | 52 | | /// <inheritdoc/> |
| | | 53 | | public VaultHeader? Get() |
| | 292 | 54 | | { |
| | 292 | 55 | | using var conn = GetConnection(); |
| | 292 | 56 | | using var cmd = conn.CreateCommand(); |
| | 292 | 57 | | cmd.CommandText = "SELECT PasswordHash, FormatVersion, KdfIdentifier, KdfParameters, Salt, WrappedVaultKey, UsesLega |
| | | 58 | | |
| | 292 | 59 | | using var reader = cmd.ExecuteReader(); |
| | 292 | 60 | | if (!reader.Read()) |
| | 166 | 61 | | return null; |
| | | 62 | | |
| | 126 | 63 | | return new VaultHeader |
| | 126 | 64 | | { |
| | 126 | 65 | | LegacyPasswordHash = reader.IsDBNull(0) ? [] : (byte[])reader[0], |
| | 126 | 66 | | FormatVersion = reader.IsDBNull(1) ? 1 : reader.GetInt32(1), |
| | 126 | 67 | | KdfIdentifier = reader.IsDBNull(2) ? "PBKDF2-SHA256" : reader.GetString(2), |
| | 126 | 68 | | KdfParameters = reader.IsDBNull(3) |
| | 126 | 69 | | ? new VaultKdfParameters() |
| | 126 | 70 | | : VaultKdfParameters.Deserialize(reader.GetString(3)), |
| | 126 | 71 | | Salt = (byte[])reader[4], |
| | 126 | 72 | | WrappedVaultKey = reader.IsDBNull(5) ? [] : (byte[])reader[5], |
| | 126 | 73 | | UsesLegacyKeyMaterial = !reader.IsDBNull(6) && reader.GetInt32(6) != 0, |
| | 126 | 74 | | RequiresStorageCompaction = !reader.IsDBNull(7) && reader.GetInt32(7) != 0, |
| | 126 | 75 | | LastStorageCompactionAttemptUtc = reader.IsDBNull(8) ? null : DateTime.Parse(reader.GetString(8), null, System.Glo |
| | 126 | 76 | | LastStorageCompactionFailureKind = reader.IsDBNull(9) ? StorageCompactionFailureKind.None : (StorageCompactionFail |
| | 126 | 77 | | LastStorageCompactionError = reader.IsDBNull(10) ? null : reader.GetString(10), |
| | 126 | 78 | | StorageMigrationState = reader.IsDBNull(11) ? VaultStorageMigrationState.None : (VaultStorageMigrationState)reader |
| | 126 | 79 | | StorageMigrationTargetMode = reader.IsDBNull(12) ? VaultStorageMigrationTargetMode.None : (VaultStorageMigrationTa |
| | 126 | 80 | | LastStorageMigrationAttemptUtc = reader.IsDBNull(13) ? null : DateTime.Parse(reader.GetString(13), null, System.Gl |
| | 126 | 81 | | LastStorageMigrationError = reader.IsDBNull(14) ? null : reader.GetString(14), |
| | 126 | 82 | | CreatedAt = reader.GetDateTime(15), |
| | 126 | 83 | | UpdatedAt = reader.GetDateTime(16), |
| | 126 | 84 | | }; |
| | 292 | 85 | | } |
| | | 86 | | |
| | | 87 | | /// <inheritdoc/> |
| | | 88 | | public void Update(VaultHeader header) |
| | 23 | 89 | | { |
| | 23 | 90 | | using var conn = GetConnection(); |
| | 23 | 91 | | using var cmd = conn.CreateCommand(); |
| | 23 | 92 | | cmd.CommandText = @" |
| | 23 | 93 | | UPDATE MasterKey |
| | 23 | 94 | | SET PasswordHash = $hash, |
| | 23 | 95 | | FormatVersion = $formatVersion, |
| | 23 | 96 | | KdfIdentifier = $kdfIdentifier, |
| | 23 | 97 | | KdfParameters = $kdfParameters, |
| | 23 | 98 | | Salt = $salt, |
| | 23 | 99 | | WrappedVaultKey = $wrappedVaultKey, |
| | 23 | 100 | | UsesLegacyKeyMaterial = $usesLegacyKeyMaterial, |
| | 23 | 101 | | RequiresStorageCompaction = $requiresStorageCompaction, |
| | 23 | 102 | | LastStorageCompactionAttemptUtc = $lastStorageCompactionAttemptUtc, |
| | 23 | 103 | | LastStorageCompactionFailureKind = $lastStorageCompactionFailureKind, |
| | 23 | 104 | | LastStorageCompactionError = $lastStorageCompactionError, |
| | 23 | 105 | | StorageMigrationState = $storageMigrationState, |
| | 23 | 106 | | StorageMigrationTargetMode = $storageMigrationTargetMode, |
| | 23 | 107 | | LastStorageMigrationAttemptUtc = $lastStorageMigrationAttemptUtc, |
| | 23 | 108 | | LastStorageMigrationError = $lastStorageMigrationError, |
| | 23 | 109 | | UpdatedAt = CURRENT_TIMESTAMP |
| | 23 | 110 | | WHERE Id = 1;"; |
| | 23 | 111 | | cmd.Parameters.AddWithValue("$hash", header.LegacyPasswordHash); |
| | 23 | 112 | | cmd.Parameters.AddWithValue("$formatVersion", header.FormatVersion); |
| | 23 | 113 | | cmd.Parameters.AddWithValue("$kdfIdentifier", header.KdfIdentifier); |
| | 23 | 114 | | cmd.Parameters.AddWithValue("$kdfParameters", header.KdfParameters.Serialize()); |
| | 23 | 115 | | cmd.Parameters.AddWithValue("$salt", header.Salt); |
| | 23 | 116 | | cmd.Parameters.AddWithValue("$wrappedVaultKey", (object?)header.WrappedVaultKey ?? DBNull.Value); |
| | 23 | 117 | | cmd.Parameters.AddWithValue("$usesLegacyKeyMaterial", header.UsesLegacyKeyMaterial ? 1 : 0); |
| | 23 | 118 | | cmd.Parameters.AddWithValue("$requiresStorageCompaction", header.RequiresStorageCompaction ? 1 : 0); |
| | 23 | 119 | | cmd.Parameters.AddWithValue("$lastStorageCompactionAttemptUtc", (object?)header.LastStorageCompactionAttemptUtc?.ToS |
| | 23 | 120 | | cmd.Parameters.AddWithValue("$lastStorageCompactionFailureKind", (int)header.LastStorageCompactionFailureKind); |
| | 23 | 121 | | cmd.Parameters.AddWithValue("$lastStorageCompactionError", (object?)header.LastStorageCompactionError ?? DBNull.Valu |
| | 23 | 122 | | cmd.Parameters.AddWithValue("$storageMigrationState", (int)header.StorageMigrationState); |
| | 23 | 123 | | cmd.Parameters.AddWithValue("$storageMigrationTargetMode", (int)header.StorageMigrationTargetMode); |
| | 23 | 124 | | cmd.Parameters.AddWithValue("$lastStorageMigrationAttemptUtc", (object?)header.LastStorageMigrationAttemptUtc?.ToStr |
| | 23 | 125 | | cmd.Parameters.AddWithValue("$lastStorageMigrationError", (object?)header.LastStorageMigrationError ?? DBNull.Value) |
| | 23 | 126 | | cmd.ExecuteNonQuery(); |
| | 46 | 127 | | } |
| | | 128 | | |
| | | 129 | | /// <inheritdoc/> |
| | | 130 | | public void Delete() |
| | 3 | 131 | | { |
| | 3 | 132 | | using var conn = GetConnection(); |
| | 3 | 133 | | using var cmd = conn.CreateCommand(); |
| | 3 | 134 | | cmd.CommandText = "DELETE FROM MasterKey WHERE Id = 1;"; |
| | 3 | 135 | | cmd.ExecuteNonQuery(); |
| | 6 | 136 | | } |
| | | 137 | | |
| | | 138 | | #endregion |
| | | 139 | | } |