176 lines
4.8 KiB
Vala
176 lines
4.8 KiB
Vala
namespace Base32 {
|
|
public errordomain Base32Error {
|
|
INCORRECT_PADDING,
|
|
INCORRECT_MAP01;
|
|
}
|
|
|
|
private const string base32_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
private Array<string> _base32_tab = null;
|
|
private GLib.HashTable<uint8, uint8> _b32rev = null;
|
|
|
|
public GLib.Bytes b32encode(GLib.Bytes input) {
|
|
if (_base32_tab == null) {
|
|
_base32_tab = new Array<string>();
|
|
var b32tab = new GLib.Bytes(base32_alphabet.data);
|
|
|
|
|
|
foreach (var a in b32tab.get_data()) {
|
|
foreach (var b in b32tab.get_data()) {
|
|
_base32_tab.append_val("%c%c".printf(a, b));
|
|
}
|
|
}
|
|
}
|
|
|
|
var leftover = input.length % 5;
|
|
var s = new GLib.ByteArray();
|
|
s.append(input.get_data());
|
|
|
|
if (leftover > 0) {
|
|
var padding = new uint8[leftover + 1];
|
|
|
|
for (var i = 0; i <= leftover; i++) {
|
|
padding[i] = 0;
|
|
}
|
|
|
|
s.append(padding);
|
|
}
|
|
|
|
string encoded = "";
|
|
var length = 0;
|
|
|
|
for (var i = 0; i < s.len; i += 5) {
|
|
var next5 = s.data[i:i + 5];
|
|
uint64 c = ((uint64)next5[0] << 32) +
|
|
((uint64)next5[1] << 24) +
|
|
((uint64)next5[2] << 16) +
|
|
((uint64)next5[3] << 8) +
|
|
(uint64)next5[4];
|
|
|
|
encoded += _base32_tab.index((uint)(c >> 30));
|
|
encoded += _base32_tab.index((uint)((c >> 20) & 0x3ff));
|
|
encoded += _base32_tab.index((uint)((c >> 10) & 0x3ff));
|
|
encoded += _base32_tab.index((uint)(c & 0x3ff));
|
|
length += 8;
|
|
}
|
|
|
|
int padder = 0;
|
|
if (leftover == 1) {
|
|
padder = -6;
|
|
} else if (leftover == 2) {
|
|
padder = -4;
|
|
} else if (leftover == 3) {
|
|
padder = -3;
|
|
} else if (leftover == 4) {
|
|
padder = -1;
|
|
}
|
|
for (var i = padder; i < 0; i++) {
|
|
encoded.data[encoded.length + i] = '=';
|
|
}
|
|
|
|
return new GLib.Bytes(encoded.data);
|
|
}
|
|
|
|
public GLib.Bytes b32decode(GLib.Bytes input, bool casefold, char map01) throws Base32Error {
|
|
if (_b32rev == null) {
|
|
_b32rev = new GLib.HashTable<uint8, uint8>(null, null);
|
|
|
|
for (var i = 0; i < base32_alphabet.length; i++) {
|
|
_b32rev[base32_alphabet[i]] = i;
|
|
}
|
|
}
|
|
|
|
if (input.length % 8 != 0) {
|
|
throw new Base32Error.INCORRECT_PADDING("Incorrect padding");
|
|
}
|
|
|
|
var s = new GLib.ByteArray();
|
|
|
|
if (map01 != 0) {
|
|
if ((map01 != 'L') && (map01 != 'I')) {
|
|
throw new Base32Error.INCORRECT_MAP01("Incorrect map01 value");
|
|
}
|
|
|
|
var translated = new uint8[input.length];
|
|
|
|
var idata = input.get_data();
|
|
|
|
for (var i = 0; i < input.length; i++) {
|
|
if (idata[i] == 'O') {
|
|
translated[i] = '0';
|
|
} else if (idata[i] == map01) {
|
|
translated[i] = '1';
|
|
} else {
|
|
translated[i] = idata[i];
|
|
}
|
|
}
|
|
|
|
s.append(translated);
|
|
} else {
|
|
s.append(input.get_data());
|
|
}
|
|
|
|
if (casefold) {
|
|
for (var i = 0; i < s.len; i++) {
|
|
s.data[i] = ((char)s.data[i]).toupper();
|
|
}
|
|
}
|
|
|
|
var padchars = 0;
|
|
while (s.data[s.len - padchars - 1] == '=') {
|
|
padchars++;
|
|
}
|
|
|
|
s.remove_range(s.len - padchars, padchars);
|
|
|
|
|
|
var decoded = new GLib.ByteArray();
|
|
|
|
uint64 acc = 0;
|
|
|
|
for (var i = 0; i < input.length; i += 8) {
|
|
var quanta = s.data[i: i + 8];
|
|
acc = 0;
|
|
|
|
for (var j = 0; j < 8; j++) {
|
|
var c = quanta[j];
|
|
acc = (acc << 5) + _b32rev[c];
|
|
}
|
|
|
|
uint8 res[5];
|
|
|
|
for (var j = 0; j < 5; j++) {
|
|
res[j] = (uint8)((acc >> (32 - j * 8)) & 0xff);
|
|
}
|
|
|
|
decoded.append(res);
|
|
}
|
|
|
|
if (padchars > 0) {
|
|
uint8 last[5];
|
|
|
|
for (var j = 0; j < 5; j++) {
|
|
last[j] = (uint8)((acc >> (32 - j * 8)) & 0xff);
|
|
}
|
|
|
|
var pad_offset = 0;
|
|
|
|
if (padchars == 1) {
|
|
pad_offset = -1;
|
|
} else if (padchars == 3) {
|
|
pad_offset = -2;
|
|
} else if (padchars == 4) {
|
|
pad_offset = -3;
|
|
} else if (padchars == 6) {
|
|
pad_offset = -4;
|
|
} else {
|
|
throw new Base32Error.INCORRECT_PADDING("Incorrect padding");
|
|
}
|
|
|
|
decoded.remove_range(decoded.len - 5, 5);
|
|
decoded.append(last[0:5 + pad_offset]);
|
|
}
|
|
|
|
return new GLib.Bytes(decoded.data);
|
|
}
|
|
}
|