diff --git a/README.md b/README.md
index 32b8457..e4fc753 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,8 @@ if you need the information outside of SailOTP.
Importing the file is also possible from the pulley menu. If a file contains tokens that are already in the database (title and secret of the token match an existing one), these tokens will not be added again.
+It is also possible to generate tokens for SteamGuard (Steams TOTP-Variant for 2-Factor-Auth). To use this feature, one will have to activate it using the original Steam Android app. After activating it one can get the secret code from '/opt/alien/data/data/com.valvesoftware.android.steam.community/files/Steamguard-$STEAMID'. This file contains json data, including the OTP-URL in the form 'otpauth://totp/Steam:$STEAM_USERNAME?secret=$SECRET&issuer=Steam'. The code from this URL can be added manually using the 'Steam Guard'-OTP-Type in SailOTP.
+
## Contact and Issues
If you find any bugs or want to suggest a feature, feel free to use Githubs
diff --git a/i18n/de.ts b/i18n/de.ts
index 7a30a33..c74d5be 100644
--- a/i18n/de.ts
+++ b/i18n/de.ts
@@ -12,26 +12,36 @@
Copyright: Stefan Brand<br/>Lizenz: BSD (3-Klausel)
-
+
Ein einfacher Sailfish OTP-Generator
(RFC 6238/4226-kompatibel)
-
+
Copyright: Stefan Brand
Lizenz: BSD (3-Klausel)
-
+
SailOTP verwendet folgende externe Bibliotheken:
-
+
+
+ Mitwirkende:
+
+
+
+
+ Unterstützung
+
+
+
Übersetzer:
@@ -39,67 +49,72 @@ Lizenz: BSD (3-Klausel)
AddOTP
-
+
QR-Code anzeigen
-
+
Ein QR-Code kann nur mit vollständigen Einstellungen erzeugt werden!
-
+
Speichern
-
+
Hinzufügen
-
+
Typ
-
+
Zeitbasiert (TOTP)
-
+
Zählerbasiert (HOTP)
-
+
+
+ Steam Guard
+
+
+
Titel
-
+
Titel für das Token
-
+
Schlüssel (mindestens 16 Zeichen)
-
+
Geheimer Schlüssel
-
+
Nächster Zählerwert
-
+
Nächster Wert für den Zähler
@@ -107,114 +122,114 @@ Lizenz: BSD (3-Klausel)
ExportPage
-
+
Datei existiert, aktiviere "Existierende überschreiben" um sie zu ersetzen.
-
+
Gewählte Datei existiert nicht!
-
-
+
+
Export
-
-
+
+
Import
-
+
Dateiname
-
+
Aus Datei importieren
-
+
In Datei exportieren
-
+
Existierende überschreiben
-
+
Passwort
-
+
Passwort für die Datei
-
+
Passwörter nicht identisch!
-
+
Passwörter identisch!
-
+
Passwort wiederholen
-
+
Hier können Tokens aus einer Datei importiert werden. Gib die Datei und das beim Export gewählte Passwort ein. Nach links ziehen um zu starten.
-
+
Hier können Tokens in eine Datei exportiert werden. Die Datei wird mit AES-256-CBC verschlüsselt und Base64-kodiert. Wähle ein starkes Passwort, die Datei enthält die geheimen Schlüssel zur Erzeugung der Tokens für deine Accounts. Nach links ziehen um zu starten.
-
+
Fehler beim Schreiben der Datei
-
+
Datenbank exportiert nach
-
+
Fehler beim Verschlüsseln. Fehler:
-
+
Datenbank konnte nicht gelesen werden
-
+
Tokens importiert aus
-
+
Fehler beim entschlüsseln, falsches Passwort?
-
+
Datei konnte nicht gelesen werden
@@ -222,7 +237,7 @@ Lizenz: BSD (3-Klausel)
MainView
-
+
Über
@@ -235,57 +250,57 @@ Lizenz: BSD (3-Klausel)
Datenbank importieren
-
+
Export / Import
-
+
Token hinzufügen
-
+
Hier ist nichts
-
+
Nach unten ziehen zum hinzufügen
-
+
Lösche
-
+
Token für
-
+
kopiert
-
+
Nach oben
-
+
Nach unten
-
+
Bearbeiten
-
+
Löschen
@@ -293,7 +308,7 @@ Lizenz: BSD (3-Klausel)
QRPage
-
+
Ein QR-Code kann nur mit vollständigen Einstellungen erzeugt werden!
@@ -301,32 +316,32 @@ Lizenz: BSD (3-Klausel)
ScanOTP
-
+
Kein Zugriff auf temporäres Verzeichnis.
-
+
Manuell hinzufügen
-
+
Code scannen
-
+
scanne...
-
+
Kein gültiges Token gefunden.
-
+
Vorschau antippen um den Scan zu starten / zu stoppen. Nach unten ziehen um manuell hinzu zu fügen.
diff --git a/i18n/en.ts b/i18n/en.ts
index 75939ea..eb435ab 100644
--- a/i18n/en.ts
+++ b/i18n/en.ts
@@ -4,24 +4,34 @@
About
-
+
-
+
-
+
-
+
+
+ Contributor:
+
+
+
+
+ support
+
+
+
Translators:
@@ -29,67 +39,72 @@ License: BSD (3-clause)
AddOTP
-
+
Show QR-Code
-
+
Can't create QR-Code from incomplete settings!
-
+
-
+
-
+
-
+
-
+
-
+
+
+ Steam Guard
+
+
+
-
+
-
+
-
+
-
+
-
+
@@ -97,114 +112,114 @@ License: BSD (3-clause)
ExportPage
-
+
-
+
-
-
+
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -212,62 +227,62 @@ License: BSD (3-clause)
MainView
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
Move up
-
+
Move down
-
+
-
+
@@ -275,7 +290,7 @@ License: BSD (3-clause)
QRPage
-
+
Can't create QR-Code from incomplete settings!
@@ -283,32 +298,32 @@ License: BSD (3-clause)
ScanOTP
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/i18n/sv.ts b/i18n/sv.ts
index 01cfa84..90b64e3 100644
--- a/i18n/sv.ts
+++ b/i18n/sv.ts
@@ -4,26 +4,36 @@
About
-
+
En enkel Sailfish OTP-generator
(RFC 6238/4226-kompatibel)
-
+
Copyright: Stefan Brand
Licens: BSD (3-clause)
-
+
SailOTP använder följande tredjeparts bibliotek:
-
+
+
+ Medarbetare:
+
+
+
+
+ försörjning
+
+
+
Översättare:
@@ -31,67 +41,72 @@ Licens: BSD (3-clause)
AddOTP
-
+
Visa QR-kod
-
+
Kan inte skapa QR-kod från ofullständiga inställningar!
-
+
Spara
-
+
Lägg till
-
+
Typ
-
+
Tidsbaserad (TOTP)
-
+
Räknarbaserad (HOTP)
-
+
+
+ Steam Guard
+
+
+
Namn
-
+
Namn på OTP:n
-
+
Hemlighet (Minst 16 tecken)
-
+
Hemlig OTP-nyckel
-
+
Nästa räknarvärde
-
+
Nästa värde på räknaren
@@ -99,114 +114,114 @@ Licens: BSD (3-clause)
ExportPage
-
+
Filen finns redan. Välj "Skriv över befintlig" för att skriva över den.
-
+
Angiven fil finns inte!
-
-
+
+
Exportera
-
-
+
+
Importera
-
+
Filnamn
-
+
Fil att importera
-
+
Fil att exportera
-
+
Skriv över befintlig
-
+
Lösenord
-
+
Lösenord för filen
-
+
Lösenorden stämmer inte!
-
+
Lösenorden stämmer!
-
+
Upprepat lösenord för filen
-
+
Här kan du importera Token från en fil. Ange filens sökväg och lösenordet du angav vid exporten. Svep åt vänster för att starta importen.
-
+
Här kan du exportera Token till en fil. Den exporterade filen kommer att krypteras med AES-256-CBC och Base64. Välj ett starkt lösenord, filen kommer att innehålla hemligheterna som användes för att generera Token för ditt konto. Svep åt vänster för att starta exporten.
-
+
Fel vid skrivning till fil
-
+
Token-databas exporterad till
-
+
Kunde inte kryptera token. Fel:
-
+
Kunde inte läsa in token från databasen
-
+
Token importerad från
-
+
Kunde inte dekryptera filen. Angav du rätt lösenord?
-
+
Kunde inte läsa från fil
@@ -214,62 +229,62 @@ Licens: BSD (3-clause)
MainView
-
+
Om
-
+
Export / Import
-
+
Lägg till Token
-
+
Inget här
-
+
Dra neråt för att lägga till en OTP
-
+
Tar bort
-
+
Token för
-
+
kopierad till urklipp
-
+
Flytta upp
-
+
Flytta ner
-
+
Redigera
-
+
Ta bort
@@ -277,7 +292,7 @@ Licens: BSD (3-clause)
QRPage
-
+
Kan inte skapa QR-kod från ofullständiga inställningar!
@@ -285,32 +300,32 @@ Licens: BSD (3-clause)
ScanOTP
-
+
Kan inte komma åt temp-mapp
-
+
Lägg till manuellt
-
+
Skanna kod
-
+
Skannar...
-
+
Ingen giltig Token-data hittades.
-
+
Tryck på bilden för att starta / stoppa skanning. Dra neråt för att lägga till Token manuellt.
diff --git a/qml/cover/CoverPage.qml b/qml/cover/CoverPage.qml
index 6a31e98..9937bc8 100644
--- a/qml/cover/CoverPage.qml
+++ b/qml/cover/CoverPage.qml
@@ -43,7 +43,7 @@ CoverBackground {
var curDate = new Date();
if (lOTP.text == "------" || curDate.getSeconds() == 30 || curDate.getSeconds() == 0 || (curDate.getTime() - lastUpdated > 2000)) {
- appWin.coverOTP = OTP.calcOTP(appWin.coverSecret, "TOTP", 0);
+ appWin.coverOTP = OTP.calcOTP(appWin.coverSecret, appWin.coverType, 0);
}
// Change color of the OTP to red if less than 5 seconds left
@@ -59,7 +59,7 @@ CoverBackground {
Timer {
interval: 1000
// Timer runs only when cover is visible and favourite is set
- running: !Qt.application.active && appWin.coverSecret != "" && appWin.coverType == "TOTP"
+ running: !Qt.application.active && appWin.coverSecret != "" && (appWin.coverType == "TOTP" || appWin.coverType == "TOTP_STEAM")
repeat: true
onTriggered: updateOTP();
}
@@ -107,7 +107,7 @@ CoverBackground {
if (index < 0) index = appWin.listModel.count - 1
appWin.setCover(index);
DB.setFav(appWin.coverTitle, appWin.coverSecret)
- if (appWin.coverType == "TOTP") updateOTP();
+ if (appWin.coverType == "TOTP" || appWin.coverType == "TOTP_STEAM") updateOTP();
}
}
}
@@ -118,7 +118,7 @@ CoverBackground {
if (index >= appWin.listModel.count) index = 0
appWin.setCover(index);
DB.setFav(appWin.coverTitle, appWin.coverSecret)
- if (appWin.coverType == "TOTP") updateOTP();
+ if (appWin.coverType == "TOTP" || appWin.coverType == "TOTP_STEAM") updateOTP();
}
}
}
diff --git a/qml/lib/crypto.js b/qml/lib/crypto.js
index 9cba4f0..bff4a58 100644
--- a/qml/lib/crypto.js
+++ b/qml/lib/crypto.js
@@ -63,6 +63,11 @@ function leftpad(str, len, pad) {
return str;
}
+// characters steam uses to generate the final code
+var steamChars = ['2', '3', '4', '5', '6', '7', '8', '9', 'B', 'C',
+ 'D', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q',
+ 'R', 'T', 'V', 'W', 'X', 'Y']
+
// *** Main Function *** //
// Calculate an OTP-Value from the given secret
@@ -75,7 +80,7 @@ function calcOTP(secret, type, counter) {
var key = base32tohex(secret);
var factor = "";
- if (type == "TOTP") {
+ if (type.substr(0, 4) == "TOTP") {
// Get current Time in UNIX Timestamp format (Seconds since 01.01.1970 00:00 UTC)
var epoch = Math.round(new Date().getTime() / 1000.0);
// Get last full 30 / 60 Seconds and convert to HEX
@@ -92,8 +97,19 @@ function calcOTP(secret, type, counter) {
// Finally convert the HMAC-Value to the corresponding 6-digit token
var offset = hex2dec(hmac.substring(hmac.length - 1));
- var otp = (hex2dec(hmac.substr(offset * 2, 8)) & hex2dec('7fffffff')) + '';
- otp = (otp).substr(otp.length - 6, 6);
+ var code = hex2dec(hmac.substr(offset * 2, 8)) & hex2dec('7fffffff');
+ var otp = '';
+
+ // Steam has it's own way of creating the code from the result
+ if (type == "TOTP_STEAM") {
+ for (var i = 0; i < 5; i++) {
+ otp += steamChars[code % steamChars.length];
+ code = Math.floor(code/steamChars.length);
+ }
+ } else {
+ otp = code + '';
+ otp = (otp).substr(otp.length - 6, 6);
+ }
} catch (e) {
otp = "Invalid Secret!"
}
diff --git a/qml/lib/storage.js b/qml/lib/storage.js
index ec139ff..94fc281 100644
--- a/qml/lib/storage.js
+++ b/qml/lib/storage.js
@@ -200,13 +200,10 @@ function changeOTP(title, secret, type, counter, oldtitle, oldsecret) {
db.transaction(
function(tx) {
- if (checkOTP(title, secret)) {
- console.log("Token " + title + " is already in DB");
- } else {
tx.executeSql("UPDATE OTPStorage SET title=?, secret=?, type=?, counter=? WHERE title=? and secret=?;", [title, secret, type, counter, oldtitle, oldsecret]);
console.log("Token " + title + " modified.");
}
- });
+ );
}
function changeOTPSort(title, secret, sort) {
diff --git a/qml/pages/About.qml b/qml/pages/About.qml
index 6f3031c..bea3e23 100644
--- a/qml/pages/About.qml
+++ b/qml/pages/About.qml
@@ -33,6 +33,9 @@ import Sailfish.Silica 1.0
// Define the Layout of the About Page
Page {
id: aboutPage
+
+ allowedOrientations: Orientation.All
+
SilicaFlickable {
id: flickable
anchors.fill: parent
@@ -93,6 +96,17 @@ Page {
color: "white"
}
+ TextArea {
+ id: contributors
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: parent.width
+ font.pixelSize: Theme.fontSizeSmall
+ horizontalAlignment: TextEdit.Center
+ readOnly: true
+ text: qsTr("Contributors:")+"\n\nRobin Appelman: SteamGuard "+qsTr("Support")
+ color: "white"
+ }
+
TextArea {
id: translators
anchors.horizontalCenter: parent.horizontalCenter
diff --git a/qml/pages/AddOTP.qml b/qml/pages/AddOTP.qml
index 782f7a6..fe79017 100644
--- a/qml/pages/AddOTP.qml
+++ b/qml/pages/AddOTP.qml
@@ -36,6 +36,8 @@ import "../lib/storage.js" as DB // Import the storage library for Config-Access
Dialog {
id: addOTP
+ allowedOrientations: Orientation.All
+
// We get the Object of the parent page on call to refresh it after adding a new Entry
property QtObject parentPage: null
@@ -65,7 +67,7 @@ Dialog {
MenuItem {
text: qsTr("Show QR-Code")
onClicked: {
- if ((paramType == "TOTP" && (otpLabel.text == "" || otpSecret.text == "")) || (paramType == "HOTP" && (otpLabel.text == "" || otpSecret.text == "" || otpCounter.text <= 0))) {
+ if (((paramType == "TOTP" || paramType == "TOTP_STEAM") && (otpLabel.text == "" || otpSecret.text == "")) || (paramType == "HOTP" && (otpLabel.text == "" || otpSecret.text == "" || otpCounter.text <= 0))) {
notify.show(qsTr("Can't create QR-Code from incomplete settings!"), 4000);
} else {
pageStack.push(Qt.resolvedUrl("QRPage.qml"), {paramLabel: otpLabel.text, paramKey: otpSecret.text, paramType: paramType, paramCounter: otpCounter.text});
@@ -88,6 +90,7 @@ Dialog {
menu: ContextMenu {
MenuItem { text: qsTr("Time-based (TOTP)"); onClicked: { paramType = "TOTP" } }
MenuItem { text: qsTr("Counter-based (HOTP)"); onClicked: { paramType = "HOTP" } }
+ MenuItem { text: qsTr("Steam Guard"); onClicked: { paramType = "TOTP_STEAM" } }
}
}
TextField {
@@ -113,8 +116,8 @@ Dialog {
horizontalAlignment: TextInput.AlignLeft
EnterKey.enabled: text.length > 15
- EnterKey.iconSource: paramType == "TOTP" ? "image://theme/icon-m-enter-accept" : "image://theme/icon-m-enter-next"
- EnterKey.onClicked: paramType == "TOTP" ? addOTP.accept() : otpCounter.focus = true
+ EnterKey.iconSource: paramType == "HOTP" ? "image://theme/icon-m-enter-next" : "image://theme/icon-m-enter-accept"
+ EnterKey.onClicked: paramType == "HOTP" ? otpCounter.focus = true : addOTP.accept()
}
TextField {
id: otpCounter
@@ -135,7 +138,7 @@ Dialog {
}
// Check if we can Save
- canAccept: otpLabel.text.length > 0 && otpSecret.text.length >= 16 && (paramType == "TOTP" || otpCounter.text.length > 0) ? true : false
+ canAccept: otpLabel.text.length > 0 && otpSecret.text.length >= 16 && (paramType == "TOTP" || paramType == "TOTP_STEAM" || otpCounter.text.length > 0) ? true : false
// Save if page is Left with Add
onDone: {
diff --git a/qml/pages/ExportPage.qml b/qml/pages/ExportPage.qml
index ea60aaa..290953f 100644
--- a/qml/pages/ExportPage.qml
+++ b/qml/pages/ExportPage.qml
@@ -38,6 +38,8 @@ import "../lib/gibberish-aes.js" as Gibberish //Import AES encryption library
Dialog {
id: exportPage
+ allowedOrientations: Orientation.All
+
// We get the Object of the parent page on call to refresh it after adding a new Entry
property QtObject parentPage: null
property string mode: "import"
diff --git a/qml/pages/MainView.qml b/qml/pages/MainView.qml
index 2a7e540..eb6b865 100644
--- a/qml/pages/MainView.qml
+++ b/qml/pages/MainView.qml
@@ -36,6 +36,8 @@ import "../lib/crypto.js" as OTP
Page {
id: mainPage
+ allowedOrientations: Orientation.All
+
// This holds the time of the last update of the page as Unix Timestamp (in Milliseconds)
property double lastUpdated: 0
@@ -70,10 +72,10 @@ Page {
// Iterate over all List entries
for (var i=0; i 2000)) {
- var curOTP = OTP.calcOTP(appWin.listModel.get(i).secret, "TOTP")
+ var curOTP = OTP.calcOTP(appWin.listModel.get(i).secret, appWin.listModel.get(i).type)
appWin.listModel.setProperty(i, "otp", curOTP);
}
} else if (appWin.coverType == "HOTP" && (curDate.getTime() - lastUpdated > 2000) && appWin.listModel.get(i).fav == 1) {
@@ -119,16 +121,20 @@ Page {
width: parent.width
maximumValue: 29
anchors.top: parent.top
- anchors.topMargin: 48
+ anchors.topMargin: 36 * Theme.pixelRatio
// Only show when there are enries
visible: appWin.listModel.count
}
+
+
SilicaListView {
id: otpList
+
header: PageHeader {
title: "SailOTP"
}
+
anchors.fill: parent
model: appWin.listModel
width: parent.width
diff --git a/qml/pages/QRPage.qml b/qml/pages/QRPage.qml
index f5339bd..719eb82 100644
--- a/qml/pages/QRPage.qml
+++ b/qml/pages/QRPage.qml
@@ -33,6 +33,8 @@ import Sailfish.Silica 1.0
Page {
id: qrpage
+ allowedOrientations: Orientation.All
+
property string paramType: ""
property string paramLabel: ""
property string paramKey: ""
diff --git a/qml/pages/ScanOTP.qml b/qml/pages/ScanOTP.qml
index 2821d23..66b5577 100644
--- a/qml/pages/ScanOTP.qml
+++ b/qml/pages/ScanOTP.qml
@@ -37,6 +37,8 @@ import "../lib/urldecoder.js" as URL
Page {
id: scanPage
+ allowedOrientations: Orientation.All
+
property QtObject parentPage: null
property bool scanning: false
diff --git a/rpm/harbour-sailotp.changes b/rpm/harbour-sailotp.changes
index d0deb1b..5aba949 100644
--- a/rpm/harbour-sailotp.changes
+++ b/rpm/harbour-sailotp.changes
@@ -1,3 +1,6 @@
+* Sun Dec 06 2015 Stefan Brand 1.3-1
+- Added SteamGuard OTP Type (Thanks to Robin Appelman)
+
* Sat May 30 2015 Stefan Brand 1.2-1
- Added Swedish translation (Thanks to Åke Engelbrektson)
diff --git a/rpm/harbour-sailotp.yaml b/rpm/harbour-sailotp.yaml
index 1867833..91145bc 100644
--- a/rpm/harbour-sailotp.yaml
+++ b/rpm/harbour-sailotp.yaml
@@ -1,6 +1,6 @@
Name: harbour-sailotp
Summary: SailOTP
-Version: 1.2
+Version: 1.3
Release: 1
Group: Security
URL: https://github.com/seiichiro0185/sailotp/