mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-10 02:24:05 +09:00
working invitation code via portal
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package net.torvald.terrarum.utils
|
||||
|
||||
import org.apache.commons.codec.binary.Base32
|
||||
import kotlin.experimental.xor
|
||||
|
||||
|
||||
@@ -10,91 +11,25 @@ import kotlin.experimental.xor
|
||||
*/
|
||||
object PasswordBase32 {
|
||||
|
||||
private val stringSet = "YBNDRFG8EJKMCP2XOTLVUIS2A345H769="
|
||||
private val si = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567="
|
||||
private val so = "YBNDRFG8EJKMCPQXOTLVUIS2A345H769 "
|
||||
|
||||
private val substituteSet = hashMapOf(
|
||||
Pair('0', 'O'),
|
||||
Pair('1', 'I'),
|
||||
Pair('Z', '2')
|
||||
)
|
||||
private val standardToModified = HashMap<Char, Char>(32)
|
||||
private val modifiedToStandard = HashMap<Char, Char>(32)
|
||||
|
||||
/*
|
||||
0 x
|
||||
1 6
|
||||
2 4
|
||||
3 3
|
||||
4 1
|
||||
*/
|
||||
val padLen = arrayOf(0, 6, 4, 3, 1)
|
||||
private val nullPw = byteArrayOf(0.toByte())
|
||||
|
||||
|
||||
private fun encodeToLetters(byteArray: ByteArray, password: ByteArray): IntArray {
|
||||
val out = ArrayList<Int>()
|
||||
|
||||
fun get(i: Int) = byteArray[i] xor (password[i % password.size])
|
||||
|
||||
|
||||
/*
|
||||
5 Bytes -> 8 Letters
|
||||
|
||||
0000 0000 | 1111 1111 | 2222 2222 | 3333 3333 | 4444 4444
|
||||
AAAA ABBB | BBCC CCCD | DDDD EEEE | EFFF FFGG | GGGH HHHH
|
||||
*/
|
||||
|
||||
// non-pads
|
||||
(0..byteArray.lastIndex - 5 step 5).forEach {
|
||||
/* A */ out.add(get(it).toInt().and(0xF8).ushr(3))
|
||||
/* B */ out.add(get(it).toInt().and(7).shl(2) or get(it+1).toInt().and(0xC0).ushr(6))
|
||||
/* C */ out.add(get(it+1).toInt().and(0x3E).ushr(1))
|
||||
/* D */ out.add(get(it+1).toInt().and(1).shl(4) or get(it+2).toInt().and(0xF0).ushr(4))
|
||||
/* E */ out.add(get(it+2).toInt().and(0xF).shl(1) or get(it+3).toInt().and(0x80).ushr(7))
|
||||
/* F */ out.add(get(it+3).toInt().and(0x7C).ushr(2))
|
||||
/* G */ out.add(get(it+3).toInt().and(3).shl(3) or get(it+4).toInt().and(0xE0).ushr(5))
|
||||
/* H */ out.add(get(it+4).toInt().and(0x1F))
|
||||
}
|
||||
// pads
|
||||
val residue = byteArray.size % 5
|
||||
if (residue != 0){
|
||||
|
||||
val it = (byteArray.size / 5) * 5 // dark magic of integer division, let's hope the compiler won't "optimise" this...
|
||||
|
||||
when (residue) {
|
||||
1 -> {
|
||||
/* A */ out.add(get(it).toInt().and(0xF8).ushr(3))
|
||||
/* B */ out.add(get(it).toInt().and(7).shl(2))
|
||||
}
|
||||
2 -> {
|
||||
/* A */ out.add(get(it).toInt().and(0xF8).ushr(3))
|
||||
/* B */ out.add(get(it).toInt().and(7).shl(2) or get(it+1).toInt().and(0xC0).ushr(6))
|
||||
/* C */ out.add(get(it+1).toInt().and(0x3E).ushr(1))
|
||||
/* D */ out.add(get(it+1).toInt().and(1).shl(4))
|
||||
}
|
||||
3 -> {
|
||||
/* A */ out.add(get(it).toInt().and(0xF8).ushr(3))
|
||||
/* B */ out.add(get(it).toInt().and(7).shl(2) or get(it+1).toInt().and(0xC0).ushr(6))
|
||||
/* C */ out.add(get(it+1).toInt().and(0x3E).ushr(1))
|
||||
/* D */ out.add(get(it+1).toInt().and(1).shl(4) or get(it+2).toInt().and(0xF0).ushr(4))
|
||||
/* E */ out.add(get(it+2).toInt().and(0xF).shl(1))
|
||||
}
|
||||
4 -> {
|
||||
/* A */ out.add(get(it).toInt().and(0xF8).ushr(3))
|
||||
/* B */ out.add(get(it).toInt().and(7).shl(2) or get(it+1).toInt().and(0xC0).ushr(6))
|
||||
/* C */ out.add(get(it+1).toInt().and(0x3E).ushr(1))
|
||||
/* D */ out.add(get(it+1).toInt().and(1).shl(4) or get(it+2).toInt().and(0xF0).ushr(4))
|
||||
/* E */ out.add(get(it+2).toInt().and(0xF).shl(1) or get(it+3).toInt().and(0x80).ushr(7))
|
||||
/* F */ out.add(get(it+3).toInt().and(0x7C).ushr(2))
|
||||
/* G */ out.add(get(it+3).toInt().and(3).shl(3))
|
||||
}
|
||||
}
|
||||
|
||||
// append padding
|
||||
kotlin.repeat(padLen[residue], { out.add(32) })
|
||||
init {
|
||||
(0 until si.length).forEach {
|
||||
standardToModified[si[it]] = so[it]
|
||||
modifiedToStandard[so[it]] = si[it]
|
||||
}
|
||||
|
||||
return out.toIntArray()
|
||||
modifiedToStandard['0'] = modifiedToStandard['O']!!
|
||||
modifiedToStandard['1'] = modifiedToStandard['I']!!
|
||||
modifiedToStandard['Z'] = modifiedToStandard['2']!!
|
||||
}
|
||||
|
||||
private val nullPw = byteArrayOf(0.toByte())
|
||||
|
||||
/**
|
||||
*
|
||||
* @param bytes size of multiple of five (5, 10, 15, 20, ...) is highly recommended to prevent
|
||||
@@ -103,11 +38,8 @@ object PasswordBase32 {
|
||||
* from doing naughty things. Longer, the better.
|
||||
*/
|
||||
fun encode(bytes: ByteArray, password: ByteArray = nullPw): String {
|
||||
val plaintext = encodeToLetters(bytes, password)
|
||||
val sb = StringBuilder()
|
||||
plaintext.forEach { sb.append(stringSet[it]) }
|
||||
|
||||
return sb.toString()
|
||||
val rawstr = Base32().encode(ByteArray(bytes.size) { bytes[it] xor password[it % password.size] }).toString(Charsets.US_ASCII)
|
||||
return rawstr.map { standardToModified[it] }.joinToString("").trim()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,36 +49,10 @@ object PasswordBase32 {
|
||||
* suspect user inputs and sanitise them.
|
||||
*/
|
||||
fun decode(input: String, outByteLength: Int, password: ByteArray = nullPw): ByteArray {
|
||||
val buffer = ByteArray(outByteLength)
|
||||
var appendCount = 0
|
||||
var input = input.toUpperCase()
|
||||
substituteSet.forEach { from, to ->
|
||||
input = input.replace(from, to)
|
||||
val decInput = input.trim().map { modifiedToStandard[it] }.joinToString("")
|
||||
return Base32().decode(decInput).let { ba ->
|
||||
ByteArray(outByteLength) { ba.getOrElse(it) { 0.toByte() } xor password[it % password.size] }
|
||||
}
|
||||
|
||||
fun append(byte: Int) {
|
||||
buffer[appendCount] = byte.toByte() xor (password[appendCount % password.size])
|
||||
appendCount++
|
||||
}
|
||||
fun sbyteOf(i: Int) = stringSet.indexOf(input.getOrElse(i) { '=' }).and(0x1F)
|
||||
|
||||
try {
|
||||
/*
|
||||
8 Letters -> 5 Bytes
|
||||
|
||||
0000 0000 | 1111 1111 | 2222 2222 | 3333 3333 | 4444 4444
|
||||
AAAA ABBB | BBCC CCCD | DDDD EEEE | EFFF FFGG | GGGH HHHH
|
||||
*/
|
||||
(0..input.lastIndex.plus(8) step 8).forEach {
|
||||
/* 0 */ append(sbyteOf(it+0).shl(3) or sbyteOf(it+1).ushr(2))
|
||||
/* 1 */ append(sbyteOf(it+1).shl(6) or sbyteOf(it+2).shl(1) or sbyteOf(it+3).ushr(4))
|
||||
/* 2 */ append(sbyteOf(it+3).shl(4) or sbyteOf(it+4).ushr(1))
|
||||
/* 3 */ append(sbyteOf(it+4).shl(7) or sbyteOf(it+5).shl(2) or sbyteOf(it+6).ushr(3))
|
||||
/* 4 */ append(sbyteOf(it+6).shl(5) or sbyteOf(it+7))
|
||||
}
|
||||
}
|
||||
catch (endOfStream: ArrayIndexOutOfBoundsException) { }
|
||||
|
||||
return buffer
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user