diff --git a/README.md b/README.md index 50c424cbd9fd41615f2744437614779c70e2d5b7..6e6f467c37bc42f3f18dc02929695438215b28b2 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,9 @@ An example database scheme for this might be: CREATE TABLE `storage_keys` ( `id` int(11) NOT NULL AUTO_INCREMENT, `enabled` tinyint(4) DEFAULT '1', + `version` tinyint(4) DEFAULT '1', `public_key` text, + `pwhash_algo` tinyint(4) DEFAULT '1', `pwhash_opslimit` int(11) DEFAULT NULL, `pwhash_memlimit` int(11) DEFAULT NULL, `pwhash_salt` varchar(255) DEFAULT NULL, @@ -98,6 +100,7 @@ NOTE: the database MUST NOT store the argon2 digest, since this value is the secret key that unlocks `locked_secretbox`. This is very different than how password hashing for authentication works, where the digest and parameters are stored. +pwhash_algo is 0 for libsodium <= 1.0.14 and 1 for libsodium >= 1.0.15 Dovecot Configuration ------------------------------------- @@ -157,9 +160,11 @@ Here is a dovecot SQL query configuration that will work with the sample CONCAT('/maildir/', mailboxes.maildir) AS userdb_home, \ REPLACE('%w', '%%', '%%%%') AS userdb_trees_password, \ storage_keys.enabled AS userdb_trees_enabled, \ + storage_keys.version AS userdb_trees_version, \ storage_keys.public_key AS userdb_trees_public_key, \ storage_keys.locked_secretbox AS userdb_trees_locked_secretbox, \ storage_keys.sk_nonce AS userdb_trees_sk_nonce, \ + storage_keys.pwhash_algo AS userdb_trees_pwhash_algo, \ storage_keys.pwhash_opslimit AS userdb_trees_pwhash_opslimit, \ storage_keys.pwhash_memlimit AS userdb_trees_pwhash_memlimit, \ storage_keys.pwhash_salt AS userdb_trees_pwhash_salt \ @@ -180,6 +185,7 @@ Here is a dovecot SQL query configuration that will work with the sample 8 AS gid, \ CONCAT('/maildir/', mailboxes.maildir) AS home, \ storage_keys.enabled AS trees_enabled, \ + storage_keys.version AS userdb_trees_version, \ storage_keys.public_key AS trees_public_key, \ CONCAT('*:bytes=', mailboxes.quota) AS quota_rule \ FROM mailboxes \ diff --git a/bin/trees-create b/bin/trees-create index 8c4f8856d0af9c17c882ec5c177f2a183d91a52e..2bb1eee90ebc1c80df0e8f5c1a03fdfe4908105f 100755 --- a/bin/trees-create +++ b/bin/trees-create @@ -27,16 +27,20 @@ end def usage puts "USAGE:" puts " trees-create --password PASSWORD [OPTIONS]" + puts " trees-create --password PASSWORD --old-password PASSWORD OPTIONS" puts puts "OPTIONS may include:" - puts " --opslimit OPSLIMIT -- argon2 ops limit, integer in 3..10, or one of" - puts " 'interactive', 'moderate', 'sensitive'" - puts " --memlimit MEMLIMIT -- argon2 memory limit, in bytes, or one of" - puts " 'interactive', 'moderate', 'sensitive'" - puts " --salt SALT -- hex encoded salt for password digest," - puts " #{StorageKey::SALT_BYTES} bytes in length" - puts " --nonce NONCE -- hex encoded nonce for secretbox encryption of" - puts " private key, #{StorageKey::NONCE_BYTES} bytes in length" + puts " --opslimit OPSLIMIT -- argon2 ops limit, integer in 3..10, or one of" + puts " 'interactive', 'moderate', 'sensitive'" + puts " --memlimit MEMLIMIT -- argon2 memory limit, in bytes, or one of" + puts " 'interactive', 'moderate', 'sensitive'" + puts " --salt SALT -- hex encoded salt for password digest," + puts " #{StorageKey::SALT_BYTES} bytes in length" + puts " --nonce NONCE -- hex encoded nonce for secretbox encryption of" + puts " private key, #{StorageKey::NONCE_BYTES} bytes in length" + puts " --secretbox SECRETBOX -- hex encoded secretbox" + puts + puts "for password change all options are required" exit 1 end @@ -46,6 +50,9 @@ def main while ARGV.any? case ARGV.first + when "--old-password" + ARGV.shift + old_password = ARGV.shift when "--password" ARGV.shift password = ARGV.shift @@ -61,12 +68,19 @@ def main when "--nonce" ARGV.shift st.sk_nonce = ARGV.shift + when "--secretbox" + ARGV.shift + st.locked_secretbox = ARGV.shift else usage end end usage unless password - st.generate_new_keypair(password) + if old_password.nil? + st.generate_new_keypair(password) + else + st.change_password(old_password, password) + end puts st.to_s end @@ -121,6 +135,14 @@ class StorageKey ) end + def change_password(old_password, password) + key = self.decrypt_key(old_password) + self.encrypt_key( + key: key, + password: password + ) + end + def to_s attrs = [:public_key, :locked_secretbox, :sk_nonce, :pwhash_opslimit, :pwhash_memlimit, :pwhash_salt] diff --git a/src/trees-plugin.h b/src/trees-plugin.h index c6fd36e26191d3d4045510b767b5febdf5279433..d3da04f14f84f5e13fca269392fa5498b139504c 100644 --- a/src/trees-plugin.h +++ b/src/trees-plugin.h @@ -33,9 +33,11 @@ trees_pluging_pwhash_map(int value) case 0: /* argon2i, libsodium <= 1.0.14. */ return crypto_pwhash_ALG_ARGON2I13; +#ifdef crypto_pwhash_ALG_ARGON2IDI3 case 1: /* argon2id, libsodium >= 1.0.15 */ return crypto_pwhash_ALG_ARGON2ID13; +#endif default: return -1; }