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

rm -rf lib

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

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

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'







|







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 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
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


# 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)








>







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

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

	return $slotInfo
}


































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








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

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

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




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

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

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




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

	}

	close $fd
}

proc _prompt {prompt} {
	puts -nonewline $prompt







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





>
>
>
>
>
>
>
|

|
|
|
|

|
|

>
>
>
|

|

|

>
>
>
|
>







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;}

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

				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 encryptedKey [binary encode base64 [::pki::encrypt -pub -binary -- $key $publicKeyItem]]

			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, 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
			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 -code error "No valid keys"
}

proc _modifyPublicKeys {passwordName userNames sql} {







|







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 [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
}

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


	db eval {SELECT publicKey FROM passwords WHERE name = $passwordName;} row {

		lappend publicKeys $row(publicKey)


	}

	_addPassword $passwordName $password $publicKeys
}

proc deletePassword {passwordName} {
	db eval {DELETE FROM passwords WHERE name = $passwordName;}







>
|
>
|
>
>







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]

	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

### 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);
}

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







|







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, verification BLOB);
}

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