summaryrefslogtreecommitdiff
path: root/wallet/src/commonMain/kotlin/net/taler/lib/crypto/Kdf.kt
blob: dee2486ace4e9e544ef5cacdacfdbbac517a3159 (plain)
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)
    }

}