gauthenticator/src/base32.vala

176 lines
4.8 KiB
Vala
Raw Permalink Normal View History

2017-03-29 10:33:01 +00:00
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);
}
}