Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f408f8e
commit ab0f7fa
Showing
6 changed files
with
155 additions
and
107 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/* Copyright (c) 2020 Dominik Enzinger. See the file LICENSE for copying permission. */ | ||
const HMAC = require('hmac'); | ||
const base32 = require('base32'); | ||
|
||
/// For internal use - generates the one time password | ||
function generateOtp(hmac, digits) { | ||
"compiled"; | ||
const o = hmac[hmac.byteLength - 1] & 0x0F; | ||
const dt = ((hmac[o] & 0x7f) << 24) | (hmac[o + 1] << 16) | (hmac[o + 2] << 8) | hmac[o + 3]; | ||
let result = '' + dt % Math.pow(10, digits); | ||
while ( result.length < digits ) { | ||
result = '0' + result; | ||
} | ||
return result; | ||
} | ||
|
||
function TOTP(secret) { | ||
this.hmac = new HMAC.FixedSHA1(base32.decode(secret), 8); | ||
this.message = new Uint8Array(8); | ||
}; | ||
|
||
/// Generate a TOTP with the given timestamp, period, and number of digits. | ||
/// For example totp.generate(getTime(), 6/*digits*, 30/*seconds*/) | ||
TOTP.prototype.generate = function(timestamp, digits, tokenPeriod) { | ||
const epoch = Math.floor(timestamp / tokenPeriod); | ||
this.message.set([epoch >> 24 & 0xFF, epoch >> 16 & 0xFF, epoch >> 8 & 0xFF, epoch & 0xFF], 4); | ||
const hmac = this.hmac.digest(this.message.buffer); | ||
return generateOtp(hmac, digits); | ||
}; | ||
|
||
/// Create a TOTP generator with a secret code in base32 | ||
exports.create = function(secret) { | ||
return new TOTP(secret); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<!--- Copyright (c) 2020 Gordon Williams. See the file LICENSE for copying permission. --> | ||
Time-based One-time Password | ||
============================ | ||
|
||
<span style="color:red">:warning: **Please view the correctly rendered version of this page at https://www.espruino.com/TOTP. Links, lists, videos, search, and other features will not work correctly when viewed on GitHub** :warning:</span> | ||
|
||
* KEYWORDS: Module,Crypto,TOTP,Time-based Password,OTP,One time password | ||
|
||
This [[TOTP.js]] module implements a Time-based One-time Password for Espruino. | ||
|
||
|
||
How to use the module: | ||
|
||
``` | ||
const TOTP = require('totp'); | ||
const totp = TOTP.create('JBSWY3DPEHPK3PXP'); | ||
// 6 digits, period of 30 seconds | ||
console.log(totp.generate(getTime(), 6, 30)); | ||
``` | ||
|
||
Reference | ||
-------------- | ||
|
||
* APPEND_JSDOC: TOTP.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
/* Copyright (c) 2020 Dominik Enzinger. See the file LICENSE for copying permission. */ | ||
const BASE32_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; | ||
|
||
/// Decode the supplied encoded base 32 string into an ArrayBuffer | ||
exports.decode = function(encoded) { | ||
encoded = encoded.toUpperCase(); | ||
const result = new Uint8Array(Math.floor(encoded.length * 5 / 8)); | ||
let buffer = 0, | ||
next = 0, | ||
bitsLeft = 0, | ||
charValue = 0; | ||
for ( let i in encoded ) { | ||
charValue = BASE32_CHARS.indexOf(encoded.charAt(i)); | ||
buffer = (buffer << 5) | charValue & 31; | ||
bitsLeft += 5; | ||
if (bitsLeft >= 8) { | ||
bitsLeft -= 8; | ||
result[next++] = (buffer >> bitsLeft) & 0xFF; | ||
} | ||
} | ||
return result.buffer; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<!--- Copyright (c) 2020 Gordon Williams. See the file LICENSE for copying permission. --> | ||
Base 32 Decoder | ||
================= | ||
|
||
<span style="color:red">:warning: **Please view the correctly rendered version of this page at https://www.espruino.com/base32. Links, lists, videos, search, and other features will not work correctly when viewed on GitHub** :warning:</span> | ||
|
||
* KEYWORDS: Module,Base32 | ||
|
||
This [[base32.js]] module implements a Base 32 decoder. | ||
|
||
How to use the module: | ||
|
||
``` | ||
var decoded = require('base32').decode('JBSWY3DPEHPK3PXP'); | ||
// decoded is now an ArrayBuffer | ||
``` | ||
|
||
Reference | ||
-------------- | ||
|
||
* APPEND_JSDOC: base32.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,101 +1,50 @@ | ||
/* Copyright (c) 2015 Mikael Ganehag Brorsson. See the file LICENSE for copying permission. */ | ||
/* | ||
Small module to add HMAC support. Depends on 'hashlib'. | ||
*/ | ||
|
||
function hmac(key, message, digestmod) { | ||
if (digestmod == null) { | ||
digestmod = hashlib.sha256; | ||
} | ||
|
||
this.finished = false; | ||
this.inner = digestmod(); | ||
this.outer = digestmod(); | ||
|
||
var i, pad = new Uint8Array(this.inner.block_size); | ||
|
||
if (key.length > this.inner.block_size) { | ||
var h = (new digestmod()).update(k), | ||
ah = E.toBufferArray(h.digest()); | ||
|
||
for(i = 0; i < ah.length; i++) { | ||
pad[i] = ah[i].charCodeAt(0); | ||
} | ||
} | ||
else { | ||
for (i = 0; i < key.length; i++) { | ||
pad[i] = key[i].charCodeAt(0); | ||
} | ||
} | ||
|
||
for (i = 0; i < pad.length; i++) { | ||
pad[i] ^= 0x36; | ||
} | ||
this.inner.update(String.fromCharCode.apply(null, pad)); | ||
|
||
for (i = 0; i < pad.length; i++) { | ||
pad[i] ^= 0x36 ^ 0x5c; | ||
} | ||
this.outer.update(String.fromCharCode.apply(null, pad)); | ||
|
||
for (i = 0; i < pad.length; i++) { | ||
pad[i] = 0; | ||
} | ||
/* Copyright (c) 2020 Dominik Enzinger. See the file LICENSE for copying permission. */ | ||
function bitwiseOr0x36(b) {"compiled"; return b ^ 0x36; } | ||
|
||
function bitwiseOr0x5c(b) {"compiled"; return b ^ 0x5c; } | ||
|
||
/// Returns an MAC instance using the given hash. Eg. HMAC(key, require('crypto').SHA1, 64, 20) | ||
exports.HMAC = function(key, hash, blockSize, outputSize) { | ||
if ( key.byteLength > blockSize ) | ||
key = hash(key); | ||
this.hash = hash; | ||
this.keyLength = Math.max(blockSize, key.byteLength); | ||
key = new Uint8Array(key, 0, this.keyLength); | ||
this.oBuf = new Uint8Array(this.keyLength + outputSize); | ||
this.oBuf.set(key.map(bitwiseOr0x5c).buffer, 0); | ||
this.iKeyPad = key.map(bitwiseOr0x36).buffer; | ||
}; | ||
|
||
if(message) { | ||
this.inner.update(message); | ||
} | ||
} | ||
|
||
hmac.prototype.update = function(m) { | ||
if(m) { | ||
this.finished = false; | ||
this.inner.update(m); | ||
} | ||
return this; | ||
/// Take a message as an arraybuffer or string, return an arraybuffer | ||
exports.HMAC.prototype.digest = function(message) { | ||
const iBuf = new Uint8Array(this.keyLength + message.byteLength); | ||
iBuf.set(this.iKeyPad, 0); | ||
iBuf.set(message, this.keyLength); | ||
this.oBuf.set(this.hash(iBuf), this.keyLength); | ||
return this.hash(this.oBuf); | ||
}; | ||
|
||
hmac.prototype.digest = function() { | ||
if(!this.finished) { | ||
this.outer.update(this.inner.digest()) | ||
this.finished = true; | ||
} | ||
return this.outer.digest(); | ||
function FixedHMAC(key, messageSize, hash, blockSize, outputSize) { | ||
exports.HMAC.call(this, key, hash, blockSize, outputSize); | ||
this.iBuf = new Uint8Array(this.keyLength + messageSize); | ||
this.iBuf.set(this.iKeyPad, 0); | ||
delete this.ikeyPad; | ||
}; | ||
|
||
hmac.prototype.hexdigest = function() { | ||
var i, v, s = "", h = this.digest(); | ||
for(i = 0; i < h.length; i++) | ||
s += (256+h.charCodeAt(i)).toString(16).substr(-2); | ||
return s; | ||
/// Take a message as an arraybuffer or string, return an arraybuffer | ||
FixedHMAC.prototype.digest = function(message) { | ||
this.iBuf.set(message, this.keyLength); | ||
this.oBuf.set(this.hash(this.iBuf), this.keyLength); | ||
return this.hash(this.oBuf); | ||
}; | ||
|
||
exports.create = function(key, message, digestmod) { | ||
return new hmac(key, message, digestmod); | ||
} | ||
|
||
/** | ||
compare_digest(a, b) -> bool | ||
Return 'a == b'. This function uses an approach designed to prevent | ||
timing analysis, making it appropriate for cryptography. | ||
a and b must both be of the same type. | ||
Note: If a and b are of different lengths, or if an error occurs, | ||
a timing attack could theoretically reveal information about the | ||
types and lengths of a and b--but not their values. | ||
*/ | ||
exports.compare_digest = function(a, b) { | ||
var match, i; | ||
|
||
if(a.length != b.length) { | ||
return false; | ||
} | ||
|
||
match = 0; | ||
for(i = 0; i < a.length; i++) { | ||
match |= a.charCodeAt(i) ^ b.charCodeAt(i); | ||
} | ||
/// Create a basic HMAC using SHA1 | ||
exports.SHA1 = function(key) { | ||
return new exports.HMAC(key, require('crypto').SHA1, 64, 20); | ||
}; | ||
|
||
return match == 0; | ||
/// FixedSHA1 is faster than SHA1, but digested message must always be the same fixed length. | ||
exports.FixedSHA1 = function(key, messageSize) { | ||
return new FixedHMAC(key, messageSize, require('crypto').SHA1, 64, 20); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,30 @@ | ||
<!--- Copyright (c) 2014 Mikael Ganehag Brorsson. See the file LICENSE for copying permission. --> | ||
<!--- Copyright (c) 2020 Gordon Williams. See the file LICENSE for copying permission. --> | ||
HMAC Module | ||
=========== | ||
|
||
<span style="color:red">:warning: **Please view the correctly rendered version of this page at https://www.espruino.com/hmac. Links, lists, videos, search, and other features will not work correctly when viewed on GitHub** :warning:</span> | ||
|
||
* KEYWORDS: Module,HMAC,Hashlib,Crypto | ||
|
||
This [[hmac.js]] module implements a HMAC for Espruino. It depends on the inclusion of hashlib to compute the checksums. | ||
This [[hmac.js]] module implements a HMAC for Espruino. | ||
|
||
**Note:** There was an older `hmac` module that depended on `hashlib` (a built-in module | ||
that is no longer included in Espruino). This new `hmac` has a different API | ||
that uses the [`crypto` library](http://www.espruino.com/Reference#crypto). | ||
|
||
How to use the module: | ||
|
||
``` | ||
var hmac = require("hmac"); | ||
var hashlib = require("hashlib"); | ||
var foo = hmac.create("secret", "message", hashlib.sha256); | ||
var bar = hmac.create("secret", "another message", hashlib.sha256); | ||
foo.digest() // raw digest | ||
foo.hexdigest() // hex encoded digest | ||
foo.update('more message') // used to iterate parts of a message | ||
// This function uses an approach designed to prevent timing analysis, | ||
// making it appropriate for cryptography. | ||
hmac.compare_digest(foo.digest(), bar.digest()) | ||
const HMAC = require('hmac'); | ||
var hmac = HMAC.SHA1(E.toArrayBuffer('my secret key')); | ||
console.log(hmac.digest(E.toArrayBuffer('my message'))); | ||
// FixedSHA1 is faster than SHA1, but digested message must always be the same fixed length. | ||
var hmacf = HMAC.FixedSHA1(E.toArrayBuffer('my secret key'), 2); // 2 bytes | ||
console.log(hmacf.digest(E.toArrayBuffer('bb'))); | ||
console.log(hmacf.digest(E.toArrayBuffer('xx'))); | ||
``` | ||
|
||
Reference | ||
-------------- | ||
|
||
* APPEND_JSDOC: hmac.js |