annotate src/main/kotlin/name/blackcap/passman/Encryption.kt @ 20:4391afcf6bd0

Fix more bugs, correct more bad tests.
author David Barts <n5jrn@me.com>
date Sun, 30 Jun 2024 22:28:52 -0700
parents 698c4a3d758d
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
0
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
1 package name.blackcap.passman
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
2
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
3 import java.nio.ByteBuffer
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
4 import java.nio.CharBuffer
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
5 import java.nio.charset.Charset
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
6 import java.nio.charset.StandardCharsets
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
7 import java.security.SecureRandom
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
8 import javax.crypto.Cipher
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
9 import javax.crypto.SecretKey
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
10 import javax.crypto.SecretKeyFactory
6
711cc42e96d7 Got the list subcommand working, but needs efficiency improvements.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
11
0
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
12 import javax.crypto.spec.IvParameterSpec
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
13 import javax.crypto.spec.PBEKeySpec
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
14 import javax.crypto.spec.SecretKeySpec
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
15 import javax.security.auth.Destroyable
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
16
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
17 class Encryption(passwordIn: CharArray, saltIn: ByteArray) : Destroyable {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
18 private companion object {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
19 const val ITERATIONS = 390000
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
20 const val IV_LENGTH = 16
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
21 const val KEY_LENGTH = 256
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
22 const val ENCRYPTION_ALGORITHM = "AES/CBC/PKCS5Padding"
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
23 const val KEY_ALGORITHM = "AES"
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
24 const val SECRET_KEY_FACTORY = "PBKDF2WithHmacSHA256"
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
25 val CHARSET : Charset = StandardCharsets.UTF_8
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
26 val ZERO_IV = ByteArray(IV_LENGTH).apply { clear() }
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
27 }
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
28
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
29 private val secretKey = getSecretKey(passwordIn, saltIn)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
30 private val secureRandom = SecureRandom()
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
31
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
32 fun encrypt(plaintext: CharArray): ByteArray {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
33 val iv = ByteArray(IV_LENGTH).also { secureRandom.nextBytes(it) }
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
34 return encrypt(plaintext, iv)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
35 }
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
36
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
37 private fun encrypt(plaintext: CharArray, iv: ByteArray): ByteArray {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
38 val cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
39 cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivFactory(iv))
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
40 val inBuffer = CHARSET.encode(CharBuffer.wrap(plaintext))
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
41 val outBuffer = ByteBuffer.allocate(cipher.getOutputSize(inBuffer.limit()) + IV_LENGTH)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
42 outBuffer.put(iv)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
43 cipher.doFinal(inBuffer, outBuffer)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
44 return outBuffer.array()
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
45 }
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
46
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
47 fun encryptFromString(plaintext: String): ByteArray = encrypt(plaintext.toCharArray())
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
48
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
49 fun encryptFromString0(plaintext: String): ByteArray =
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
50 encrypt(plaintext.toCharArray(), ZERO_IV)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
51
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
52 fun decrypt(ciphertext: ByteArray): CharArray {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
53 val cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
54 cipher.init(Cipher.DECRYPT_MODE, secretKey, ivFactory(ciphertext))
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
55 val bytes = cipher.doFinal(ciphertext, IV_LENGTH, ciphertext.size - IV_LENGTH)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
56 val charBuffer = CHARSET.decode(ByteBuffer.wrap(bytes))
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
57 bytes.clear()
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
58 val ret = CharArray(charBuffer.limit())
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
59 charBuffer.run {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
60 rewind()
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
61 get(ret)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
62 zero()
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
63 }
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
64 return ret
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
65 }
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
66
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
67 fun decryptToString(ciphertext: ByteArray): String = String(decrypt(ciphertext))
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
68
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
69 override fun destroy() {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
70 secretKey.destroy()
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
71 }
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
72
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
73 override fun isDestroyed(): Boolean {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
74 return secretKey.isDestroyed
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
75 }
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
76
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
77 protected fun finalize() {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
78 destroy()
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
79 }
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
80
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
81 private fun getSecretKey(password: CharArray, salt: ByteArray): SecretKey {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
82 val factory = SecretKeyFactory.getInstance(SECRET_KEY_FACTORY)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
83 val spec = PBEKeySpec(password, salt, ITERATIONS, KEY_LENGTH)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
84 return SecretKeySpec(factory.generateSecret(spec).encoded, KEY_ALGORITHM)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
85 }
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
86
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
87 private fun ivFactory(ciphertext: ByteArray) = IvParameterSpec(ciphertext, 0, IV_LENGTH)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
88 }
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
89
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
90 fun ByteArray.clear() = indices.forEach { this[it] = 0 }
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
91
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
92 fun CharArray.clear() = indices.forEach { this[it] = '\u0000' }
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
93
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
94 fun CharBuffer.zero() {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
95 clear()
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
96 array().clear()
8
698c4a3d758d Some code clean-up.
David Barts <n5jrn@me.com>
parents: 6
diff changeset
97 }