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