Last active
March 17, 2024 19:44
-
-
Save jonfriesen/234c7471c3e3199f97d5 to your computer and use it in GitHub Desktop.
TOTP Client for PowerShell
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
#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; | |
} |
@rey021 of course, glad it helps. Though, I'd expect there to be some C# TOTP libraries out in the world already :P
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
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
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. ❤️