-my %ATTRS = (
- sig1 => KDBX_SIG1,
- sig2 => KDBX_SIG2_2,
- version => KDBX_VERSION_3_1,
- headers => sub { +{} },
- inner_headers => sub { +{} },
- meta => sub { +{} },
- binaries => sub { +{} },
- deleted_objects => sub { +{} },
- raw => undef,
-);
-my %ATTRS_HEADERS = (
- HEADER_COMMENT() => '',
- HEADER_CIPHER_ID() => CIPHER_UUID_CHACHA20,
- HEADER_COMPRESSION_FLAGS() => COMPRESSION_GZIP,
- HEADER_MASTER_SEED() => sub { random_bytes(32) },
- # HEADER_TRANSFORM_SEED() => sub { random_bytes(32) },
- # HEADER_TRANSFORM_ROUNDS() => 100_000,
- HEADER_ENCRYPTION_IV() => sub { random_bytes(16) },
- # HEADER_INNER_RANDOM_STREAM_KEY() => sub { random_bytes(32) }, # 64?
- HEADER_STREAM_START_BYTES() => sub { random_bytes(32) },
- # HEADER_INNER_RANDOM_STREAM_ID() => STREAM_ID_CHACHA20,
- HEADER_KDF_PARAMETERS() => sub {
- +{
- KDF_PARAM_UUID() => KDF_UUID_AES,
- KDF_PARAM_AES_ROUNDS() => $_[0]->headers->{+HEADER_TRANSFORM_ROUNDS} // KDF_DEFAULT_AES_ROUNDS,
- KDF_PARAM_AES_SEED() => $_[0]->headers->{+HEADER_TRANSFORM_SEED} // random_bytes(32),
- };
- },
- # HEADER_PUBLIC_CUSTOM_DATA() => sub { +{} },
-);
-my %ATTRS_META = (
- generator => '',
- header_hash => '',
- database_name => '',
- database_name_changed => sub { scalar gmtime },
- database_description => '',
- database_description_changed => sub { scalar gmtime },
- default_username => '',
- default_username_changed => sub { scalar gmtime },
- maintenance_history_days => 0,
- color => '',
- master_key_changed => sub { scalar gmtime },
- master_key_change_rec => -1,
- master_key_change_force => -1,
- # memory_protection => sub { +{} },
- custom_icons => sub { +{} },
- recycle_bin_enabled => true,
- recycle_bin_uuid => "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
- recycle_bin_changed => sub { scalar gmtime },
- entry_templates_group => "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
- entry_templates_group_changed => sub { scalar gmtime },
- last_selected_group => "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
- last_top_visible_group => "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
- history_max_items => HISTORY_DEFAULT_MAX_ITEMS,
- history_max_size => HISTORY_DEFAULT_MAX_SIZE,
- settings_changed => sub { scalar gmtime },
- # binaries => sub { +{} },
- # custom_data => sub { +{} },
-);
-my %ATTRS_MEMORY_PROTECTION = (
- protect_title => false,
- protect_username => false,
- protect_password => true,
- protect_url => false,
- protect_notes => false,
- # auto_enable_visual_hiding => false,
-);
-
-while (my ($attr, $default) = each %ATTRS) {
- no strict 'refs'; ## no critic (ProhibitNoStrict)
- *{$attr} = sub {
- my $self = shift;
- $self->{$attr} = shift if @_;
- $self->{$attr} //= (ref $default eq 'CODE') ? $default->($self) : $default;
+has sig1 => KDBX_SIG1, coerce => \&to_number;
+has sig2 => KDBX_SIG2_2, coerce => \&to_number;
+has version => KDBX_VERSION_3_1, coerce => \&to_number;
+has headers => {};
+has inner_headers => {};
+has meta => {};
+has binaries => {};
+has deleted_objects => {};
+has raw => coerce => \&to_string;
+
+# HEADERS
+has 'headers.comment' => '', coerce => \&to_string;
+has 'headers.cipher_id' => CIPHER_UUID_CHACHA20, coerce => \&to_uuid;
+has 'headers.compression_flags' => COMPRESSION_GZIP, coerce => \&to_compression_constant;
+has 'headers.master_seed' => sub { random_bytes(32) }, coerce => \&to_string;
+has 'headers.encryption_iv' => sub { random_bytes(16) }, coerce => \&to_string;
+has 'headers.stream_start_bytes' => sub { random_bytes(32) }, coerce => \&to_string;
+has 'headers.kdf_parameters' => sub {
+ +{
+ KDF_PARAM_UUID() => KDF_UUID_AES,
+ KDF_PARAM_AES_ROUNDS() => $_[0]->headers->{+HEADER_TRANSFORM_ROUNDS} // KDF_DEFAULT_AES_ROUNDS,
+ KDF_PARAM_AES_SEED() => $_[0]->headers->{+HEADER_TRANSFORM_SEED} // random_bytes(32),