Skip to content

Instantly share code, notes, and snippets.

@matteobertozzi
Last active August 9, 2024 15:57
Show Gist options
  • Select an option

  • Save matteobertozzi/772b6f3620a6258ccd01987f7f34249a to your computer and use it in GitHub Desktop.

Select an option

Save matteobertozzi/772b6f3620a6258ccd01987f7f34249a to your computer and use it in GitHub Desktop.

Revisions

  1. matteobertozzi revised this gist Apr 23, 2023. No changes.
  2. matteobertozzi revised this gist Apr 23, 2023. 3 changed files with 0 additions and 0 deletions.
    File renamed without changes.
    File renamed without changes.
    File renamed without changes.
  3. matteobertozzi renamed this gist Apr 23, 2023. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  4. matteobertozzi created this gist Apr 23, 2023.
    63 changes: 63 additions & 0 deletions Otp.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,63 @@
    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);
    }
    }
    }
    22 changes: 22 additions & 0 deletions otp.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,22 @@
    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)
    30 changes: 30 additions & 0 deletions otp.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,30 @@
    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);
    }
    }