Module: Lich::Common::GUI::MasterPasswordManager

Defined in:
documented/common/gui/master_password_manager.rb

Overview

Manages the storage and retrieval of the master password This module provides functionality to store, retrieve, validate, and delete the master password using the system’s keychain.

Examples:

Storing a master password

MasterPasswordManager.store_master_password("my_secret_password")

Constant Summary collapse

KEYCHAIN_SERVICE =

The service name used for storing the master password in the keychain.

'lich5.master_password'
VALIDATION_ITERATIONS =

The number of iterations used for password validation.

100_000
VALIDATION_KEY_LENGTH =

The length of the key used for password validation.

32
VALIDATION_SALT_PREFIX =

The prefix used for the salt in password validation.

'lich5-master-password-validation-v1'

Class Method Summary collapse

Class Method Details

.create_validation_test(master_password) ⇒ Hash

Creates a validation test for the master password.

Examples:

Creating a validation test

validation_test = MasterPasswordManager.create_validation_test("my_secret_password")

Parameters:

  • master_password (String)

    The master password to validate

Returns:

  • (Hash)

    a hash containing the validation salt and hash



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'documented/common/gui/master_password_manager.rb', line 96

def self.create_validation_test(master_password)
  random_salt = SecureRandom.random_bytes(16)
  full_salt = VALIDATION_SALT_PREFIX + random_salt

  validation_key = OpenSSL::PKCS5.pbkdf2_hmac(
    master_password, full_salt, VALIDATION_ITERATIONS,
    VALIDATION_KEY_LENGTH, OpenSSL::Digest.new('SHA256')
  )

  validation_hash = OpenSSL::Digest::SHA256.digest(validation_key)

  {
    'validation_salt'    => Base64.strict_encode64(random_salt),
    'validation_hash'    => Base64.strict_encode64(validation_hash),
    'validation_version' => 1
  }
end

.delete_master_passwordBoolean

Deletes the master password from the system’s keychain.

Examples:

Deleting the master password

MasterPasswordManager.delete_master_password

Returns:

  • (Boolean)

    true if the password was deleted successfully, false otherwise

Raises:

  • (StandardError)

    if an error occurs during deletion



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'documented/common/gui/master_password_manager.rb', line 148

def self.delete_master_password
  return false unless keychain_available?

  if OS.mac?
    delete_macos_keychain
  elsif OS.linux?
    delete_linux_keychain
  elsif OS.windows?
    delete_windows_keychain
  else
    false
  end
rescue StandardError => e
  Lich.log "error: Failed to delete master password: #{e.message}"
  false
end

.keychain_available?Boolean

Checks if the keychain is available on the current operating system.

Examples:

Checking keychain availability

if MasterPasswordManager.keychain_available?
  puts "Keychain is available"
end

Returns:

  • (Boolean)

    true if the keychain is available, false otherwise



34
35
36
37
38
39
40
41
42
43
44
# File 'documented/common/gui/master_password_manager.rb', line 34

def self.keychain_available?
  if OS.mac?
    macos_keychain_available?
  elsif OS.linux?
    linux_keychain_available?
  elsif OS.windows?
    windows_keychain_available?
  else
    false
  end
end

.retrieve_master_passwordString?

Retrieves the master password from the system’s keychain.

Examples:

Retrieving the master password

password = MasterPasswordManager.retrieve_master_password

Returns:

  • (String, nil)

    the master password if found, nil otherwise

Raises:

  • (StandardError)

    if an error occurs during retrieval



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'documented/common/gui/master_password_manager.rb', line 74

def self.retrieve_master_password
  return nil unless keychain_available?

  if OS.mac?
    retrieve_macos_keychain
  elsif OS.linux?
    retrieve_linux_keychain
  elsif OS.windows?
    retrieve_windows_keychain
  else
    nil
  end
rescue StandardError => e
  Lich.log "error: Failed to retrieve master password: #{e.message}"
  nil
end

.store_master_password(master_password) ⇒ Boolean

Stores the master password in the system’s keychain.

Examples:

Storing a master password

MasterPasswordManager.store_master_password("my_secret_password")

Parameters:

  • master_password (String)

    The master password to store

Returns:

  • (Boolean)

    true if the password was stored successfully, false otherwise

Raises:

  • (StandardError)

    if an error occurs during storage



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'documented/common/gui/master_password_manager.rb', line 52

def self.store_master_password(master_password)
  return false unless keychain_available?

  if OS.mac?
    store_macos_keychain(master_password)
  elsif OS.linux?
    store_linux_keychain(master_password)
  elsif OS.windows?
    store_windows_keychain(master_password)
  else
    false
  end
rescue StandardError => e
  Lich.log "error: Failed to store master password: #{e.message}"
  false
end

.validate_master_password(entered_password, validation_test) ⇒ Boolean

Validates the entered master password against the stored validation test.

Examples:

Validating a master password

is_valid = MasterPasswordManager.validate_master_password("my_secret_password", validation_test)

Parameters:

  • entered_password (String)

    The password entered by the user

  • validation_test (Hash)

    The validation test hash

Returns:

  • (Boolean)

    true if the password is valid, false otherwise

Raises:

  • (StandardError)

    if an error occurs during validation



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'documented/common/gui/master_password_manager.rb', line 121

def self.validate_master_password(entered_password, validation_test)
  return false unless validation_test.is_a?(Hash)
  return false unless validation_test['validation_salt'] && validation_test['validation_hash']

  begin
    random_salt = Base64.strict_decode64(validation_test['validation_salt'])
    stored_hash = Base64.strict_decode64(validation_test['validation_hash'])
    full_salt = VALIDATION_SALT_PREFIX + random_salt

    validation_key = OpenSSL::PKCS5.pbkdf2_hmac(
      entered_password, full_salt, VALIDATION_ITERATIONS,
      VALIDATION_KEY_LENGTH, OpenSSL::Digest.new('SHA256')
    )

    computed_hash = OpenSSL::Digest::SHA256.digest(validation_key)
    secure_compare(computed_hash, stored_hash)
  rescue StandardError => e
    Lich.log "error: Validation failed: #{e.message}"
    false
  end
end