Check-in [d12f13c977]
Overview
Comment:Added verification system so that passwords are only updated if they currently contain a valid password
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: d12f13c9776d4b5d96bd544188013f1aeb985a3c
User & Date: rkeene on 2016-03-15 18:28:46
Other Links: manifest | tags
Context
2016-03-15
18:32
Added a "whoami" command to determine the current users check-in: 92d30d85bb user: rkeene tags: trunk
18:28
Added verification system so that passwords are only updated if they currently contain a valid password check-in: d12f13c977 user: rkeene tags: trunk
2016-03-11
21:45
Added README check-in: 47680a1e16 user: rkeene tags: trunk
Changes

Modified build/pre.sh from [7d2c051364] to [7789bf77c1].

16
17
18
19
20
21
22
23

24
25
26
27
28
29
30
16
17
18
19
20
21
22

23
24
25
26
27
28
29
30







-
+








rm -rf lib

if [ "$1" = 'clean' -o "$1" = 'distclean' ]; then
	exit 0
fi

"${TCLKIT:-tclkit}" "${ourdir}/teapot-client.kit" get . tcl tcl pki aes
"${TCLKIT:-tclkit}" "${ourdir}/teapot-client.kit" get . tcl tcl pki aes sha256

for platform in linux-ix86 linux-x86_64 macosx-ix86 macosx-x86_64 win32-ix86 win32-x86_64; do
	dl_platform="${platform}"
	vers='0.9.9'
	case "${platform}" in
		macosx-*)
			vers='0.9.6'

Modified hunter2 from [7cb1f824eb] to [e039ea66c1].

100
101
102
103
104
105
106

107
108
109
110
111
112
113
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114







+







lappend ::auto_path [file join [file dirname [info script]] lib [platform::identify]]
lappend ::auto_path [file join [file dirname [info script]] lib [platform::generic]]
lappend ::auto_path [file join [file dirname [info script]] lib]

package require pki
package require pki::pkcs11
package require aes
package require sha256

# Backports for older versions of "pki"
proc ::pki::pkcs::parse_public_key {key {password ""}} {
        array set parsed_key [::pki::_parse_pem $key "-----BEGIN PUBLIC KEY-----" "-----END PUBLIC KEY-----" $password]

        set key_seq $parsed_key(data)

203
204
205
206
207
208
209

































210
211
212
213
214







215

216
217
218
219
220




221
222
223


224



225

226
227

228
229

230



231


232
233
234
235
236
237
238
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255

256
257




258
259
260
261
262


263
264
265
266
267
268

269
270

271
272

273
274
275
276
277

278
279
280
281
282
283
284
285
286







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





+
+
+
+
+
+
+
-
+

-
-
-
-
+
+
+
+

-
-
+
+

+
+
+
-
+

-
+

-
+

+
+
+
-
+
+








			lappend slotInfo [list handle $handle id $slotID prompt $slotPromptForPIN cert $cert pubkey $pubkey]
		}
	}

	return $slotInfo
}

proc _verifyPassword {name password} {
	set publicKeys [list]

	db eval {SELECT publicKey, verification FROM passwords WHERE name = $name} row {
		set salt [dict get $row(verification) salt]
		set hashAlgorithm [dict get $row(verification) hashAlgorithm]
		set publicKey $row(publicKey)

		set plaintext "${salt}|${publicKey}|${password}"

		switch -- $hashAlgorithm {
			"sha256" {
				set verificationHash [sha2::sha256 -hex -- $plaintext]
			}
			default {
				return -code error "Unknown hashing algorithm: $hashAlgorithm"
			}
		}

		set row(verificationHash) [dict get $row(verification) hash]

		if {$verificationHash ne $row(verificationHash)} {
			puts stderr "FAILED: verification failed for $name with public key $publicKey -- it will not get the new password."

			continue
		}

		lappend publicKeys $publicKey
	}

	return $publicKeys
}

proc _addPassword {name password publicKeys} {
	set fd [open "/dev/urandom" r]
	fconfigure $fd -translation binary

	set keySize 16

	# Pad the password with 0 bytes until it is a multiple of the key size
	set blockPassword $password
	append blockPassword [string repeat "\x00" [expr {-[string length $password] % $keySize}]]

	db transaction {
	db eval {DELETE FROM passwords WHERE name = $name;}
		db eval {DELETE FROM passwords WHERE name = $name;}

	foreach publicKey $publicKeys {
		set key [read $fd 16]
		if {[string length $key] != 16} {
			close $fd
		foreach publicKey $publicKeys {
			set key [read $fd $keySize]
			if {[string length $key] != $keySize} {
				close $fd

			return -code error "ERROR: Short read from random device"
		}
				return -code error "ERROR: Short read from random device"
			}

			set salt [read $fd $keySize]
			set salt [binary encode base64 $salt]

		set publicKeyItem [::pki::pkcs::parse_public_key [binary decode base64 $publicKey]]
			set publicKeyItem [::pki::pkcs::parse_public_key [binary decode base64 $publicKey]]

		set encryptedKey [binary encode base64 [::pki::encrypt -pub -binary -- $key $publicKeyItem]]
			set encryptedKey [binary encode base64 [::pki::encrypt -pub -binary -- $key $publicKeyItem]]

		set encryptedPass [binary encode base64 [::aes::aes -dir encrypt -key $key -- $password]]
			set encryptedPass [binary encode base64 [::aes::aes -dir encrypt -key $key -- $blockPassword]]

			set verificationHash [sha2::sha256 -hex -- "${salt}|${publicKey}|${password}"]
			set verification [list salt $salt hashAlgorithm sha256 hash $verificationHash]

		db eval {INSERT INTO passwords (name, encryptedPass, encryptedKey, publicKey) VALUES ($name, @encryptedPass, @encryptedKey, @publicKey);}
			db eval {INSERT INTO passwords (name, encryptedPass, encryptedKey, publicKey, verification) VALUES ($name, @encryptedPass, @encryptedKey, @publicKey, @verification);}
		}
	}

	close $fd
}

proc _prompt {prompt} {
	puts -nonewline $prompt
277
278
279
280
281
282
283
284

285
286
287
288
289
290
291
325
326
327
328
329
330
331

332
333
334
335
336
337
338
339







-
+







			set prompted($slotInfo(id)) 1
		}

		db eval {SELECT encryptedPass, encryptedKey FROM passwords WHERE name = $name AND publicKey = $pubkey;} row {
			set key [::pki::decrypt -binary -priv -- [binary decode base64 $row(encryptedKey)] $slotInfo(cert)]
			set password [::aes::aes -dir decrypt -key $key -- [binary decode base64 $row(encryptedPass)]]

			return $password
			return [string trimright $password "\x00"]
		}
	}

	return -code error "No valid keys"
}

proc _modifyPublicKeys {passwordName userNames sql} {
452
453
454
455
456
457
458

459
460





461
462
463
464
465
466
467
500
501
502
503
504
505
506
507


508
509
510
511
512
513
514
515
516
517
518
519







+
-
-
+
+
+
+
+







}

proc updatePassword {passwordName password} {
	if {$password eq ""} {
		set password [_prompt "Please enter the new password: "]
	}

	set oldPassword [_getPassword $passwordName]
	db eval {SELECT publicKey FROM passwords WHERE name = $passwordName;} row {
		lappend publicKeys $row(publicKey)

	set publicKeys [_verifyPassword $passwordName $oldPassword]

	if {[llength $publicKeys] == 0} {
		puts stderr "Warning: This will delete the password since there are no valid public keys."
	}

	_addPassword $passwordName $password $publicKeys
}

proc deletePassword {passwordName} {
	db eval {DELETE FROM passwords WHERE name = $passwordName;}
511
512
513
514
515
516
517
518

519
520
521
522
523
524
525
563
564
565
566
567
568
569

570
571
572
573
574
575
576
577







-
+








### MAIN

sqlite3 db $passwordFile

db eval {
	CREATE TABLE IF NOT EXISTS users(name, publicKey BLOB);
	CREATE TABLE IF NOT EXISTS passwords(name, encryptedPass BLOB, encryptedKey BLOB, publicKey BLOB);
	CREATE TABLE IF NOT EXISTS passwords(name, encryptedPass BLOB, encryptedKey BLOB, publicKey BLOB, verification BLOB);
}

if {$action in $validCommands} {
	if {[catch {
		$action {*}$argv
	} error]} {
		puts stderr "Error: $error"