1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
/*
* This file is part of GNU Taler
* (C) 2020 Taler Systems S.A.
*
* GNU Taler 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, or (at your option) any later version.
*
* GNU Taler 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
* GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
package net.taler.lib.crypto
import kotlin.experimental.xor
import kotlin.math.ceil
internal object Kdf {
const val HMAC_SHA256_BLOCK_SIZE = 64
const val HMAC_SHA512_BLOCK_SIZE = 128
fun kdf(
outputLength: Int,
ikm: ByteArray,
salt: ByteArray,
info: ByteArray,
sha256: (ByteArray) -> ByteArray,
sha512: (ByteArray) -> ByteArray,
): ByteArray {
//extract
val prk = hmacSha512(salt, ikm, sha512)
// expand
val n = ceil(outputLength.toDouble() / 32).toInt()
val output = ByteArray(n * 32)
for (i in 0 until n) {
val buf: ByteArray
if (i == 0) {
buf = ByteArray(info.size + 1)
info.copyInto(buf)
} else {
buf = ByteArray(info.size + 1 + 32)
for (j in 0 until 32) {
buf[j] = output[(i - 1) * 32 + j]
}
info.copyInto(buf, destinationOffset = 32)
}
buf[buf.size - 1] = (i + 1).toByte()
val chunk = hmacSha256(prk, buf, sha256)
chunk.copyInto(output, destinationOffset = i * 32)
}
return output.copyOfRange(0, outputLength)
}
fun hmacSha256(key: ByteArray, message: ByteArray, sha256: (ByteArray) -> ByteArray): ByteArray {
return hmac(HMAC_SHA256_BLOCK_SIZE, key, message) { sha256(it) }
}
fun hmacSha512(key: ByteArray, message: ByteArray, sha512: (ByteArray) -> ByteArray): ByteArray {
return hmac(HMAC_SHA512_BLOCK_SIZE, key, message) { sha512(it) }
}
private fun hmac(blockSize: Int, key: ByteArray, message: ByteArray, hash: (ByteArray) -> ByteArray): ByteArray {
var newKey = key
if (newKey.size > blockSize) newKey = hash(newKey)
if (newKey.size < blockSize) newKey = ByteArray(blockSize).apply {
newKey.copyInto(this)
}
val okp = ByteArray(blockSize)
val ikp = ByteArray(blockSize)
for (i in 0 until blockSize) {
ikp[i] = newKey[i] xor 0x36
okp[i] = newKey[i] xor 0x5c
}
val b1 = ByteArray(blockSize + message.size)
ikp.copyInto(b1)
message.copyInto(b1, destinationOffset = blockSize)
val h0 = hash(b1)
val b2 = ByteArray(blockSize + h0.size)
okp.copyInto(b2)
h0.copyInto(b2, destinationOffset = blockSize)
return hash(b2)
}
}
|