Skip to content

Instantly share code, notes, and snippets.

@matteobertozzi
Last active June 5, 2024 19:04
Show Gist options
  • Save matteobertozzi/772b6f3620a6258ccd01987f7f34249a to your computer and use it in GitHub Desktop.
Save matteobertozzi/772b6f3620a6258ccd01987f7f34249a to your computer and use it in GitHub Desktop.
Generate Time Based OTP in Javascript/Typescript, Python, Java
async function generateOneTimePassword(rawKey: Uint8Array, counter: number): Promise<number> {
const data = new DataView(new ArrayBuffer(8));
data.setBigUint64(0, BigInt(Math.floor(counter)), false);
const algo = { name: 'HMAC', hash: 'SHA-1' };
const key = await crypto.subtle.importKey('raw', rawKey, algo, false, ['sign']);
const hmacHash = new Uint8Array(await crypto.subtle.sign(algo, key, data.buffer));
const offset = hmacHash[hmacHash.byteLength - 1] & 0x0f;
const hotp = (hmacHash[offset] & 0x7f) << 24
| (hmacHash[1 + offset] & 0xff) << 16
| (hmacHash[2 + offset] & 0xff) << 8
| (hmacHash[3 + offset] & 0xff);
return hotp % 1_000_000;
}
async function generateTimeBasedOneTimePassword(rawKey: Uint8Array): Promise<number> {
const interval = (Date.now() / 1000) / 30; // 30sec window
return generateOneTimePassword(rawKey, interval);
}
async function demo() {
const sleep = time => new Promise(res => setTimeout(res, time));
const secret = new TextEncoder().encode('TestKeYWow');
while (true) {
console.log(await generateTimeBasedOneTimePassword(secret));
await sleep(1000);
}
}
import struct, hashlib, hmac, time
def generateOneTimePassword(key, counter):
data = struct.pack('>Q', int(counter))
hmacHash = hmac.HMAC(key, data, digestmod=hashlib.sha1).digest()
offset = hmacHash[len(hmacHash) - 1] & 0x0f
hotp = (hmacHash[offset] & 0x7f) << 24 \
| (hmacHash[1 + offset] & 0xff) << 16 \
| (hmacHash[2 + offset] & 0xff) << 8 \
| (hmacHash[3 + offset] & 0xff);
return hotp % 1_000_000
def generateTimeBasedOneTimePassword(key):
interval = time.time() / 30 # 30sec window
return generateOneTimePassword(key, interval)
if __name__ == '__main__':
key = 'TestKeYWow'.encode()
for i in range(260):
print(i, generateTimeBasedOneTimePassword(key))
time.sleep(1)
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class Otp {
public static int generateGoogleOneTimePassword(final byte[] key, final long counter)
throws NoSuchAlgorithmException, InvalidKeyException {
final Mac hmac = Mac.getInstance("HmacSHA1");
hmac.init(new SecretKeySpec(key, "HmacSHA1"));
final ByteBuffer data = ByteBuffer.allocate(8);
data.putLong(counter);
final byte[] hmacHash = hmac.doFinal(data.array());
final int offset = hmacHash[hmacHash.length - 1] & 0x0f;
final int hotp = (hmacHash[offset] & 0x7f) << 24
| (hmacHash[1 + offset] & 0xff) << 16
| (hmacHash[2 + offset] & 0xff) << 8
| (hmacHash[3 + offset] & 0xff);
return hotp % 1_000_000;
}
public static int generateGoogleTimeBasedOneTimePassword(final byte[] key)
throws InvalidKeyException, NoSuchAlgorithmException {
final long interval = (System.currentTimeMillis() / 1000) / 30; // 30sec window
return generateGoogleOneTimePassword(key, interval);
}
private static final char[] BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".toCharArray();
public static String encode32(final byte[] buf) {
final StringBuilder result = new StringBuilder();
int vBits = 0;
int value = 0;
for (int i = 0; i < buf.length; ++i) {
value = (value << 8) | buf[i] & 0xff;
vBits += 8;
while (vBits >= 5) {
result.append(BASE32_ALPHABET[(value >>> (vBits - 5)) & 31]);
vBits -= 5;
}
}
if (vBits > 0) {
result.append(BASE32_ALPHABET[(value << (5 - vBits)) & 31]);
}
return result.toString();
}
public static void main(final String[] args) throws Exception {
final byte[] key = { 'T', 'e', 's', 't', 'K', 'e', 'Y', 'W', 'o', 'w' };
System.out.println(encode32(key));
while (true) {
System.out.println(generateGoogleTimeBasedOneTimePassword(key));
Thread.sleep(1000);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment