mirror of https://github.com/iSoron/uhabits.git
parent
e26b643423
commit
1567e2c0ad
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.platform.crypto
|
||||||
|
|
||||||
|
class Bip39(private val wordlist: List<String>, private val crypto: Crypto) {
|
||||||
|
private fun computeChecksum(entropy: List<Boolean>): List<Boolean> {
|
||||||
|
val sha256 = crypto.sha256()
|
||||||
|
var byte = 0
|
||||||
|
entropy.forEachIndexed { i, bit ->
|
||||||
|
byte = byte shl 1
|
||||||
|
if (bit) byte += 1
|
||||||
|
if (i.rem(8) == 7) {
|
||||||
|
sha256.update(byte.toByte())
|
||||||
|
byte = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sha256.finalize().toBits().subList(0, entropy.size / 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun encode(entropy: ByteArray): List<String> {
|
||||||
|
val entropyBits = entropy.toBits()
|
||||||
|
val msg = entropyBits + computeChecksum(entropyBits)
|
||||||
|
var wordIndex = 0
|
||||||
|
val mnemonic = mutableListOf<String>()
|
||||||
|
msg.forEachIndexed { i, bit ->
|
||||||
|
wordIndex = wordIndex shl 1
|
||||||
|
if (bit) wordIndex += 1
|
||||||
|
if (i.rem(11) == 10) {
|
||||||
|
mnemonic.add(wordlist[wordIndex])
|
||||||
|
wordIndex = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mnemonic
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decode(mnemonic: List<String>): ByteArray {
|
||||||
|
val bits = mutableListOf<Boolean>()
|
||||||
|
mnemonic.forEach { word ->
|
||||||
|
val wordBits = mutableListOf<Boolean>()
|
||||||
|
var wordIndex = wordlist.binarySearch(word)
|
||||||
|
if (wordIndex < 0) throw InvalidWordException(word)
|
||||||
|
for (it in 0..10) {
|
||||||
|
wordBits.add(wordIndex.rem(2) == 1)
|
||||||
|
wordIndex = wordIndex shr 1
|
||||||
|
}
|
||||||
|
bits.addAll(wordBits.reversed())
|
||||||
|
}
|
||||||
|
if (bits.size.rem(33) != 0) throw InvalidMnemonicLength()
|
||||||
|
val checksumSize = bits.size / 33
|
||||||
|
val checksum = bits.subList(bits.size - checksumSize, bits.size)
|
||||||
|
val entropy = bits.subList(0, bits.size - checksumSize)
|
||||||
|
if (computeChecksum(entropy) != checksum) throw InvalidChecksumException()
|
||||||
|
return byteArray(entropy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InvalidChecksumException : Exception()
|
||||||
|
class InvalidWordException(word: String) : Exception(word)
|
||||||
|
class InvalidMnemonicLength : Exception()
|
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.platform.crypto
|
||||||
|
|
||||||
|
class Key(val bytes: ByteArray)
|
||||||
|
|
||||||
|
interface Crypto {
|
||||||
|
fun sha256(): Sha256
|
||||||
|
fun hmacSha256(): HmacSha256
|
||||||
|
fun aesGcm(key: Key): AesGcm
|
||||||
|
fun secureRandomBytes(numBytes: Int): ByteArray
|
||||||
|
|
||||||
|
fun generateKey(): Key {
|
||||||
|
return Key(secureRandomBytes(32))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deriveKey(master: Key, subkeyName: String): Key {
|
||||||
|
val mac = hmacSha256()
|
||||||
|
mac.init(master.bytes)
|
||||||
|
mac.update(subkeyName)
|
||||||
|
return Key(mac.finalize())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Sha256 {
|
||||||
|
fun update(byte: Byte)
|
||||||
|
fun finalize(): ByteArray
|
||||||
|
}
|
||||||
|
|
||||||
|
interface HmacSha256 {
|
||||||
|
fun init(key: ByteArray)
|
||||||
|
fun update(byte: Byte)
|
||||||
|
fun finalize(): ByteArray
|
||||||
|
|
||||||
|
fun update(msg: String) {
|
||||||
|
for (b in msg.encodeToByteArray()) {
|
||||||
|
update(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AesGcm {
|
||||||
|
fun encrypt(msg: ByteArray, iv: ByteArray): ByteArray
|
||||||
|
fun decrypt(cipherText: ByteArray): ByteArray
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Byte.toBits(): List<Boolean> = (7 downTo 0).map { (toInt() and (1 shl it)) != 0 }
|
||||||
|
fun ByteArray.toBits(): List<Boolean> = flatMap { it.toBits() }
|
||||||
|
fun byteArrayOfInts(vararg b: Int) = b.map { it.toByte() }.toByteArray()
|
||||||
|
fun byteArray(bits: List<Boolean>): ByteArray {
|
||||||
|
var byte = 0
|
||||||
|
val bytes = ByteArray(bits.size / 8)
|
||||||
|
bits.forEachIndexed { i, b ->
|
||||||
|
byte = byte shl 1
|
||||||
|
if (b) byte += 1
|
||||||
|
if (i.rem(8) == 7) {
|
||||||
|
bytes[i / 8] = byte.toByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bytes
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.platform.crypto
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import java.security.MessageDigest
|
||||||
|
import java.security.SecureRandom
|
||||||
|
import javax.crypto.Cipher
|
||||||
|
import javax.crypto.Mac
|
||||||
|
import javax.crypto.spec.GCMParameterSpec
|
||||||
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
|
||||||
|
class JavaCrypto : Crypto {
|
||||||
|
override fun sha256() = JavaSha256()
|
||||||
|
override fun hmacSha256() = JavaHmacSha256()
|
||||||
|
override fun aesGcm(key: Key) = JavaAesGcm(key)
|
||||||
|
|
||||||
|
override fun secureRandomBytes(numBytes: Int): ByteArray {
|
||||||
|
val sr = SecureRandom()
|
||||||
|
val bytes = ByteArray(numBytes)
|
||||||
|
sr.nextBytes(bytes)
|
||||||
|
return bytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class JavaSha256 : Sha256 {
|
||||||
|
private val md = MessageDigest.getInstance("SHA-256")
|
||||||
|
override fun update(byte: Byte) = md.update(byte)
|
||||||
|
override fun finalize(): ByteArray = md.digest()
|
||||||
|
}
|
||||||
|
|
||||||
|
class JavaHmacSha256 : HmacSha256 {
|
||||||
|
private val mac = Mac.getInstance("HmacSHA256")
|
||||||
|
override fun init(key: ByteArray) = mac.init(SecretKeySpec(key, "HmacSHA256"))
|
||||||
|
override fun update(byte: Byte) = mac.update(byte)
|
||||||
|
override fun finalize(): ByteArray = mac.doFinal()
|
||||||
|
}
|
||||||
|
|
||||||
|
class JavaAesGcm(val key: Key) : AesGcm {
|
||||||
|
override fun encrypt(msg: ByteArray, iv: ByteArray): ByteArray {
|
||||||
|
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key.bytes, "AES"), GCMParameterSpec(128, iv))
|
||||||
|
val encrypted = cipher.doFinal(msg)
|
||||||
|
return ByteBuffer
|
||||||
|
.allocate(iv.size + encrypted.size)
|
||||||
|
.put(iv)
|
||||||
|
.put(encrypted)
|
||||||
|
.array()
|
||||||
|
}
|
||||||
|
override fun decrypt(cipherText: ByteArray): ByteArray {
|
||||||
|
val buffer = ByteBuffer.wrap(cipherText)
|
||||||
|
val iv = ByteArray(12)
|
||||||
|
buffer.get(iv)
|
||||||
|
val encrypted = ByteArray(buffer.remaining())
|
||||||
|
buffer.get(encrypted)
|
||||||
|
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key.bytes, "AES"), GCMParameterSpec(128, iv))
|
||||||
|
return cipher.doFinal(encrypted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ByteArray.toHexString(): String {
|
||||||
|
val sb = StringBuilder()
|
||||||
|
for (b in this) sb.append(String.format("%02x", b))
|
||||||
|
return sb.toString()
|
||||||
|
}
|
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.isoron.platform.crypto
|
||||||
|
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.isoron.platform.io.JavaFileOpener
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import kotlin.test.assertFailsWith
|
||||||
|
|
||||||
|
class Bip39Test {
|
||||||
|
|
||||||
|
private lateinit var bip39: Bip39
|
||||||
|
|
||||||
|
private val phrases = listOf(
|
||||||
|
listOf(
|
||||||
|
"gather",
|
||||||
|
"capable",
|
||||||
|
"since",
|
||||||
|
),
|
||||||
|
listOf(
|
||||||
|
"exit",
|
||||||
|
"churn",
|
||||||
|
"hazard",
|
||||||
|
"garage",
|
||||||
|
"hint",
|
||||||
|
"great",
|
||||||
|
),
|
||||||
|
listOf(
|
||||||
|
"exile",
|
||||||
|
"blouse",
|
||||||
|
"athlete",
|
||||||
|
"dinner",
|
||||||
|
"chef",
|
||||||
|
"home",
|
||||||
|
"destroy",
|
||||||
|
"disagree",
|
||||||
|
"select",
|
||||||
|
"eight",
|
||||||
|
"slim",
|
||||||
|
"talent",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
private val entropies = listOf(
|
||||||
|
byteArrayOfInts(0x60, 0x64, 0x3f, 0x24),
|
||||||
|
byteArrayOfInts(0x4f, 0xe5, 0x19, 0xa7, 0xaf, 0xb6, 0xbc, 0xcc),
|
||||||
|
byteArrayOfInts(
|
||||||
|
0x4f,
|
||||||
|
0xa3,
|
||||||
|
0x04,
|
||||||
|
0x38,
|
||||||
|
0x9f,
|
||||||
|
0x22,
|
||||||
|
0x74,
|
||||||
|
0xda,
|
||||||
|
0x0f,
|
||||||
|
0x09,
|
||||||
|
0xf6,
|
||||||
|
0xc3,
|
||||||
|
0x48,
|
||||||
|
0xdf,
|
||||||
|
0x2f,
|
||||||
|
0x6e,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() = runBlocking {
|
||||||
|
bip39 = Bip39(JavaFileOpener().openResourceFile("bip39/en_US.txt").lines(), JavaCrypto())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_encode_decode() {
|
||||||
|
phrases.zip(entropies).forEach { (phrase, entropy) ->
|
||||||
|
assertEquals(phrase, bip39.encode(entropy))
|
||||||
|
assertEquals(entropy.toHexString(), bip39.decode(phrase).toHexString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_decode_invalid_checksum() {
|
||||||
|
assertFailsWith<InvalidChecksumException> {
|
||||||
|
bip39.decode(
|
||||||
|
listOf(
|
||||||
|
"lawn",
|
||||||
|
"dirt",
|
||||||
|
"work",
|
||||||
|
"mountain",
|
||||||
|
"depth",
|
||||||
|
"loyal",
|
||||||
|
"citizen",
|
||||||
|
"theory",
|
||||||
|
"cram",
|
||||||
|
"trip",
|
||||||
|
"boil",
|
||||||
|
"about",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_decode_invalid_word() {
|
||||||
|
assertFailsWith<InvalidWordException> {
|
||||||
|
bip39.decode(listOf("dirt", "bee", "work"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,234 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.platform.crypto
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertNotEquals
|
||||||
|
|
||||||
|
class CryptoTest {
|
||||||
|
private val crypto = JavaCrypto()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_sha256() {
|
||||||
|
val sha256 = crypto.sha256()
|
||||||
|
sha256.update(0x10.toByte())
|
||||||
|
sha256.update(0x20.toByte())
|
||||||
|
sha256.update(0x30.toByte())
|
||||||
|
val digest = sha256.finalize()
|
||||||
|
assertEquals(32, digest.size)
|
||||||
|
assertEquals(0x8e.toByte(), digest[0])
|
||||||
|
assertEquals(0x13.toByte(), digest[1])
|
||||||
|
assertEquals(0x36.toByte(), digest[2])
|
||||||
|
assertEquals(0xb9.toByte(), digest[31])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_hmacsha256() {
|
||||||
|
val hmac = crypto.hmacSha256()
|
||||||
|
hmac.init(byteArrayOfInts(0x01, 0x02, 0x03))
|
||||||
|
hmac.update(0x40.toByte())
|
||||||
|
hmac.update("AB")
|
||||||
|
val checksum = hmac.finalize()
|
||||||
|
assertEquals(32, checksum.size)
|
||||||
|
assertEquals(0x6d.toByte(), checksum[0])
|
||||||
|
assertEquals(0xc9.toByte(), checksum[1])
|
||||||
|
assertEquals(0x05.toByte(), checksum[2])
|
||||||
|
assertEquals(0xa1.toByte(), checksum[31])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_aes_gcm() {
|
||||||
|
val msg = byteArrayOfInts(
|
||||||
|
0x2f,
|
||||||
|
0xdc,
|
||||||
|
0xaa,
|
||||||
|
0x41,
|
||||||
|
0xfa,
|
||||||
|
0xb8,
|
||||||
|
0x5e,
|
||||||
|
0xe8,
|
||||||
|
0xa3,
|
||||||
|
0x12,
|
||||||
|
0x69,
|
||||||
|
0x68,
|
||||||
|
0x14,
|
||||||
|
0x31,
|
||||||
|
0xd8,
|
||||||
|
0x59,
|
||||||
|
0x74,
|
||||||
|
0x29,
|
||||||
|
0x2e,
|
||||||
|
0xae,
|
||||||
|
0xed,
|
||||||
|
0x76,
|
||||||
|
0x0a,
|
||||||
|
0x56,
|
||||||
|
0x46,
|
||||||
|
0x90,
|
||||||
|
0xb6,
|
||||||
|
0xcb,
|
||||||
|
0x9f,
|
||||||
|
0x37,
|
||||||
|
0xbe,
|
||||||
|
0xae,
|
||||||
|
)
|
||||||
|
|
||||||
|
val key = Key(
|
||||||
|
byteArrayOfInts(
|
||||||
|
0xed,
|
||||||
|
0xa8,
|
||||||
|
0xc3,
|
||||||
|
0xc6,
|
||||||
|
0x44,
|
||||||
|
0x1e,
|
||||||
|
0xa1,
|
||||||
|
0xd5,
|
||||||
|
0x71,
|
||||||
|
0x8c,
|
||||||
|
0x71,
|
||||||
|
0x45,
|
||||||
|
0xbe,
|
||||||
|
0x2d,
|
||||||
|
0xf7,
|
||||||
|
0xa4,
|
||||||
|
0x81,
|
||||||
|
0x2e,
|
||||||
|
0x0a,
|
||||||
|
0x0b,
|
||||||
|
0xa8,
|
||||||
|
0xe4,
|
||||||
|
0x20,
|
||||||
|
0x49,
|
||||||
|
0x94,
|
||||||
|
0x8a,
|
||||||
|
0x71,
|
||||||
|
0x1a,
|
||||||
|
0x15,
|
||||||
|
0xf5,
|
||||||
|
0x29,
|
||||||
|
0x78,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val iv = byteArrayOfInts(
|
||||||
|
0xa7,
|
||||||
|
0xef,
|
||||||
|
0xe1,
|
||||||
|
0xba,
|
||||||
|
0xdf,
|
||||||
|
0x4f,
|
||||||
|
0x85,
|
||||||
|
0xca,
|
||||||
|
0xc3,
|
||||||
|
0x81,
|
||||||
|
0xc1,
|
||||||
|
0x93,
|
||||||
|
)
|
||||||
|
|
||||||
|
val expected = byteArrayOfInts(
|
||||||
|
// iv
|
||||||
|
0xa7,
|
||||||
|
0xef,
|
||||||
|
0xe1,
|
||||||
|
0xba,
|
||||||
|
0xdf,
|
||||||
|
0x4f,
|
||||||
|
0x85,
|
||||||
|
0xca,
|
||||||
|
0xc3,
|
||||||
|
0x81,
|
||||||
|
0xc1,
|
||||||
|
0x93,
|
||||||
|
// msg
|
||||||
|
0x24,
|
||||||
|
0xe7,
|
||||||
|
0x26,
|
||||||
|
0x9b,
|
||||||
|
0xb8,
|
||||||
|
0x59,
|
||||||
|
0xf0,
|
||||||
|
0xe0,
|
||||||
|
0x4f,
|
||||||
|
0xda,
|
||||||
|
0xc0,
|
||||||
|
0x85,
|
||||||
|
0xc6,
|
||||||
|
0x23,
|
||||||
|
0x21,
|
||||||
|
0x61,
|
||||||
|
0x80,
|
||||||
|
0x59,
|
||||||
|
0xd6,
|
||||||
|
0x18,
|
||||||
|
0xee,
|
||||||
|
0xa0,
|
||||||
|
0xd8,
|
||||||
|
0x00,
|
||||||
|
0xe3,
|
||||||
|
0xdf,
|
||||||
|
0x6e,
|
||||||
|
0xcf,
|
||||||
|
0x89,
|
||||||
|
0x82,
|
||||||
|
0xfd,
|
||||||
|
0x63,
|
||||||
|
// verification tag
|
||||||
|
0xe9,
|
||||||
|
0xe9,
|
||||||
|
0xac,
|
||||||
|
0x92,
|
||||||
|
0xdc,
|
||||||
|
0xb1,
|
||||||
|
0x7c,
|
||||||
|
0x2d,
|
||||||
|
0x9a,
|
||||||
|
0x73,
|
||||||
|
0xda,
|
||||||
|
0x25,
|
||||||
|
0x6d,
|
||||||
|
0xda,
|
||||||
|
0xc0,
|
||||||
|
0x83,
|
||||||
|
)
|
||||||
|
val cipher = crypto.aesGcm(key)
|
||||||
|
val actual = cipher.encrypt(msg, iv)
|
||||||
|
assertEquals(actual.toHexString(), expected.toHexString())
|
||||||
|
|
||||||
|
val recovered = cipher.decrypt(actual)
|
||||||
|
assertEquals(msg.toHexString(), recovered.toHexString())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_rand() {
|
||||||
|
val r1 = crypto.secureRandomBytes(8)
|
||||||
|
val r2 = crypto.secureRandomBytes(8)
|
||||||
|
assertEquals(8, r1.size)
|
||||||
|
assertNotEquals(r1.toBits(), r2.toBits())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_derive_key() {
|
||||||
|
val k1 = Key(byteArrayOfInts(0x01, 0x02, 0x03))
|
||||||
|
val k2 = crypto.deriveKey(k1, "TEST")
|
||||||
|
assertEquals(0x44.toByte(), k2.bytes[0])
|
||||||
|
assertEquals(0xd3.toByte(), k2.bytes[31])
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue