commit 0c0f10f7abe8bab30c9c0d6ad8c93a611d06bb25 Author: Stefan Brand Date: Sun Jan 5 21:58:25 2014 +0100 Initial Import diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..356e055 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# User Specific Project Files +*.pro.user +*.pro.user.* diff --git a/harbour-sailotp.desktop b/harbour-sailotp.desktop new file mode 100644 index 0000000..8c1a801 --- /dev/null +++ b/harbour-sailotp.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Application +X-Nemo-Application-Type=silica-qt5 +Name=SailOTP +Icon=harbour-sailotp +Exec=harbour-sailotp + diff --git a/harbour-sailotp.png b/harbour-sailotp.png new file mode 100644 index 0000000..b3895eb Binary files /dev/null and b/harbour-sailotp.png differ diff --git a/harbour-sailotp.pro b/harbour-sailotp.pro new file mode 100644 index 0000000..bc48895 --- /dev/null +++ b/harbour-sailotp.pro @@ -0,0 +1,27 @@ +# The name of your app. +# NOTICE: name defined in TARGET has a corresponding QML filename. +# If name defined in TARGET is changed, following needs to be +# done to match new name: +# - corresponding QML filename must be changed +# - desktop icon filename must be changed +# - desktop filename must be changed +# - icon definition filename in desktop file must be changed +TARGET = harbour-sailotp + +CONFIG += sailfishapp + +SOURCES += src/harbour-sailotp.cpp + +OTHER_FILES += qml/harbour-sailotp.qml \ + qml/cover/CoverPage.qml \ + rpm/harbour-sailotp.spec \ + rpm/harbour-sailotp.yaml \ + harbour-sailotp.desktop \ + qml/pages/MainView.qml \ + qml/pages/AddOTP.qml \ + qml/pages/About.qml \ + qml/lib/storage.js \ + qml/lib/crypto.js \ + qml/lib/sha.js \ + qml/sailotp.png + diff --git a/harbour-sailotp.pro.user b/harbour-sailotp.pro.user new file mode 100644 index 0000000..8537e8a --- /dev/null +++ b/harbour-sailotp.pro.user @@ -0,0 +1,529 @@ + + + + + + ProjectExplorer.Project.ActiveTarget + 1 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + true + 1 + true + 0 + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.PluginSettings + + + + ProjectExplorer.Project.Target.0 + + MerSDK-SailfishOS-armv7hl + MerSDK-SailfishOS-armv7hl + {45efb901-3e2e-4d50-95ba-c432b8781a69} + 0 + 0 + 0 + + + + true + qmake + + QtProjectManager.QMakeBuildStep + false + true + + false + + + true + Make + + Qt4ProjectManager.MakeStep + + -w + -r + + false + + + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + -w + -r + + true + clean + + + 1 + Bereinigen + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + + Qt4ProjectManager.Qt4BuildConfiguration + 2 + /home/seiichiro/Projekte/Sailfish/build-harbour-sailotp-MerSDK_SailfishOS_armv7hl-Debug + true + + + + + true + qmake + + QtProjectManager.QMakeBuildStep + false + true + + false + + + true + Make + + Qt4ProjectManager.MakeStep + + -w + -r + + false + + + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + -w + -r + + true + clean + + + 1 + Bereinigen + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + + Qt4ProjectManager.Qt4BuildConfiguration + 0 + /home/seiichiro/Projekte/Sailfish/build-harbour-sailotp-MerSDK_SailfishOS_armv7hl-Release + true + + 2 + + + + true + Rpm + + Qt4ProjectManager.MerRpmBuildStep + + 1 + Deployment + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy By Building An RPM Package + + Qt4ProjectManager.MerArmDeployConfiguration + + + + + true + Rsync + + Qt4ProjectManager.MerRsyncDeployStep + + 1 + Deployment + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy By Copying Binaries + + Qt4ProjectManager.MerRSyncDeployConfiguration + + + + + true + Rpm + + Qt4ProjectManager.MerRpmDeployStep + + 1 + Deployment + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy As RPM Package + + Qt4ProjectManager.MerRpmDeployConfiguration + + 3 + + + true + + false + false + false + false + true + 0.01 + 10 + true + 25 + + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 1 + + harbour-sailotp (auf Mobilgerät) + + Qt4ProjectManager.MerRunConfiguration:/home/seiichiro/Projekte/Sailfish/harbour-sailotp/harbour-sailotp.pro + + harbour-sailotp.pro + + false + + 3768 + true + false + false + true + false + + 1 + + + + ProjectExplorer.Project.Target.1 + + MerSDK-SailfishOS-i486-x86 + MerSDK-SailfishOS-i486-x86 + {08e6ddb6-6d52-4f4e-938d-998fac04baad} + 0 + 0 + 0 + + + + true + qmake + + QtProjectManager.QMakeBuildStep + false + true + + false + + + true + Make + + Qt4ProjectManager.MakeStep + + -w + -r + + false + + + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + -w + -r + + true + clean + + + 1 + Bereinigen + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + + Qt4ProjectManager.Qt4BuildConfiguration + 2 + /home/seiichiro/Projekte/Sailfish/build-harbour-sailotp-MerSDK_SailfishOS_i486_x86-Debug + true + + + + + true + qmake + + QtProjectManager.QMakeBuildStep + false + true + + false + + + true + Make + + Qt4ProjectManager.MakeStep + + -w + -r + + false + + + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + -w + -r + + true + clean + + + 1 + Bereinigen + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + + Qt4ProjectManager.Qt4BuildConfiguration + 0 + /home/seiichiro/Projekte/Sailfish/build-harbour-sailotp-MerSDK_SailfishOS_i486_x86-Release + true + + 2 + + + + true + Start Emulator + + Qt4ProjectManager.MerEmulatorStartStep + + + true + Rsync + + Qt4ProjectManager.MerRsyncDeployStep + + 2 + Deployment + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy By Copying Binaries + + Qt4ProjectManager.MerRSyncDeployConfiguration + + + + + true + Start Emulator + + Qt4ProjectManager.MerEmulatorStartStep + + + true + Rpm + + Qt4ProjectManager.MerRpmDeployStep + + 2 + Deployment + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy As RPM Package + + Qt4ProjectManager.MerRpmDeployConfiguration + + 2 + + + true + + false + false + false + false + true + 0.01 + 10 + true + 25 + + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 1 + + harbour-sailotp (auf Mobilgerät) + + Qt4ProjectManager.MerRunConfiguration:/home/seiichiro/Projekte/Sailfish/harbour-sailotp/harbour-sailotp.pro + + harbour-sailotp.pro + + false + + 3768 + true + false + false + true + false + + 1 + + + + ProjectExplorer.Project.TargetCount + 2 + + + ProjectExplorer.Project.Updater.EnvironmentId + {ea1259ba-c840-46cd-876a-b91d58edff1f} + + + ProjectExplorer.Project.Updater.FileVersion + 14 + + diff --git a/qml/cover/CoverPage.qml b/qml/cover/CoverPage.qml new file mode 100644 index 0000000..07e83ab --- /dev/null +++ b/qml/cover/CoverPage.qml @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2013, Stefan Brand + * 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. + */ + +import QtQuick 2.0 +import Sailfish.Silica 1.0 + +CoverBackground { + + Image { + id: logo + source: "../sailotp.png" + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 48 + } + + Label { + id: label + anchors.centerIn: parent + text: "SailOTP" + } +} diff --git a/qml/harbour-sailotp.qml b/qml/harbour-sailotp.qml new file mode 100644 index 0000000..260a2f3 --- /dev/null +++ b/qml/harbour-sailotp.qml @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013, Stefan Brand + * 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. + */ + +import QtQuick 2.0 +import Sailfish.Silica 1.0 +import "pages" + +ApplicationWindow +{ + initialPage: Component { MainView { } } + cover: Qt.resolvedUrl("cover/CoverPage.qml") +} + + diff --git a/qml/lib/crypto.js b/qml/lib/crypto.js new file mode 100644 index 0000000..2d96a78 --- /dev/null +++ b/qml/lib/crypto.js @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2013, Stefan Brand + * 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. + */ + +.import "./sha.js" as SHA + +// Helper Functions +function dec2hex(s) { return (s < 15.5 ? '0' : '') + Math.round(s).toString(16); } + +function hex2dec(s) { return parseInt(s, 16); } + + +function base32tohex(base32) { + var base32chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + var bits = ""; + var hex = ""; + + for (var i = 0; i < base32.length; i++) { + var val = base32chars.indexOf(base32.charAt(i).toUpperCase()); + bits += leftpad(val.toString(2), 5, '0'); + } + + for (var i = 0; i+4 <= bits.length; i+=4) { + var chunk = bits.substr(i, 4); + hex = hex + parseInt(chunk, 2).toString(16) ; + } + return hex; + +} + +function leftpad(str, len, pad) { + if (len + 1 >= str.length) { + str = Array(len + 1 - str.length).join(pad) + str; + } + return str; +} + +// Calculate an OTP-Value from the given secret +function calcOTP(secret) { + var key = base32tohex(secret); + var epoch = Math.round(new Date().getTime() / 1000.0); + var time = leftpad(dec2hex(Math.floor(epoch / 30)), 16, '0'); + + var hmacObj = new SHA.jsSHA(time, 'HEX'); + var hmac = hmacObj.getHMAC(key, 'HEX', 'SHA-1', "HEX"); + + 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); + return otp; +} diff --git a/qml/lib/sha.js b/qml/lib/sha.js new file mode 100644 index 0000000..8e93be8 --- /dev/null +++ b/qml/lib/sha.js @@ -0,0 +1,1380 @@ +/** + * @preserve A JavaScript implementation of the SHA family of hashes, as + * defined in FIPS PUB 180-2 as well as the corresponding HMAC implementation + * as defined in FIPS PUB 198a + * + * Copyright Brian Turek 2008-2013 + * Distributed under the BSD License + * See http://caligatio.github.com/jsSHA/ for more information + * + * Several functions taken from Paul Johnston + */ + + /** + * SUPPORTED_ALGS is the stub for a compile flag that will cause pruning of + * functions that are not needed when a limited number of SHA families are + * selected + * + * @define {number} ORed value of SHA variants to be supported + * 1 = SHA-1, 2 = SHA-224/SHA-256, 4 = SHA-384/SHA-512 + */ +var SUPPORTED_ALGS = 4 | 2 | 1; + + "use strict"; + /** + * Int_64 is a object for 2 32-bit numbers emulating a 64-bit number + * + * @private + * @constructor + * @this {Int_64} + * @param {number} msint_32 The most significant 32-bits of a 64-bit number + * @param {number} lsint_32 The least significant 32-bits of a 64-bit number + */ + function Int_64(msint_32, lsint_32) + { + this.highOrder = msint_32; + this.lowOrder = lsint_32; + } + + /** + * Convert a string to an array of big-endian words + * + * @private + * @param {string} str String to be converted to binary representation + * @param {string} utfType The Unicode type, UTF8 or UTF16, to use to + * encode the source string + * @return {{value : Array., binLen : number}} Hash list where + * "value" contains the output number array and "binLen" is the binary + * length of "value" + */ + function str2binb(str, utfType) + { + var bin = [], codePnt, binArr = [], byteCnt = 0, i, j; + + if ("UTF8" === utfType) + { + for (i = 0; i < str.length; i += 1) + { + codePnt = str.charCodeAt(i); + binArr = []; + + if (0x800 < codePnt) + { + binArr[0] = 0xE0 | ((codePnt & 0xF000) >>> 12); + binArr[1] = 0x80 | ((codePnt & 0xFC0) >>> 6); + binArr[2] = 0x80 | (codePnt & 0x3F); + } + else if (0x80 < codePnt) + { + binArr[0] = 0xC0 | ((codePnt & 0x7C0) >>> 6); + binArr[1] = 0x80 | (codePnt & 0x3F); + } + else + { + binArr[0] = codePnt; + } + + for (j = 0; j < binArr.length; j += 1) + { + bin[byteCnt >>> 2] |= binArr[j] << (24 - (8 * (byteCnt % 4))); + byteCnt += 1; + } + } + } + else if ("UTF16" === utfType) + { + for (i = 0; i < str.length; i += 1) + { + codePnt = str.charCodeAt(i); + + bin[byteCnt >>> 2] |= str.charCodeAt(i) << (16 - (8 * (byteCnt % 4))); + byteCnt += 2; + } + } + return {"value" : bin, "binLen" : byteCnt * 8}; + } + + /** + * Convert a hex string to an array of big-endian words + * + * @private + * @param {string} str String to be converted to binary representation + * @return {{value : Array., binLen : number}} Hash list where + * "value" contains the output number array and "binLen" is the binary + * length of "value" + */ + function hex2binb(str) + { + var bin = [], length = str.length, i, num; + + if (0 !== (length % 2)) + { + throw "String of HEX type must be in byte increments"; + } + + for (i = 0; i < length; i += 2) + { + num = parseInt(str.substr(i, 2), 16); + if (!isNaN(num)) + { + bin[i >>> 3] |= num << (24 - (4 * (i % 8))); + } + else + { + throw "String of HEX type contains invalid characters"; + } + } + + return {"value" : bin, "binLen" : length * 4}; + } + + /** + * Convert a base-64 string to an array of big-endian words + * + * @private + * @param {string} str String to be converted to binary representation + * @return {{value : Array., binLen : number}} Hash list where + * "value" contains the output number array and "binLen" is the binary + * length of "value" + */ + function b642binb(str) + { + var retVal = [], byteCnt = 0, index, i, j, tmpInt, strPart, firstEqual, + b64Tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + if (-1 === str.search(/^[a-zA-Z0-9=+\/]+$/)) + { + throw "Invalid character in base-64 string"; + } + firstEqual = str.indexOf('='); + str = str.replace(/\=/g, ''); + if ((-1 !== firstEqual) && (firstEqual < str.length)) + { + throw "Invalid '=' found in base-64 string"; + } + + for (i = 0; i < str.length; i += 4) + { + strPart = str.substr(i, 4); + tmpInt = 0; + + for (j = 0; j < strPart.length; j += 1) + { + index = b64Tab.indexOf(strPart[j]); + tmpInt |= index << (18 - (6 * j)); + } + + for (j = 0; j < strPart.length - 1; j += 1) + { + retVal[byteCnt >> 2] |= ((tmpInt >>> (16 - (j * 8))) & 0xFF) << + (24 - (8 * (byteCnt % 4))); + byteCnt += 1; + } + } + + return {"value" : retVal, "binLen" : byteCnt * 8}; + } + + /** + * Convert an array of big-endian words to a hex string. + * + * @private + * @param {Array.} binarray Array of integers to be converted to + * hexidecimal representation + * @param {{outputUpper : boolean, b64Pad : string}} formatOpts Hash list + * containing validated output formatting options + * @return {string} Hexidecimal representation of the parameter in String + * form + */ + function binb2hex(binarray, formatOpts) + { + var hex_tab = "0123456789abcdef", str = "", + length = binarray.length * 4, i, srcByte; + + for (i = 0; i < length; i += 1) + { + srcByte = binarray[i >>> 2] >>> ((3 - (i % 4)) * 8); + str += hex_tab.charAt((srcByte >>> 4) & 0xF) + + hex_tab.charAt(srcByte & 0xF); + } + + return (formatOpts["outputUpper"]) ? str.toUpperCase() : str; + } + + /** + * Convert an array of big-endian words to a base-64 string + * + * @private + * @param {Array.} binarray Array of integers to be converted to + * base-64 representation + * @param {{outputUpper : boolean, b64Pad : string}} formatOpts Hash list + * containing validated output formatting options + * @return {string} Base-64 encoded representation of the parameter in + * String form + */ + function binb2b64(binarray, formatOpts) + { + var str = "", length = binarray.length * 4, i, j, triplet, + b64Tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + for (i = 0; i < length; i += 3) + { + triplet = (((binarray[i >>> 2] >>> 8 * (3 - i % 4)) & 0xFF) << 16) | + (((binarray[i + 1 >>> 2] >>> 8 * (3 - (i + 1) % 4)) & 0xFF) << 8) | + ((binarray[i + 2 >>> 2] >>> 8 * (3 - (i + 2) % 4)) & 0xFF); + for (j = 0; j < 4; j += 1) + { + if (i * 8 + j * 6 <= binarray.length * 32) + { + str += b64Tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F); + } + else + { + str += formatOpts["b64Pad"]; + } + } + } + return str; + } + + /** + * Validate hash list containing output formatting options, ensuring + * presence of every option or adding the default value + * + * @private + * @param {{outputUpper : boolean, b64Pad : string}|undefined} outputOpts + * Hash list of output formatting options + * @return {{outputUpper : boolean, b64Pad : string}} Validated hash list + * containing output formatting options + */ + function getOutputOpts(outputOpts) + { + var retVal = {"outputUpper" : false, "b64Pad" : "="}; + + try + { + if (outputOpts.hasOwnProperty("outputUpper")) + { + retVal["outputUpper"] = outputOpts["outputUpper"]; + } + + if (outputOpts.hasOwnProperty("b64Pad")) + { + retVal["b64Pad"] = outputOpts["b64Pad"]; + } + } + catch(ignore) + {} + + if ("boolean" !== typeof(retVal["outputUpper"])) + { + throw "Invalid outputUpper formatting option"; + } + + if ("string" !== typeof(retVal["b64Pad"])) + { + throw "Invalid b64Pad formatting option"; + } + + return retVal; + } + + /** + * The 32-bit implementation of circular rotate left + * + * @private + * @param {number} x The 32-bit integer argument + * @param {number} n The number of bits to shift + * @return {number} The x shifted circularly by n bits + */ + function rotl_32(x, n) + { + return (x << n) | (x >>> (32 - n)); + } + + /** + * The 32-bit implementation of circular rotate right + * + * @private + * @param {number} x The 32-bit integer argument + * @param {number} n The number of bits to shift + * @return {number} The x shifted circularly by n bits + */ + function rotr_32(x, n) + { + return (x >>> n) | (x << (32 - n)); + } + + /** + * The 64-bit implementation of circular rotate right + * + * @private + * @param {Int_64} x The 64-bit integer argument + * @param {number} n The number of bits to shift + * @return {Int_64} The x shifted circularly by n bits + */ + function rotr_64(x, n) + { + var retVal = null, tmp = new Int_64(x.highOrder, x.lowOrder); + + if (32 >= n) + { + retVal = new Int_64( + (tmp.highOrder >>> n) | ((tmp.lowOrder << (32 - n)) & 0xFFFFFFFF), + (tmp.lowOrder >>> n) | ((tmp.highOrder << (32 - n)) & 0xFFFFFFFF) + ); + } + else + { + retVal = new Int_64( + (tmp.lowOrder >>> (n - 32)) | ((tmp.highOrder << (64 - n)) & 0xFFFFFFFF), + (tmp.highOrder >>> (n - 32)) | ((tmp.lowOrder << (64 - n)) & 0xFFFFFFFF) + ); + } + + return retVal; + } + + /** + * The 32-bit implementation of shift right + * + * @private + * @param {number} x The 32-bit integer argument + * @param {number} n The number of bits to shift + * @return {number} The x shifted by n bits + */ + function shr_32(x, n) + { + return x >>> n; + } + + /** + * The 64-bit implementation of shift right + * + * @private + * @param {Int_64} x The 64-bit integer argument + * @param {number} n The number of bits to shift + * @return {Int_64} The x shifted by n bits + */ + function shr_64(x, n) + { + var retVal = null; + + if (32 >= n) + { + retVal = new Int_64( + x.highOrder >>> n, + x.lowOrder >>> n | ((x.highOrder << (32 - n)) & 0xFFFFFFFF) + ); + } + else + { + retVal = new Int_64( + 0, + x.highOrder >>> (n - 32) + ); + } + + return retVal; + } + + /** + * The 32-bit implementation of the NIST specified Parity function + * + * @private + * @param {number} x The first 32-bit integer argument + * @param {number} y The second 32-bit integer argument + * @param {number} z The third 32-bit integer argument + * @return {number} The NIST specified output of the function + */ + function parity_32(x, y, z) + { + return x ^ y ^ z; + } + + /** + * The 32-bit implementation of the NIST specified Ch function + * + * @private + * @param {number} x The first 32-bit integer argument + * @param {number} y The second 32-bit integer argument + * @param {number} z The third 32-bit integer argument + * @return {number} The NIST specified output of the function + */ + function ch_32(x, y, z) + { + return (x & y) ^ (~x & z); + } + + /** + * The 64-bit implementation of the NIST specified Ch function + * + * @private + * @param {Int_64} x The first 64-bit integer argument + * @param {Int_64} y The second 64-bit integer argument + * @param {Int_64} z The third 64-bit integer argument + * @return {Int_64} The NIST specified output of the function + */ + function ch_64(x, y, z) + { + return new Int_64( + (x.highOrder & y.highOrder) ^ (~x.highOrder & z.highOrder), + (x.lowOrder & y.lowOrder) ^ (~x.lowOrder & z.lowOrder) + ); + } + + /** + * The 32-bit implementation of the NIST specified Maj function + * + * @private + * @param {number} x The first 32-bit integer argument + * @param {number} y The second 32-bit integer argument + * @param {number} z The third 32-bit integer argument + * @return {number} The NIST specified output of the function + */ + function maj_32(x, y, z) + { + return (x & y) ^ (x & z) ^ (y & z); + } + + /** + * The 64-bit implementation of the NIST specified Maj function + * + * @private + * @param {Int_64} x The first 64-bit integer argument + * @param {Int_64} y The second 64-bit integer argument + * @param {Int_64} z The third 64-bit integer argument + * @return {Int_64} The NIST specified output of the function + */ + function maj_64(x, y, z) + { + return new Int_64( + (x.highOrder & y.highOrder) ^ + (x.highOrder & z.highOrder) ^ + (y.highOrder & z.highOrder), + (x.lowOrder & y.lowOrder) ^ + (x.lowOrder & z.lowOrder) ^ + (y.lowOrder & z.lowOrder) + ); + } + + /** + * The 32-bit implementation of the NIST specified Sigma0 function + * + * @private + * @param {number} x The 32-bit integer argument + * @return {number} The NIST specified output of the function + */ + function sigma0_32(x) + { + return rotr_32(x, 2) ^ rotr_32(x, 13) ^ rotr_32(x, 22); + } + + /** + * The 64-bit implementation of the NIST specified Sigma0 function + * + * @private + * @param {Int_64} x The 64-bit integer argument + * @return {Int_64} The NIST specified output of the function + */ + function sigma0_64(x) + { + var rotr28 = rotr_64(x, 28), rotr34 = rotr_64(x, 34), + rotr39 = rotr_64(x, 39); + + return new Int_64( + rotr28.highOrder ^ rotr34.highOrder ^ rotr39.highOrder, + rotr28.lowOrder ^ rotr34.lowOrder ^ rotr39.lowOrder); + } + + /** + * The 32-bit implementation of the NIST specified Sigma1 function + * + * @private + * @param {number} x The 32-bit integer argument + * @return {number} The NIST specified output of the function + */ + function sigma1_32(x) + { + return rotr_32(x, 6) ^ rotr_32(x, 11) ^ rotr_32(x, 25); + } + + /** + * The 64-bit implementation of the NIST specified Sigma1 function + * + * @private + * @param {Int_64} x The 64-bit integer argument + * @return {Int_64} The NIST specified output of the function + */ + function sigma1_64(x) + { + var rotr14 = rotr_64(x, 14), rotr18 = rotr_64(x, 18), + rotr41 = rotr_64(x, 41); + + return new Int_64( + rotr14.highOrder ^ rotr18.highOrder ^ rotr41.highOrder, + rotr14.lowOrder ^ rotr18.lowOrder ^ rotr41.lowOrder); + } + + /** + * The 32-bit implementation of the NIST specified Gamma0 function + * + * @private + * @param {number} x The 32-bit integer argument + * @return {number} The NIST specified output of the function + */ + function gamma0_32(x) + { + return rotr_32(x, 7) ^ rotr_32(x, 18) ^ shr_32(x, 3); + } + + /** + * The 64-bit implementation of the NIST specified Gamma0 function + * + * @private + * @param {Int_64} x The 64-bit integer argument + * @return {Int_64} The NIST specified output of the function + */ + function gamma0_64(x) + { + var rotr1 = rotr_64(x, 1), rotr8 = rotr_64(x, 8), shr7 = shr_64(x, 7); + + return new Int_64( + rotr1.highOrder ^ rotr8.highOrder ^ shr7.highOrder, + rotr1.lowOrder ^ rotr8.lowOrder ^ shr7.lowOrder + ); + } + + /** + * The 32-bit implementation of the NIST specified Gamma1 function + * + * @private + * @param {number} x The 32-bit integer argument + * @return {number} The NIST specified output of the function + */ + function gamma1_32(x) + { + return rotr_32(x, 17) ^ rotr_32(x, 19) ^ shr_32(x, 10); + } + + /** + * The 64-bit implementation of the NIST specified Gamma1 function + * + * @private + * @param {Int_64} x The 64-bit integer argument + * @return {Int_64} The NIST specified output of the function + */ + function gamma1_64(x) + { + var rotr19 = rotr_64(x, 19), rotr61 = rotr_64(x, 61), + shr6 = shr_64(x, 6); + + return new Int_64( + rotr19.highOrder ^ rotr61.highOrder ^ shr6.highOrder, + rotr19.lowOrder ^ rotr61.lowOrder ^ shr6.lowOrder + ); + } + + /** + * Add two 32-bit integers, wrapping at 2^32. This uses 16-bit operations + * internally to work around bugs in some JS interpreters. + * + * @private + * @param {number} a The first 32-bit integer argument to be added + * @param {number} b The second 32-bit integer argument to be added + * @return {number} The sum of a + b + */ + function safeAdd_32_2(a, b) + { + var lsw = (a & 0xFFFF) + (b & 0xFFFF), + msw = (a >>> 16) + (b >>> 16) + (lsw >>> 16); + + return ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + } + + /** + * Add four 32-bit integers, wrapping at 2^32. This uses 16-bit operations + * internally to work around bugs in some JS interpreters. + * + * @private + * @param {number} a The first 32-bit integer argument to be added + * @param {number} b The second 32-bit integer argument to be added + * @param {number} c The third 32-bit integer argument to be added + * @param {number} d The fourth 32-bit integer argument to be added + * @return {number} The sum of a + b + c + d + */ + function safeAdd_32_4(a, b, c, d) + { + var lsw = (a & 0xFFFF) + (b & 0xFFFF) + (c & 0xFFFF) + (d & 0xFFFF), + msw = (a >>> 16) + (b >>> 16) + (c >>> 16) + (d >>> 16) + + (lsw >>> 16); + + return ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + } + + /** + * Add five 32-bit integers, wrapping at 2^32. This uses 16-bit operations + * internally to work around bugs in some JS interpreters. + * + * @private + * @param {number} a The first 32-bit integer argument to be added + * @param {number} b The second 32-bit integer argument to be added + * @param {number} c The third 32-bit integer argument to be added + * @param {number} d The fourth 32-bit integer argument to be added + * @param {number} e The fifth 32-bit integer argument to be added + * @return {number} The sum of a + b + c + d + e + */ + function safeAdd_32_5(a, b, c, d, e) + { + var lsw = (a & 0xFFFF) + (b & 0xFFFF) + (c & 0xFFFF) + (d & 0xFFFF) + + (e & 0xFFFF), + msw = (a >>> 16) + (b >>> 16) + (c >>> 16) + (d >>> 16) + + (e >>> 16) + (lsw >>> 16); + + return ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + } + + /** + * Add two 64-bit integers, wrapping at 2^64. This uses 16-bit operations + * internally to work around bugs in some JS interpreters. + * + * @private + * @param {Int_64} x The first 64-bit integer argument to be added + * @param {Int_64} y The second 64-bit integer argument to be added + * @return {Int_64} The sum of x + y + */ + function safeAdd_64_2(x, y) + { + var lsw, msw, lowOrder, highOrder; + + lsw = (x.lowOrder & 0xFFFF) + (y.lowOrder & 0xFFFF); + msw = (x.lowOrder >>> 16) + (y.lowOrder >>> 16) + (lsw >>> 16); + lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + + lsw = (x.highOrder & 0xFFFF) + (y.highOrder & 0xFFFF) + (msw >>> 16); + msw = (x.highOrder >>> 16) + (y.highOrder >>> 16) + (lsw >>> 16); + highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + + return new Int_64(highOrder, lowOrder); + } + + /** + * Add four 64-bit integers, wrapping at 2^64. This uses 16-bit operations + * internally to work around bugs in some JS interpreters. + * + * @private + * @param {Int_64} a The first 64-bit integer argument to be added + * @param {Int_64} b The second 64-bit integer argument to be added + * @param {Int_64} c The third 64-bit integer argument to be added + * @param {Int_64} d The fouth 64-bit integer argument to be added + * @return {Int_64} The sum of a + b + c + d + */ + function safeAdd_64_4(a, b, c, d) + { + var lsw, msw, lowOrder, highOrder; + + lsw = (a.lowOrder & 0xFFFF) + (b.lowOrder & 0xFFFF) + + (c.lowOrder & 0xFFFF) + (d.lowOrder & 0xFFFF); + msw = (a.lowOrder >>> 16) + (b.lowOrder >>> 16) + + (c.lowOrder >>> 16) + (d.lowOrder >>> 16) + (lsw >>> 16); + lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + + lsw = (a.highOrder & 0xFFFF) + (b.highOrder & 0xFFFF) + + (c.highOrder & 0xFFFF) + (d.highOrder & 0xFFFF) + (msw >>> 16); + msw = (a.highOrder >>> 16) + (b.highOrder >>> 16) + + (c.highOrder >>> 16) + (d.highOrder >>> 16) + (lsw >>> 16); + highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + + return new Int_64(highOrder, lowOrder); + } + + /** + * Add five 64-bit integers, wrapping at 2^64. This uses 16-bit operations + * internally to work around bugs in some JS interpreters. + * + * @private + * @param {Int_64} a The first 64-bit integer argument to be added + * @param {Int_64} b The second 64-bit integer argument to be added + * @param {Int_64} c The third 64-bit integer argument to be added + * @param {Int_64} d The fouth 64-bit integer argument to be added + * @param {Int_64} e The fouth 64-bit integer argument to be added + * @return {Int_64} The sum of a + b + c + d + e + */ + function safeAdd_64_5(a, b, c, d, e) + { + var lsw, msw, lowOrder, highOrder; + + lsw = (a.lowOrder & 0xFFFF) + (b.lowOrder & 0xFFFF) + + (c.lowOrder & 0xFFFF) + (d.lowOrder & 0xFFFF) + + (e.lowOrder & 0xFFFF); + msw = (a.lowOrder >>> 16) + (b.lowOrder >>> 16) + + (c.lowOrder >>> 16) + (d.lowOrder >>> 16) + (e.lowOrder >>> 16) + + (lsw >>> 16); + lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + + lsw = (a.highOrder & 0xFFFF) + (b.highOrder & 0xFFFF) + + (c.highOrder & 0xFFFF) + (d.highOrder & 0xFFFF) + + (e.highOrder & 0xFFFF) + (msw >>> 16); + msw = (a.highOrder >>> 16) + (b.highOrder >>> 16) + + (c.highOrder >>> 16) + (d.highOrder >>> 16) + + (e.highOrder >>> 16) + (lsw >>> 16); + highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + + return new Int_64(highOrder, lowOrder); + } + + /** + * Calculates the SHA-1 hash of the string set at instantiation + * + * @private + * @param {Array.} message The binary array representation of the + * string to hash + * @param {number} messageLen The number of bits in the message + * @return {Array.} The array of integers representing the SHA-1 + * hash of message + */ + function coreSHA1(message, messageLen) + { + var W = [], a, b, c, d, e, T, ch = ch_32, parity = parity_32, + maj = maj_32, rotl = rotl_32, safeAdd_2 = safeAdd_32_2, i, t, + safeAdd_5 = safeAdd_32_5, appendedMessageLength, + H = [ + 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 + ]; + + /* Append '1' at the end of the binary string */ + message[messageLen >>> 5] |= 0x80 << (24 - (messageLen % 32)); + /* Append length of binary string in the position such that the new + length is a multiple of 512. Logic does not work for even multiples + of 512 but there can never be even multiples of 512 */ + message[(((messageLen + 65) >>> 9) << 4) + 15] = messageLen; + + appendedMessageLength = message.length; + + for (i = 0; i < appendedMessageLength; i += 16) + { + a = H[0]; + b = H[1]; + c = H[2]; + d = H[3]; + e = H[4]; + + for (t = 0; t < 80; t += 1) + { + if (t < 16) + { + W[t] = message[t + i]; + } + else + { + W[t] = rotl(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1); + } + + if (t < 20) + { + T = safeAdd_5(rotl(a, 5), ch(b, c, d), e, 0x5a827999, W[t]); + } + else if (t < 40) + { + T = safeAdd_5(rotl(a, 5), parity(b, c, d), e, 0x6ed9eba1, W[t]); + } + else if (t < 60) + { + T = safeAdd_5(rotl(a, 5), maj(b, c, d), e, 0x8f1bbcdc, W[t]); + } else { + T = safeAdd_5(rotl(a, 5), parity(b, c, d), e, 0xca62c1d6, W[t]); + } + + e = d; + d = c; + c = rotl(b, 30); + b = a; + a = T; + } + + H[0] = safeAdd_2(a, H[0]); + H[1] = safeAdd_2(b, H[1]); + H[2] = safeAdd_2(c, H[2]); + H[3] = safeAdd_2(d, H[3]); + H[4] = safeAdd_2(e, H[4]); + } + + return H; + } + + /** + * Calculates the desired SHA-2 hash of the string set at instantiation + * + * @private + * @param {Array.} message The binary array representation of the + * string to hash + * @param {number} messageLen The number of bits in message + * @param {string} variant The desired SHA-2 variant + * @return {Array.} The array of integers representing the SHA-2 + * hash of message + */ + function coreSHA2(message, messageLen, variant) + { + var a, b, c, d, e, f, g, h, T1, T2, H, numRounds, lengthPosition, i, t, + binaryStringInc, binaryStringMult, safeAdd_2, safeAdd_4, safeAdd_5, + gamma0, gamma1, sigma0, sigma1, ch, maj, Int, W = [], + appendedMessageLength, retVal, + K = [ + 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, + 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, + 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, + 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, + 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, + 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, + 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, + 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, + 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, + 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, + 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, + 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, + 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, + 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, + 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, + 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 + ], + H_trunc = [ + 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, + 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4 + ], + H_full = [ + 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, + 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 + ]; + + /* Set up the various function handles and variable for the specific + * variant */ + if ((variant === "SHA-224" || variant === "SHA-256") && + (2 & SUPPORTED_ALGS)) + { + /* 32-bit variant */ + numRounds = 64; + lengthPosition = (((messageLen + 65) >>> 9) << 4) + 15; + binaryStringInc = 16; + binaryStringMult = 1; + Int = Number; + safeAdd_2 = safeAdd_32_2; + safeAdd_4 = safeAdd_32_4; + safeAdd_5 = safeAdd_32_5; + gamma0 = gamma0_32; + gamma1 = gamma1_32; + sigma0 = sigma0_32; + sigma1 = sigma1_32; + maj = maj_32; + ch = ch_32; + + if ("SHA-224" === variant) + { + H = H_trunc; + } + else /* "SHA-256" === variant */ + { + H = H_full; + } + } + else if ((variant === "SHA-384" || variant === "SHA-512") && + (4 & SUPPORTED_ALGS)) + { + /* 64-bit variant */ + numRounds = 80; + lengthPosition = (((messageLen + 128) >>> 10) << 5) + 31; + binaryStringInc = 32; + binaryStringMult = 2; + Int = Int_64; + safeAdd_2 = safeAdd_64_2; + safeAdd_4 = safeAdd_64_4; + safeAdd_5 = safeAdd_64_5; + gamma0 = gamma0_64; + gamma1 = gamma1_64; + sigma0 = sigma0_64; + sigma1 = sigma1_64; + maj = maj_64; + ch = ch_64; + + K = [ + new Int(K[ 0], 0xd728ae22), new Int(K[ 1], 0x23ef65cd), + new Int(K[ 2], 0xec4d3b2f), new Int(K[ 3], 0x8189dbbc), + new Int(K[ 4], 0xf348b538), new Int(K[ 5], 0xb605d019), + new Int(K[ 6], 0xaf194f9b), new Int(K[ 7], 0xda6d8118), + new Int(K[ 8], 0xa3030242), new Int(K[ 9], 0x45706fbe), + new Int(K[10], 0x4ee4b28c), new Int(K[11], 0xd5ffb4e2), + new Int(K[12], 0xf27b896f), new Int(K[13], 0x3b1696b1), + new Int(K[14], 0x25c71235), new Int(K[15], 0xcf692694), + new Int(K[16], 0x9ef14ad2), new Int(K[17], 0x384f25e3), + new Int(K[18], 0x8b8cd5b5), new Int(K[19], 0x77ac9c65), + new Int(K[20], 0x592b0275), new Int(K[21], 0x6ea6e483), + new Int(K[22], 0xbd41fbd4), new Int(K[23], 0x831153b5), + new Int(K[24], 0xee66dfab), new Int(K[25], 0x2db43210), + new Int(K[26], 0x98fb213f), new Int(K[27], 0xbeef0ee4), + new Int(K[28], 0x3da88fc2), new Int(K[29], 0x930aa725), + new Int(K[30], 0xe003826f), new Int(K[31], 0x0a0e6e70), + new Int(K[32], 0x46d22ffc), new Int(K[33], 0x5c26c926), + new Int(K[34], 0x5ac42aed), new Int(K[35], 0x9d95b3df), + new Int(K[36], 0x8baf63de), new Int(K[37], 0x3c77b2a8), + new Int(K[38], 0x47edaee6), new Int(K[39], 0x1482353b), + new Int(K[40], 0x4cf10364), new Int(K[41], 0xbc423001), + new Int(K[42], 0xd0f89791), new Int(K[43], 0x0654be30), + new Int(K[44], 0xd6ef5218), new Int(K[45], 0x5565a910), + new Int(K[46], 0x5771202a), new Int(K[47], 0x32bbd1b8), + new Int(K[48], 0xb8d2d0c8), new Int(K[49], 0x5141ab53), + new Int(K[50], 0xdf8eeb99), new Int(K[51], 0xe19b48a8), + new Int(K[52], 0xc5c95a63), new Int(K[53], 0xe3418acb), + new Int(K[54], 0x7763e373), new Int(K[55], 0xd6b2b8a3), + new Int(K[56], 0x5defb2fc), new Int(K[57], 0x43172f60), + new Int(K[58], 0xa1f0ab72), new Int(K[59], 0x1a6439ec), + new Int(K[60], 0x23631e28), new Int(K[61], 0xde82bde9), + new Int(K[62], 0xb2c67915), new Int(K[63], 0xe372532b), + new Int(0xca273ece, 0xea26619c), new Int(0xd186b8c7, 0x21c0c207), + new Int(0xeada7dd6, 0xcde0eb1e), new Int(0xf57d4f7f, 0xee6ed178), + new Int(0x06f067aa, 0x72176fba), new Int(0x0a637dc5, 0xa2c898a6), + new Int(0x113f9804, 0xbef90dae), new Int(0x1b710b35, 0x131c471b), + new Int(0x28db77f5, 0x23047d84), new Int(0x32caab7b, 0x40c72493), + new Int(0x3c9ebe0a, 0x15c9bebc), new Int(0x431d67c4, 0x9c100d4c), + new Int(0x4cc5d4be, 0xcb3e42b6), new Int(0x597f299c, 0xfc657e2a), + new Int(0x5fcb6fab, 0x3ad6faec), new Int(0x6c44198c, 0x4a475817) + ]; + + if ("SHA-384" === variant) + { + H = [ + new Int(0xcbbb9d5d, H_trunc[0]), new Int(0x0629a292a, H_trunc[1]), + new Int(0x9159015a, H_trunc[2]), new Int(0x0152fecd8, H_trunc[3]), + new Int(0x67332667, H_trunc[4]), new Int(0x98eb44a87, H_trunc[5]), + new Int(0xdb0c2e0d, H_trunc[6]), new Int(0x047b5481d, H_trunc[7]) + ]; + } + else /* "SHA-512" === variant */ + { + H = [ + new Int(H_full[0], 0xf3bcc908), new Int(H_full[1], 0x84caa73b), + new Int(H_full[2], 0xfe94f82b), new Int(H_full[3], 0x5f1d36f1), + new Int(H_full[4], 0xade682d1), new Int(H_full[5], 0x2b3e6c1f), + new Int(H_full[6], 0xfb41bd6b), new Int(H_full[7], 0x137e2179) + ]; + } + } + else + { + throw "Unexpected error in SHA-2 implementation"; + } + + /* Append '1' at the end of the binary string */ + message[messageLen >>> 5] |= 0x80 << (24 - messageLen % 32); + /* Append length of binary string in the position such that the new + * length is correct */ + message[lengthPosition] = messageLen; + + appendedMessageLength = message.length; + + for (i = 0; i < appendedMessageLength; i += binaryStringInc) + { + a = H[0]; + b = H[1]; + c = H[2]; + d = H[3]; + e = H[4]; + f = H[5]; + g = H[6]; + h = H[7]; + + for (t = 0; t < numRounds; t += 1) + { + if (t < 16) + { + /* Bit of a hack - for 32-bit, the second term is ignored */ + W[t] = new Int(message[t * binaryStringMult + i], + message[t * binaryStringMult + i + 1]); + } + else + { + W[t] = safeAdd_4( + gamma1(W[t - 2]), W[t - 7], + gamma0(W[t - 15]), W[t - 16] + ); + } + + T1 = safeAdd_5(h, sigma1(e), ch(e, f, g), K[t], W[t]); + T2 = safeAdd_2(sigma0(a), maj(a, b, c)); + h = g; + g = f; + f = e; + e = safeAdd_2(d, T1); + d = c; + c = b; + b = a; + a = safeAdd_2(T1, T2); + + } + + H[0] = safeAdd_2(a, H[0]); + H[1] = safeAdd_2(b, H[1]); + H[2] = safeAdd_2(c, H[2]); + H[3] = safeAdd_2(d, H[3]); + H[4] = safeAdd_2(e, H[4]); + H[5] = safeAdd_2(f, H[5]); + H[6] = safeAdd_2(g, H[6]); + H[7] = safeAdd_2(h, H[7]); + } + + if (("SHA-224" === variant) && (2 & SUPPORTED_ALGS)) + { + retVal = [ + H[0], H[1], H[2], H[3], + H[4], H[5], H[6] + ]; + } + else if (("SHA-256" === variant) && (2 & SUPPORTED_ALGS)) + { + retVal = H; + } + else if (("SHA-384" === variant) && (4 & SUPPORTED_ALGS)) + { + retVal = [ + H[0].highOrder, H[0].lowOrder, + H[1].highOrder, H[1].lowOrder, + H[2].highOrder, H[2].lowOrder, + H[3].highOrder, H[3].lowOrder, + H[4].highOrder, H[4].lowOrder, + H[5].highOrder, H[5].lowOrder + ]; + } + else if (("SHA-512" === variant) && (4 & SUPPORTED_ALGS)) + { + retVal = [ + H[0].highOrder, H[0].lowOrder, + H[1].highOrder, H[1].lowOrder, + H[2].highOrder, H[2].lowOrder, + H[3].highOrder, H[3].lowOrder, + H[4].highOrder, H[4].lowOrder, + H[5].highOrder, H[5].lowOrder, + H[6].highOrder, H[6].lowOrder, + H[7].highOrder, H[7].lowOrder + ]; + } + else /* This should never be reached */ + { + throw "Unexpected error in SHA-2 implementation"; + } + + return retVal; + } + + /** + * jsSHA is the workhorse of the library. Instantiate it with the string to + * be hashed as the parameter + * + * @constructor + * @this {jsSHA} + * @param {string} srcString The string to be hashed + * @param {string} inputFormat The format of srcString, HEX, TEXT, or ASCII + * @param {string=} encoding The text encoding to use to encode the source + * string + */ + var jsSHA = function(srcString, inputFormat, encoding) + { + var strBinLen = 0, strToHash = [0], utfType = '', srcConvertRet = null; + + utfType = encoding || "UTF8"; + + if (!(("UTF8" === utfType) || ("UTF16" === utfType))) + { + throw "encoding must be UTF8 or UTF16"; + } + + /* Convert the input string into the correct type */ + if ("HEX" === inputFormat) + { + if (0 !== (srcString.length % 2)) + { + throw "srcString of HEX type must be in byte increments"; + } + srcConvertRet = hex2binb(srcString); + strBinLen = srcConvertRet["binLen"]; + strToHash = srcConvertRet["value"]; + } + else if (("ASCII" === inputFormat) || ("TEXT" === inputFormat)) + { + srcConvertRet = str2binb(srcString, utfType); + strBinLen = srcConvertRet["binLen"]; + strToHash = srcConvertRet["value"]; + } + else if ("B64" === inputFormat) + { + srcConvertRet = b642binb(srcString); + strBinLen = srcConvertRet["binLen"]; + strToHash = srcConvertRet["value"]; + } + else + { + throw "inputFormat must be HEX, TEXT, ASCII, or B64"; + } + + /** + * Returns the desired SHA hash of the string specified at instantiation + * using the specified parameters + * + * @expose + * @param {string} variant The desired SHA variant (SHA-1, SHA-224, + * SHA-256, SHA-384, or SHA-512) + * @param {string} format The desired output formatting (B64 or HEX) + * @param {number=} numRounds The number of rounds of hashing to be + * executed + * @param {{outputUpper : boolean, b64Pad : string}=} outputFormatOpts + * Hash list of output formatting options + * @return {string} The string representation of the hash in the format + * specified + */ + this.getHash = function(variant, format, numRounds, outputFormatOpts) + { + var formatFunc = null, message = strToHash.slice(), + messageBinLen = strBinLen, i; + + /* Need to do argument patching since both numRounds and + outputFormatOpts are optional */ + if (3 === arguments.length) + { + if ("number" !== typeof numRounds) + { + outputFormatOpts = numRounds; + numRounds = 1; + } + } + else if (2 === arguments.length) + { + numRounds = 1; + } + + /* Validate the numRounds argument */ + if ((numRounds !== parseInt(numRounds, 10)) || (1 > numRounds)) + { + throw "numRounds must a integer >= 1"; + } + + /* Validate the output format selection */ + switch (format) + { + case "HEX": + formatFunc = binb2hex; + break; + case "B64": + formatFunc = binb2b64; + break; + default: + throw "format must be HEX or B64"; + } + + if (("SHA-1" === variant) && (1 & SUPPORTED_ALGS)) + { + for (i = 0; i < numRounds; i++) + { + message = coreSHA1(message, messageBinLen); + messageBinLen = 160; + } + } + else if (("SHA-224" === variant) && (2 & SUPPORTED_ALGS)) + { + for (i = 0; i < numRounds; i++) + { + message = coreSHA2(message, messageBinLen, variant); + messageBinLen = 224; + } + } + else if (("SHA-256" === variant) && (2 & SUPPORTED_ALGS)) + { + for (i = 0; i < numRounds; i++) + { + message = coreSHA2(message, messageBinLen, variant); + messageBinLen = 256; + } + } + else if (("SHA-384" === variant) && (4 & SUPPORTED_ALGS)) + { + for (i = 0; i < numRounds; i++) + { + message = coreSHA2(message, messageBinLen, variant); + messageBinLen = 384; + } + } + else if (("SHA-512" === variant) && (4 & SUPPORTED_ALGS)) + { + for (i = 0; i < numRounds; i++) + { + message = coreSHA2(message, messageBinLen, variant); + messageBinLen = 512; + } + } + else + { + throw "Chosen SHA variant is not supported"; + } + + return formatFunc(message, getOutputOpts(outputFormatOpts)); + }; + + /** + * Returns the desired HMAC of the string specified at instantiation + * using the key and variant parameter + * + * @expose + * @param {string} key The key used to calculate the HMAC + * @param {string} inputFormat The format of key, HEX or TEXT or ASCII + * @param {string} variant The desired SHA variant (SHA-1, SHA-224, + * SHA-256, SHA-384, or SHA-512) + * @param {string} outputFormat The desired output formatting + * (B64 or HEX) + * @param {{outputUpper : boolean, b64Pad : string}=} outputFormatOpts + * associative array of output formatting options + * @return {string} The string representation of the hash in the format + * specified + */ + this.getHMAC = function(key, inputFormat, variant, outputFormat, + outputFormatOpts) + { + var formatFunc, keyToUse, blockByteSize, blockBitSize, i, + retVal, lastArrayIndex, keyBinLen, hashBitSize, + keyWithIPad = [], keyWithOPad = [], keyConvertRet = null; + + /* Validate the output format selection */ + switch (outputFormat) + { + case "HEX": + formatFunc = binb2hex; + break; + case "B64": + formatFunc = binb2b64; + break; + default: + throw "outputFormat must be HEX or B64"; + } + + /* Validate the hash variant selection and set needed variables */ + if (("SHA-1" === variant) && (1 & SUPPORTED_ALGS)) + { + blockByteSize = 64; + hashBitSize = 160; + } + else if (("SHA-224" === variant) && (2 & SUPPORTED_ALGS)) + { + blockByteSize = 64; + hashBitSize = 224; + } + else if (("SHA-256" === variant) && (2 & SUPPORTED_ALGS)) + { + blockByteSize = 64; + hashBitSize = 256; + } + else if (("SHA-384" === variant) && (4 & SUPPORTED_ALGS)) + { + blockByteSize = 128; + hashBitSize = 384; + } + else if (("SHA-512" === variant) && (4 & SUPPORTED_ALGS)) + { + blockByteSize = 128; + hashBitSize = 512; + } + else + { + throw "Chosen SHA variant is not supported"; + } + + /* Validate input format selection */ + if ("HEX" === inputFormat) + { + keyConvertRet = hex2binb(key); + keyBinLen = keyConvertRet["binLen"]; + keyToUse = keyConvertRet["value"]; + } + else if (("ASCII" === inputFormat) || ("TEXT" === inputFormat)) + { + keyConvertRet = str2binb(key, utfType); + keyBinLen = keyConvertRet["binLen"]; + keyToUse = keyConvertRet["value"]; + } + else if ("B64" === inputFormat) + { + keyConvertRet = b642binb(key); + keyBinLen = keyConvertRet["binLen"]; + keyToUse = keyConvertRet["value"]; + } + else + { + throw "inputFormat must be HEX, TEXT, ASCII, or B64"; + } + + /* These are used multiple times, calculate and store them */ + blockBitSize = blockByteSize * 8; + lastArrayIndex = (blockByteSize / 4) - 1; + + /* Figure out what to do with the key based on its size relative to + * the hash's block size */ + if (blockByteSize < (keyBinLen / 8)) + { + if (("SHA-1" === variant) && (1 & SUPPORTED_ALGS)) + { + keyToUse = coreSHA1(keyToUse, keyBinLen); + } + else if (6 & SUPPORTED_ALGS) + { + keyToUse = coreSHA2(keyToUse, keyBinLen, variant); + } + else + { + throw "Unexpected error in HMAC implementation"; + } + /* For all variants, the block size is bigger than the output + * size so there will never be a useful byte at the end of the + * string */ + keyToUse[lastArrayIndex] &= 0xFFFFFF00; + } + else if (blockByteSize > (keyBinLen / 8)) + { + /* If the blockByteSize is greater than the key length, there + * will always be at LEAST one "useless" byte at the end of the + * string */ + keyToUse[lastArrayIndex] &= 0xFFFFFF00; + } + + /* Create ipad and opad */ + for (i = 0; i <= lastArrayIndex; i += 1) + { + keyWithIPad[i] = keyToUse[i] ^ 0x36363636; + keyWithOPad[i] = keyToUse[i] ^ 0x5C5C5C5C; + } + + /* Calculate the HMAC */ + if (("SHA-1" === variant) && (1 & SUPPORTED_ALGS)) + { + retVal = coreSHA1( + keyWithOPad.concat( + coreSHA1( + keyWithIPad.concat(strToHash), + blockBitSize + strBinLen + ) + ), + blockBitSize + hashBitSize); + } + else if (6 & SUPPORTED_ALGS) + { + retVal = coreSHA2( + keyWithOPad.concat( + coreSHA2( + keyWithIPad.concat(strToHash), + blockBitSize + strBinLen, + variant + ) + ), + blockBitSize + hashBitSize, variant); + } + else + { + throw "Unexpected error in HMAC implementation"; + } + + return formatFunc(retVal, getOutputOpts(outputFormatOpts)); + }; + }; diff --git a/qml/lib/storage.js b/qml/lib/storage.js new file mode 100644 index 0000000..b6826ab --- /dev/null +++ b/qml/lib/storage.js @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2013, Stefan Brand + * 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. + */ + +.import QtQuick.LocalStorage 2.0 as LS + +// Get DB Connection +function getDB() { + return LS.LocalStorage.openDatabaseSync("harbour-sailotp", "1.0", "SailOTP Config Storage", 1000000); +} + +// Initialize Table if not exists +function initialize() { + var db = getDB(); + + db.transaction( + function(tx) { + tx.executeSql("CREATE TABLE IF NOT EXISTS OTPStorage(title TEXT, secret TEXT);"); + } + ) +} + +// Get all OTPs into the list model +function getOTP() { + var db = getDB(); + + db.transaction( + function(tx) { + var res = tx.executeSql("select * from OTPStorage;"); + for (var i=0; i < res.rows.length; i++) { + mainPage.appendOTP(res.rows.item(i).title, res.rows.item(i).secret); + } + } + ) +} + +// Add a new OTP +function addOTP(title, secret) { + var db = getDB(); + + db.transaction( + function(tx) { + tx.executeSql("INSERT INTO OTPStorage VALUES(?, ?);", [title, secret]); + } + ) +} + +// Remove an existing OTP +function removeOTP(title, secret) { + var db = getDB(); + + db.transaction( + function(tx) { + tx.executeSql("DELETE FROM OTPStorage WHERE title=? and secret=?;", [title, secret]); + } + ) +} diff --git a/qml/pages/About.qml b/qml/pages/About.qml new file mode 100644 index 0000000..e75d2f1 --- /dev/null +++ b/qml/pages/About.qml @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2013, Stefan Brand + * 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. + */ + +import QtQuick 2.0 +import Sailfish.Silica 1.0 + +Page { + id: aboutPage + + Image { + id: logo + source: "../sailotp.png" + anchors.horizontalCenter: parent.horizontalCenter + y: 200 + } + + Label { + id: name + anchors.horizontalCenter: parent.horizontalCenter + y: 320 + font.bold: true + text: "SailOTP 0.1" + } + Text { + id: desc + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: name.bottom + anchors.topMargin: 20 + text: "A Simple Sailfish TOTP Generator
(RFC 6238 compatible)" + color: "white" + } + Text { + id: copyright + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: desc.bottom + anchors.topMargin: 20 + text: "Copyright: Stefan Brand
License: BSD (3-clause)" + color: "white" + } + Button { + id: homepage + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: copyright.bottom + anchors.topMargin: 20 + text: "SailOTP on Github" + onClicked: { + Qt.openUrlExternally("https://github.com/seiichiro0185/sailotp") + } + } + Text { + id: accnowledgement + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: homepage.bottom + anchors.topMargin: 20 + text: "SailOTP uses the SHA-1 Implementation
from http://caligatio.github.io/jsSHA/" + color: "white" + } + +} diff --git a/qml/pages/AddOTP.qml b/qml/pages/AddOTP.qml new file mode 100644 index 0000000..3968b96 --- /dev/null +++ b/qml/pages/AddOTP.qml @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2013, Stefan Brand + * 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. + */ + + +import QtQuick 2.0 +import Sailfish.Silica 1.0 +import "../lib/storage.js" as DB + +Dialog { + id: addOTP + + property QtObject parentPage: null + + SilicaFlickable { + id: addOtpList + anchors.fill: parent + + VerticalScrollDecorator {} + + Column { + anchors.fill: parent + DialogHeader { + acceptText: "Add" + } + + TextField { + id: otpLabel + width: parent.width + label: "Title" + placeholderText: "Title for the OTP" + focus: true + horizontalAlignment: TextInput.AlignLeft + } + + TextField { + id: otpSecret + width: parent.width + label: "Secret" + placeholderText: "Secret OTP Key" + focus: true + horizontalAlignment: TextInput.AlignLeft + } + } + } + + onDone: { + if (otpLabel.text != "" && otpSecret.text != "") { + DB.addOTP(otpLabel.text, otpSecret.text); + parentPage.refreshOTPList(); + } + } + +} + + + + + diff --git a/qml/pages/MainView.qml b/qml/pages/MainView.qml new file mode 100644 index 0000000..9f7d3de --- /dev/null +++ b/qml/pages/MainView.qml @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2013, Stefan Brand + * 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. + */ + + +import QtQuick 2.0 +import Sailfish.Silica 1.0 +import "../lib/storage.js" as DB +import "../lib/crypto.js" as OTP + +Page { + id: mainPage + + ListModel { + id: otpListModel + } + + property double lastUpdated: null + + // Add an entry to the list + function appendOTP(title, secret) { + otpListModel.append({"secret": secret, "title": title, "otp": ""}); + } + + // Reload the List of OTPs from storage + function refreshOTPList() { + otpListModel.clear(); + DB.getOTP(); + refreshOTPValues(); + } + + // Calculate new OTPs for every entry + function refreshOTPValues() { + var curDate = new Date(); + var seconds = curDate.getSeconds(); + + for (var i=0; i 2000)) { + var curOTP = OTP.calcOTP(otpListModel.get(i).secret) + otpListModel.setProperty(i, "otp", curOTP); + console.log("Updating Value ", i); + } + } + + updateProgress.value = 29 - (seconds % 30) + lastUpdated = curDate.getTime(); + } + + Timer { + interval: 1000 + running: Qt.application.active + repeat: true + + onTriggered: refreshOTPValues(); + } + + SilicaFlickable { + anchors.fill: parent + + PullDownMenu { + MenuItem { + text: "About" + onClicked: pageStack.push(Qt.resolvedUrl("About.qml")) + } + MenuItem { + text: "Add OTP" + onClicked: pageStack.push(Qt.resolvedUrl("AddOTP.qml"), {parentPage: mainPage}) + } + } + + ProgressBar { + id: updateProgress + width: parent.width + maximumValue: 29 + anchors.top: parent.top + anchors.topMargin: 48 + } + + SilicaListView { + id: otpList + header: PageHeader { + title: "SailOTP" + } + anchors.fill: parent + model: otpListModel + width: parent.width + + ViewPlaceholder { + enabled: otpList.count == 0 + text: "Nothing here" + hintText: "Pull down to add a OTP" + } + + + + delegate: ListItem { + id: otpListItem + menu: otpContextMenu + width: otpList.width + contentHeight: Theme.itemSizeMedium + + function remove() { + remorseAction("Deleting", function() { DB.removeOTP(title, secret); otpListModel.remove(index) }) + } + + ListView.onRemove: animateRemoval() + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + + Label { + id: otpLabel + text: model.title + color: Theme.secondaryColor + anchors.horizontalCenter: parent.horizontalCenter + } + + Label { + id: otpValue + anchors.top: otpLabel.bottom + text: model.otp + anchors.horizontalCenter: parent.horizontalCenter + color: Theme.highlightColor + font.pixelSize: Theme.fontSizeLarge + } + } + + Component { + id: otpContextMenu + ContextMenu { + MenuItem { + text: "Delete" + onClicked: remove() + } + } + } + } + VerticalScrollDecorator{} + + Component.onCompleted: { + DB.initialize(); + refreshOTPList(); + } + } + } +} + + diff --git a/qml/sailotp.png b/qml/sailotp.png new file mode 100644 index 0000000..b3895eb Binary files /dev/null and b/qml/sailotp.png differ diff --git a/rpm/harbour-sailotp.spec b/rpm/harbour-sailotp.spec new file mode 100644 index 0000000..3e130a4 --- /dev/null +++ b/rpm/harbour-sailotp.spec @@ -0,0 +1,75 @@ +# +# Do NOT Edit the Auto-generated Part! +# Generated by: spectacle version 0.27 +# + +Name: harbour-sailotp + +# >> macros +# << macros + +%{!?qtc_qmake:%define qtc_qmake %qmake} +%{!?qtc_qmake5:%define qtc_qmake5 %qmake5} +%{!?qtc_make:%define qtc_make make} +%{?qtc_builddir:%define _builddir %qtc_builddir} +Summary: SailOTP +Version: 0.1 +Release: 1 +Group: Security +License: BSD +URL: https://github.com/seiichiro0185/sailotp/ +Source0: %{name}-%{version}.tar.bz2 +Source100: harbour-sailotp.yaml +Requires: sailfishsilica-qt5 >= 0.10.9 +BuildRequires: pkgconfig(sailfishapp) >= 0.0.10 +BuildRequires: pkgconfig(Qt5Core) +BuildRequires: pkgconfig(Qt5Qml) +BuildRequires: pkgconfig(Qt5Quick) +BuildRequires: desktop-file-utils + +%description +A Sailfish implementation of the Timebased One Time Pad algorithm as used by Google Authenticator and a growing number of Websites. + + +%prep +%setup -q -n %{name}-%{version} + +# >> setup +# << setup + +%build +# >> build pre +# << build pre + +%qtc_qmake5 + +%qtc_make %{?_smp_mflags} + +# >> build post +# << build post + +%install +rm -rf %{buildroot} +# >> install pre +# << install pre +%qmake5_install + +# >> install post +# << install post + +desktop-file-install --delete-original \ + --dir %{buildroot}%{_datadir}/applications \ + %{buildroot}%{_datadir}/applications/*.desktop + +%files +%defattr(-,root,root,-) +/usr/share/icons/hicolor/86x86/apps +/usr/share/applications +/usr/share/harbour-sailotp +/usr/bin +%{_datadir}/icons/hicolor/86x86/apps/%{name}.png +%{_datadir}/applications/%{name}.desktop +%{_datadir}/%{name}/qml +%{_bindir} +# >> files +# << files diff --git a/rpm/harbour-sailotp.yaml b/rpm/harbour-sailotp.yaml new file mode 100644 index 0000000..b905f0d --- /dev/null +++ b/rpm/harbour-sailotp.yaml @@ -0,0 +1,30 @@ +Name: harbour-sailotp +Summary: SailOTP +Version: 0.1 +Release: 1 +Group: Security +URL: https://github.com/seiichiro0185/sailotp/ +License: "BSD\t" +Sources: +- '%{name}-%{version}.tar.bz2' +Description: | + A Sailfish implementation of the Timebased One Time Pad algorithm as used by Google Authenticator and a growing number of Websites. +Configure: none +Builder: qtc5 +PkgConfigBR: +- sailfishapp >= 0.0.10 +- Qt5Core +- Qt5Qml +- Qt5Quick +Requires: +- sailfishsilica-qt5 >= 0.10.9 +Files: +- /usr/share/icons/hicolor/86x86/apps +- /usr/share/applications +- /usr/share/harbour-sailotp +- /usr/bin +- '%{_datadir}/icons/hicolor/86x86/apps/%{name}.png' +- '%{_datadir}/applications/%{name}.desktop' +- '%{_datadir}/%{name}/qml' +- '%{_bindir}' +PkgBR: [] diff --git a/src/harbour-sailotp.cpp b/src/harbour-sailotp.cpp new file mode 100644 index 0000000..0f897ec --- /dev/null +++ b/src/harbour-sailotp.cpp @@ -0,0 +1,42 @@ +/* + Copyright (C) 2013 Jolla Ltd. + Contact: Thomas Perl + All rights reserved. + + You may use this file under the terms of BSD license as follows: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of the Jolla Ltd nor the + names of its contributors may 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 HOLDERS 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. +*/ + +#ifdef QT_QML_DEBUG +#include +#endif + +#include + + +int main(int argc, char *argv[]) +{ + return SailfishApp::main(argc, argv); +} +