annotate src/main/kotlin/name/blackcap/passman/Encryption.kt @ 4:02b101422726

See bugs.
author David Barts <n5jrn@me.com>
date Sun, 11 Sep 2022 20:44:59 -0700
parents a6cfdffcaa94
children 711cc42e96d7
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
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
11 import javax.crypto.spec.IvParameterSpec
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
12 import javax.crypto.spec.PBEKeySpec
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
13 import javax.crypto.spec.SecretKeySpec
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
14 import javax.security.auth.Destroyable
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
15
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
16 class Encryption(passwordIn: CharArray, saltIn: ByteArray) : Destroyable {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
17 private companion object {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
18 const val ITERATIONS = 390000
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
19 const val IV_LENGTH = 16
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
20 const val KEY_LENGTH = 256
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
21 const val ENCRYPTION_ALGORITHM = "AES/CBC/PKCS5Padding"
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
22 const val KEY_ALGORITHM = "AES"
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
23 const val SECRET_KEY_FACTORY = "PBKDF2WithHmacSHA256"
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
24 val CHARSET : Charset = StandardCharsets.UTF_8
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
25 val ZERO_IV = ByteArray(IV_LENGTH).apply { clear() }
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
26 }
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 private val secretKey = getSecretKey(passwordIn, saltIn)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
29 private val secureRandom = SecureRandom()
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
30
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
31 fun encrypt(plaintext: CharArray): ByteArray {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
32 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
33 return encrypt(plaintext, iv)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
34 }
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 private fun encrypt(plaintext: CharArray, iv: ByteArray): ByteArray {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
37 val cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
38 cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivFactory(iv))
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
39 val inBuffer = CHARSET.encode(CharBuffer.wrap(plaintext))
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
40 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
41 outBuffer.put(iv)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
42 cipher.doFinal(inBuffer, outBuffer)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
43 return outBuffer.array()
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
44 }
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 fun encryptFromString(plaintext: String): ByteArray = encrypt(plaintext.toCharArray())
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
47
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
48 fun encryptFromString0(plaintext: String): ByteArray =
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
49 encrypt(plaintext.toCharArray(), ZERO_IV)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
50
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
51 fun decrypt(ciphertext: ByteArray): CharArray {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
52 val cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
53 cipher.init(Cipher.DECRYPT_MODE, secretKey, ivFactory(ciphertext))
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
54 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
55 val charBuffer = CHARSET.decode(ByteBuffer.wrap(bytes))
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
56 bytes.clear()
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
57 val ret = CharArray(charBuffer.limit())
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
58 charBuffer.run {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
59 rewind()
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
60 get(ret)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
61 zero()
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
62 }
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
63 return ret
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
64 }
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 fun decryptToString(ciphertext: ByteArray): String = String(decrypt(ciphertext))
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
67
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
68 override fun destroy() {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
69 secretKey.destroy()
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
70 }
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 override fun isDestroyed(): Boolean {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
73 return secretKey.isDestroyed
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
74 }
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 protected fun finalize() {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
77 destroy()
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
78 }
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 private fun getSecretKey(password: CharArray, salt: ByteArray): SecretKey {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
81 val factory = SecretKeyFactory.getInstance(SECRET_KEY_FACTORY)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
82 val spec = PBEKeySpec(password, salt, ITERATIONS, KEY_LENGTH)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
83 return SecretKeySpec(factory.generateSecret(spec).encoded, KEY_ALGORITHM)
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
84 }
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 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
87 }
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 fun ByteArray.clear() = indices.forEach { this[it] = 0 }
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
90
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
91 fun CharArray.clear() = indices.forEach { this[it] = '\u0000' }
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
92
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
93 fun CharBuffer.zero() {
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
94 clear()
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
95 array().clear()
a6cfdffcaa94 Initial commit, incomplete but it runs sorta.
David Barts <n5jrn@me.com>
parents:
diff changeset
96 }