1
0
Fork 0
mirror of https://github.com/seiichiro0185/sailotp.git synced 2024-05-11 14:00:53 +00:00

Cleanup and Comments for publishing

* Added Readme and License
* Cleaned up the code
* Added comments to the code
This commit is contained in:
seiichiro 2014-01-06 20:08:16 +01:00
parent 0c0f10f7ab
commit f98444d5a4
10 changed files with 386 additions and 301 deletions

26
COPYING Normal file
View file

@ -0,0 +1,26 @@
Copyright (c) 2013, Stefan Brand <seiichiro@seiichiro0185.org>
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
3. The names of the contributors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

36
README.md Normal file
View file

@ -0,0 +1,36 @@
# SailOTP
SailOTP is a Sailfish Implementation of the Google-Authenticator algorithm,
also known as Timebased One Time Pad (TOPT) as described in RFC 6238. A growing
number of sites uses this algorithm for two-factor-authentication, including
Github, Linode and several Google services.
At the moment the App is quite basic. One can add new OTP-entries using the
pulley-menu. The main view of the app will show a List off all entries and
their current One-Time-Tokens. The entries will be regenerated every 30 seconds, the remaining time for the current tokens is shown through a progress bar at the top of the app. An entry can be deleted by long-pressing on it.
## Known Limitations
At the moment the only way to insert new entries into the app is to insert the
title and secret key by hand. It's not possible to use the QR-Codes some sites
provide directly.
## Contact and Issues
If you find any bugs or want to suggest a feature, feel free to use Githubs
Issues feature.
## License
SailOTP is licensed under a 3-Clause BSD-License. See COPYING for details.
## Accnowledgements
SailOTP uses the SHA-1 and HMAC-Implementation from
<a href="https://github.com/Caligatio/jsSHA" target="_blank">https://github.com/Caligatio/jsSHA</a>
The implementation of the TOTP-algorithm was inspired by:
<a href="http://blog.tinisles.com/2011/10/google-authenticator-one-time-password-algorithm-in-javascript/" target="_blank">http://blog.tinisles.com/2011/10/google-authenticator-one-time-password-algorithm-in-javascript/</a>

View file

@ -30,8 +30,10 @@
import QtQuick 2.0 import QtQuick 2.0
import Sailfish.Silica 1.0 import Sailfish.Silica 1.0
// Define the Layout of the Active Cover
CoverBackground { CoverBackground {
// Show the SailOTP Logo
Image { Image {
id: logo id: logo
source: "../sailotp.png" source: "../sailotp.png"
@ -40,6 +42,7 @@ CoverBackground {
anchors.topMargin: 48 anchors.topMargin: 48
} }
// Show the Application Name
Label { Label {
id: label id: label
anchors.centerIn: parent anchors.centerIn: parent

View file

@ -29,12 +29,15 @@
.import "./sha.js" as SHA .import "./sha.js" as SHA
// Helper Functions // *** Helper Functions *** //
// Decimal to HEX
function dec2hex(s) { return (s < 15.5 ? '0' : '') + Math.round(s).toString(16); } function dec2hex(s) { return (s < 15.5 ? '0' : '') + Math.round(s).toString(16); }
// HEX to Decimal
function hex2dec(s) { return parseInt(s, 16); } function hex2dec(s) { return parseInt(s, 16); }
// Convert Base32-secret to HEX Value
function base32tohex(base32) { function base32tohex(base32) {
var base32chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; var base32chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
var bits = ""; var bits = "";
@ -50,9 +53,9 @@ function base32tohex(base32) {
hex = hex + parseInt(chunk, 2).toString(16) ; hex = hex + parseInt(chunk, 2).toString(16) ;
} }
return hex; return hex;
} }
// Pad Strings to given length
function leftpad(str, len, pad) { function leftpad(str, len, pad) {
if (len + 1 >= str.length) { if (len + 1 >= str.length) {
str = Array(len + 1 - str.length).join(pad) + str; str = Array(len + 1 - str.length).join(pad) + str;
@ -60,18 +63,28 @@ function leftpad(str, len, pad) {
return str; return str;
} }
// *** Main Function *** //
// Calculate an OTP-Value from the given secret // Calculate an OTP-Value from the given secret
// Parameter is the secret key in Base32-notation
function calcOTP(secret) { function calcOTP(secret) {
// Convert the key to HEX
var key = base32tohex(secret); var key = base32tohex(secret);
// Get current Time in UNIX Timestamp format (Seconds since 01.01.1970 00:00 UTC)
var epoch = Math.round(new Date().getTime() / 1000.0); var epoch = Math.round(new Date().getTime() / 1000.0);
// Get last full 30 / 60 Seconds and convert to HEX
var time = leftpad(dec2hex(Math.floor(epoch / 30)), 16, '0'); var time = leftpad(dec2hex(Math.floor(epoch / 30)), 16, '0');
// Calculate the SHA-1 HMAC Value from time and key
var hmacObj = new SHA.jsSHA(time, 'HEX'); var hmacObj = new SHA.jsSHA(time, 'HEX');
var hmac = hmacObj.getHMAC(key, 'HEX', 'SHA-1', "HEX"); var hmac = hmacObj.getHMAC(key, 'HEX', 'SHA-1', "HEX");
// Finally convert the HMAC-Value to the corresponding 6-digit token
var offset = hex2dec(hmac.substring(hmac.length - 1)); var offset = hex2dec(hmac.substring(hmac.length - 1));
var otp = (hex2dec(hmac.substr(offset * 2, 8)) & hex2dec('7fffffff')) + ''; var otp = (hex2dec(hmac.substr(offset * 2, 8)) & hex2dec('7fffffff')) + '';
otp = (otp).substr(otp.length - 6, 6); otp = (otp).substr(otp.length - 6, 6);
// return the calculated token
return otp; return otp;
} }

View file

@ -30,16 +30,15 @@
import QtQuick 2.0 import QtQuick 2.0
import Sailfish.Silica 1.0 import Sailfish.Silica 1.0
// Define the Layout of the About Page
Page { Page {
id: aboutPage id: aboutPage
Image { Image {
id: logo id: logo
source: "../sailotp.png" source: "../sailotp.png"
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
y: 200 y: 200
} }
Label { Label {
id: name id: name
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
@ -81,5 +80,4 @@ Page {
text: "SailOTP uses the SHA-1 Implementation<br />from http://caligatio.github.io/jsSHA/" text: "SailOTP uses the SHA-1 Implementation<br />from http://caligatio.github.io/jsSHA/"
color: "white" color: "white"
} }
} }

View file

@ -30,11 +30,13 @@
import QtQuick 2.0 import QtQuick 2.0
import Sailfish.Silica 1.0 import Sailfish.Silica 1.0
import "../lib/storage.js" as DB import "../lib/storage.js" as DB // Import the storage library for Config-Access
// Define Layout of the Add OTP Dialog
Dialog { Dialog {
id: addOTP id: addOTP
// We get the Object of the parent page on call to refresh it after adding a new Entry
property QtObject parentPage: null property QtObject parentPage: null
SilicaFlickable { SilicaFlickable {
@ -48,7 +50,6 @@ Dialog {
DialogHeader { DialogHeader {
acceptText: "Add" acceptText: "Add"
} }
TextField { TextField {
id: otpLabel id: otpLabel
width: parent.width width: parent.width
@ -57,7 +58,6 @@ Dialog {
focus: true focus: true
horizontalAlignment: TextInput.AlignLeft horizontalAlignment: TextInput.AlignLeft
} }
TextField { TextField {
id: otpSecret id: otpSecret
width: parent.width width: parent.width
@ -69,13 +69,16 @@ Dialog {
} }
} }
// Save if page is Left with Add
onDone: { onDone: {
// Some basic Input Check, we need both Values to work
if (otpLabel.text != "" && otpSecret.text != "") { if (otpLabel.text != "" && otpSecret.text != "") {
// Save the entry to the Config DB
DB.addOTP(otpLabel.text, otpSecret.text); DB.addOTP(otpLabel.text, otpSecret.text);
// Refresh the main Page
parentPage.refreshOTPList(); parentPage.refreshOTPList();
} }
} }
} }

View file

@ -40,6 +40,7 @@ Page {
id: otpListModel id: otpListModel
} }
// This holds the time of the last update of the page as Unix Timestamp (in Milliseconds)
property double lastUpdated: null property double lastUpdated: null
// Add an entry to the list // Add an entry to the list
@ -56,10 +57,13 @@ Page {
// Calculate new OTPs for every entry // Calculate new OTPs for every entry
function refreshOTPValues() { function refreshOTPValues() {
// get seconds from current Date
var curDate = new Date(); var curDate = new Date();
var seconds = curDate.getSeconds(); var seconds = curDate.getSeconds();
// Iterate over all List entries
for (var i=0; i<otpListModel.count; i++) { for (var i=0; i<otpListModel.count; i++) {
// Only update on full 30 / 60 Seconds or if last run of the Functions is more than 2s in the past (e.g. app was in background)
if (otpListModel.get(i).otp == "" || seconds == 30 || seconds == 0 || (curDate.getTime() - lastUpdated > 2000)) { if (otpListModel.get(i).otp == "" || seconds == 30 || seconds == 0 || (curDate.getTime() - lastUpdated > 2000)) {
var curOTP = OTP.calcOTP(otpListModel.get(i).secret) var curOTP = OTP.calcOTP(otpListModel.get(i).secret)
otpListModel.setProperty(i, "otp", curOTP); otpListModel.setProperty(i, "otp", curOTP);
@ -67,15 +71,16 @@ Page {
} }
} }
// Update the Progressbar
updateProgress.value = 29 - (seconds % 30) updateProgress.value = 29 - (seconds % 30)
// Set lastUpdate property
lastUpdated = curDate.getTime(); lastUpdated = curDate.getTime();
} }
Timer { Timer {
interval: 1000 interval: 1000
running: Qt.application.active running: Qt.application.active // Timer only runs when App is active
repeat: true repeat: true
onTriggered: refreshOTPValues(); onTriggered: refreshOTPValues();
} }
@ -125,6 +130,7 @@ Page {
contentHeight: Theme.itemSizeMedium contentHeight: Theme.itemSizeMedium
function remove() { function remove() {
// Show 5s countdown, then delete from DB and List
remorseAction("Deleting", function() { DB.removeOTP(title, secret); otpListModel.remove(index) }) remorseAction("Deleting", function() { DB.removeOTP(title, secret); otpListModel.remove(index) })
} }
@ -162,7 +168,9 @@ Page {
VerticalScrollDecorator{} VerticalScrollDecorator{}
Component.onCompleted: { Component.onCompleted: {
// Initialize DB (create tables etc..)
DB.initialize(); DB.initialize();
// Load list of OTP-Entries
refreshOTPList(); refreshOTPList();
} }
} }

View file

@ -1,31 +1,30 @@
/* /*
Copyright (C) 2013 Jolla Ltd. * Copyright (c) 2013, Stefan Brand <seiichiro@seiichiro0185.org>
Contact: Thomas Perl <thomas.perl@jollamobile.com> * All rights reserved.
All rights reserved. *
* Redistribution and use in source and binary forms, with or without modification,
You may use this file under the terms of BSD license as follows: * are permitted provided that the following conditions are met:
*
Redistribution and use in source and binary forms, with or without * 1. Redistributions of source code must retain the above copyright notice, this
modification, are permitted provided that the following conditions are met: * list of conditions and the following disclaimer.
* Redistributions of source code must retain the above copyright *
notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, this
* Redistributions in binary form must reproduce the above copyright * list of conditions and the following disclaimer in the documentation and/or other
notice, this list of conditions and the following disclaimer in the * materials provided with the distribution.
documentation and/or other materials provided with the distribution. *
* Neither the name of the Jolla Ltd nor the * 3. The names of the contributors may not be used to endorse or promote products
names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission.
derived from this software without specific prior written permission. *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#ifdef QT_QML_DEBUG #ifdef QT_QML_DEBUG
@ -34,7 +33,6 @@
#include <sailfishapp.h> #include <sailfishapp.h>
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
return SailfishApp::main(argc, argv); return SailfishApp::main(argc, argv);