Add dedicated base32 helper utilities in its own namespace.

master
Johan Ouwerkerk 2019-09-09 11:29:22 +02:00
parent 4f4d1c93f8
commit 71180195e6
3 changed files with 148 additions and 0 deletions

View File

@ -2,6 +2,7 @@ set(otpclient_SRCS
main.cpp
accountmodel.cpp
account.cpp
base32.cpp
)
qt5_add_resources(RESOURCES resources.qrc)

116
src/base32.cpp Normal file
View File

@ -0,0 +1,116 @@
/*****************************************************************************
* Copyright: 2019 Johan Ouwerkerk <jm.ouwerkerk@gmail.com> *
* *
* This project 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 of the License, or *
* (at your option) any later version. *
* *
* This project 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 this program. If not, see <http://www.gnu.org/licenses/>. *
* *
****************************************************************************/
#include "base32.h"
#include "oath_p.h"
#include <QtDebug>
#include <stdlib.h>
#include <string.h>
static inline size_t determineCapacity(size_t encodedBytes, size_t accountFor, size_t lastBytes)
{
return 5 * (encodedBytes - accountFor) / 8 + lastBytes;
}
namespace base32
{
std::optional<QByteArray> decode(const QString &encoded)
{
QByteArray result;
QByteArray bytes = encoded.toLocal8Bit();
int size = bytes.size(), capacity = size;
bool ok = false;
// size should be a multiple of 8 if the input is to be valid base32
// (smaller data should be padded correctly)
if (size % 8) {
return std::nullopt;
}
while (size > 0 && bytes[size -1] == '=') {
size --;
}
// based on the amount of padding, determine the exact size of the encoded data
switch (capacity - size) {
case 0:
capacity = determineCapacity(size, 0, 0);
break;
case 1:
capacity = determineCapacity(size, 7, 4);
break;
case 3:
capacity = determineCapacity(size, 5, 3);
break;
case 4:
capacity = determineCapacity(size, 4, 2);
break;
case 6:
capacity = determineCapacity(size, 2, 1);
break;
default:
// invalid amount of padding, reject the input
return std::nullopt;
}
/*
* We want to fill this buffer *exactly* if possible, to avoid accidental copies of (partial) secrets
* when filling in the decoded secret later
*/
result.reserve(capacity);
char * output = nullptr;
size_t reportedCapacity = (size_t) capacity;
int status = oath_base32_decode(bytes.data(), (size_t) size, &output, &reportedCapacity);
/*
* sanity check that:
* - decoding base32 succeeded
* - the library agrees on how big the output buffer should be, i.e. that the preceeding allocation logic was correct
*/
ok = status == OATH_OK && reportedCapacity == ((size_t) capacity);
/*
* Avoid += because then strlen() is used which:
* - Might branch on unitialised memory according to Valgrind
* - Does not work well with embedded \0, which might be used and *is* valid
*/
if (ok) {
result.append(output, reportedCapacity);
}
/*
* At this point we have an extra copy of the (decoded) secret in memory.
* Wipe it and free up the memory.
*
* Note the +1 for trailing \0: not strictly necessary but good to be aware?
*/
if (output) {
memset(output, '\0', reportedCapacity + 1);
free(output);
}
std::optional<QByteArray> r = std::optional<QByteArray>(result);
return ok ? r : std::nullopt;
}
}

31
src/base32.h Normal file
View File

@ -0,0 +1,31 @@
/*****************************************************************************
* Copyright: 2019 Johan Ouwerkerk <jm.ouwerkerk@gmail.com> *
* *
* This project 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 of the License, or *
* (at your option) any later version. *
* *
* This project 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 this program. If not, see <http://www.gnu.org/licenses/>. *
* *
****************************************************************************/
#ifndef BASE32_H
#define BASE32_H
#include <QByteArray>
#include <QString>
#include <optional>
namespace base32
{
std::optional<QByteArray> decode(const QString &input);
}
#endif