blob: 88a8ebbd125f54a791ccf044c2ebc8b49fe991c8 (
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
/*
* 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
internal class EncodingException : Exception("Invalid encoding")
internal object Base32Crockford {
private fun ByteArray.getIntAt(index: Int): Int {
val x = this[index].toInt()
return if (x >= 0) x else (x + 256)
}
private const val encTable = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
public fun encode(data: ByteArray): String {
val sb = StringBuilder()
val size = data.size
var bitBuf = 0
var numBits = 0
var pos = 0
while (pos < size || numBits > 0) {
if (pos < size && numBits < 5) {
val d = data.getIntAt(pos++)
bitBuf = (bitBuf shl 8) or d
numBits += 8
}
if (numBits < 5) {
// zero-padding
bitBuf = bitBuf shl (5 - numBits)
numBits = 5
}
val v = bitBuf.ushr(numBits - 5) and 31
sb.append(encTable[v])
numBits -= 5
}
return sb.toString()
}
public fun decode(encoded: String): ByteArray {
val size = encoded.length
var bitpos = 0
var bitbuf = 0
var readPosition = 0
var writePosition = 0
val out = ByteArray(calculateDecodedDataLength(size))
while (readPosition < size || bitpos > 0) {
//println("at position $readPosition with bitpos $bitpos")
if (readPosition < size) {
val v = getValue(encoded[readPosition++])
bitbuf = (bitbuf shl 5) or v
bitpos += 5
}
while (bitpos >= 8) {
val d = (bitbuf ushr (bitpos - 8)) and 0xFF
out[writePosition] = d.toByte()
writePosition++
bitpos -= 8
}
if (readPosition == size && bitpos > 0) {
bitbuf = (bitbuf shl (8 - bitpos)) and 0xFF
bitpos = if (bitbuf == 0) 0 else 8
}
}
return out
}
private fun getValue(chr: Char): Int {
var a = chr
when (a) {
'O', 'o' -> a = '0'
'i', 'I', 'l', 'L' -> a = '1'
'u', 'U' -> a = 'V'
}
if (a in '0'..'9')
return a - '0'
if (a in 'a'..'z')
a = a.toUpperCase()
var dec = 0
if (a in 'A'..'Z') {
if ('I' < a) dec++
if ('L' < a) dec++
if ('O' < a) dec++
if ('U' < a) dec++
return a - 'A' + 10 - dec
}
throw EncodingException()
}
/**
* Compute the length of the resulting string when encoding data of the given size
* in bytes.
*
* @param dataSize size of the data to encode in bytes
* @return size of the string that would result from encoding
*/
private fun calculateEncodedStringLength(dataSize: Int): Int {
return (dataSize * 8 + 4) / 5
}
/**
* Compute the length of the resulting data in bytes when decoding a (valid) string of the
* given size.
*
* @param stringSize size of the string to decode
* @return size of the resulting data in bytes
*/
internal fun calculateDecodedDataLength(stringSize: Int): Int {
return stringSize * 5 / 8
}
}
|