Skip to content

Instantly share code, notes, and snippets.

@jonfriesen
Last active March 17, 2024 19:44
Show Gist options
  • Star 34 You must be signed in to star a gist
  • Fork 10 You must be signed in to fork a gist
  • Save jonfriesen/234c7471c3e3199f97d5 to your computer and use it in GitHub Desktop.
Save jonfriesen/234c7471c3e3199f97d5 to your computer and use it in GitHub Desktop.
TOTP Client for PowerShell
#requires -version 2
<#
.SYNOPSIS
Time-base One-Time Password Algorithm (RFC 6238)
.DESCRIPTION
This is an implementation of the RFC 6238 Time-Based One-Time Password Algorithm draft based upon the HMAC-based One-Time Password (HOTP) algorithm (RFC 4226). This is a time based variant of the HOTP algorithm providing short-lived OTP values.
.NOTES
Version: 1.0
Author: Jon Friesen
Creation Date: May 7, 2015
Purpose/Change: Provide an easy way of generating OTPs
#>
function Get-Otp($SECRET, $LENGTH, $WINDOW){
$enc = [System.Text.Encoding]::UTF8
$hmac = New-Object -TypeName System.Security.Cryptography.HMACSHA1
$hmac.key = Convert-HexToByteArray(Convert-Base32ToHex(($SECRET.ToUpper())))
$timeBytes = Get-TimeByteArray $WINDOW
$randHash = $hmac.ComputeHash($timeBytes)
$offset = $randhash[($randHash.Length-1)] -band 0xf
$fullOTP = ($randhash[$offset] -band 0x7f) * [math]::pow(2, 24)
$fullOTP += ($randHash[$offset + 1] -band 0xff) * [math]::pow(2, 16)
$fullOTP += ($randHash[$offset + 2] -band 0xff) * [math]::pow(2, 8)
$fullOTP += ($randHash[$offset + 3] -band 0xff)
$modNumber = [math]::pow(10, $LENGTH)
$otp = $fullOTP % $modNumber
$otp = $otp.ToString("0" * $LENGTH)
return $otp
}
function Get-TimeByteArray($WINDOW) {
$span = (New-TimeSpan -Start (Get-Date -Year 1970 -Month 1 -Day 1 -Hour 0 -Minute 0 -Second 0) -End (Get-Date).ToUniversalTime()).TotalSeconds
$unixTime = [Convert]::ToInt64([Math]::Floor($span/$WINDOW))
$byteArray = [BitConverter]::GetBytes($unixTime)
[array]::Reverse($byteArray)
return $byteArray
}
function Convert-HexToByteArray($hexString) {
$byteArray = $hexString -replace '^0x', '' -split "(?<=\G\w{2})(?=\w{2})" | %{ [Convert]::ToByte( $_, 16 ) }
return $byteArray
}
function Convert-Base32ToHex($base32) {
$base32chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
$bits = "";
$hex = "";
for ($i = 0; $i -lt $base32.Length; $i++) {
$val = $base32chars.IndexOf($base32.Chars($i));
$binary = [Convert]::ToString($val, 2)
$staticLen = 5
$padder = '0'
# Write-Host $binary
$bits += Add-LeftPad $binary.ToString() $staticLen $padder
}
for ($i = 0; $i+4 -le $bits.Length; $i+=4) {
$chunk = $bits.Substring($i, 4)
# Write-Host $chunk
$intChunk = [Convert]::ToInt32($chunk, 2)
$hexChunk = Convert-IntToHex($intChunk)
# Write-Host $hexChunk
$hex = $hex + $hexChunk
}
return $hex;
}
function Convert-IntToHex([int]$num) {
return ('{0:x}' -f $num)
}
function Add-LeftPad($str, $len, $pad) {
if(($len + 1) -ge $str.Length) {
while (($len - 1) -ge $str.Length) {
$str = ($pad + $str)
}
}
return $str;
}
@DaDDyICEPOC
Copy link

Hi,

I have a token setup in Authy and I would like to use that one with this script. Is this possible?

Many Thanks
POC

@ecspresso
Copy link

I have a token setup in Authy and I would like to use that one with this script. Is this possible?

@DaDDyICEPOC If you can get the token as a string, yes.

@jamez2128
Copy link

OMG thank you for this! Mind if I... you know 👉👈?

@jonfriesen
Copy link
Author

@jamez2128 👍

@rey021
Copy link

rey021 commented Feb 26, 2024

Magnificient mind @jonfriesen, you've blown me away with this writing.
Would you allow me if i follow your logic and will implement it in C#? Credits still be yours. ❤️

@jonfriesen
Copy link
Author

@rey021 of course, glad it helps. Though, I'd expect there to be some C# TOTP libraries out in the world already :P

@AlecMcCutcheon
Copy link

doesn't the secret need to be in base 32? Hoping to generate a QR code to use with authenticator, but that requires a base 32 secret

@AlecMcCutcheon
Copy link

NVM, Yes if you use base 32 string, and use it to make a QR code it will match the script output <3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment