Functions/Common/Get-TimeBasedOneTimePassword.ps1
<#
.SYNOPSIS Generate a Time-Base One-Time Password based on RFC 6238. .DESCRIPTION This command uses the reference implementation of RFC 6238 to calculate a Time-Base One-Time Password. It bases on the HMAC SHA-1 hash function to generate a shot living One-Time Password. .INPUTS None. .OUTPUTS System.String. The one time password. .EXAMPLE PS C:\> Get-TimeBasedOneTimePassword -SharedSecret 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' Get the Time-Based One-Time Password at the moment. .NOTES Author : Claudio Spizzi License : MIT License .LINK https://github.com/claudiospizzi/SecurityFever https://tools.ietf.org/html/rfc6238 #> function Get-TimeBasedOneTimePassword { [CmdletBinding()] [Alias('Get-TOTP')] param ( # Base 32 formatted shared secret (RFC 4648). [Parameter(Mandatory = $true)] [System.String] $SharedSecret, # The date and time for the target calculation, default is now (UTC). [Parameter(Mandatory = $false)] [System.DateTime] $Timestamp = (Get-Date).ToUniversalTime(), # Token length of the one-time password, default is 6 characters. [Parameter(Mandatory = $false)] [System.Int32] $Length = 6, # The hash method to calculate the TOTP, default is HMAC SHA-1. [Parameter(Mandatory = $false)] [System.Security.Cryptography.KeyedHashAlgorithm] $KeyedHashAlgorithm = (New-Object -TypeName 'System.Security.Cryptography.HMACSHA1'), # Baseline time to start counting the steps (T0), default is Unix epoch. [Parameter(Mandatory = $false)] [System.DateTime] $Baseline = '1970-01-01 00:00:00', # Interval for the steps in seconds (TI), default is 30 seconds. [Parameter(Mandatory = $false)] [System.Int32] $Interval = 30 ) # Generate the number of intervals between T0 and the timestamp (now) and # convert it to a byte array with the help of Int64 and the bit converter. $numberOfSeconds = ($Timestamp - $Baseline).TotalSeconds $numberOfIntervals = [Convert]::ToInt64([Math]::Floor($numberOfSeconds / $Interval)) $byteArrayInterval = [System.BitConverter]::GetBytes($numberOfIntervals) [Array]::Reverse($byteArrayInterval) # Use the shared secret as a key to convert the number of intervals to a # hash value. $KeyedHashAlgorithm.Key = Convert-Base32ToByte -Base32 $SharedSecret $hash = $KeyedHashAlgorithm.ComputeHash($byteArrayInterval) # Calculate offset, binary and otp according to RFC 6238 page 13. $offset = $hash[($hash.Length-1)] -band 0xf $binary = (($hash[$offset + 0] -band '0x7f') -shl 24) -bor (($hash[$offset + 1] -band '0xff') -shl 16) -bor (($hash[$offset + 2] -band '0xff') -shl 8) -bor (($hash[$offset + 3] -band '0xff')) $otpInt = $binary % ([Math]::Pow(10, $Length)) $otpStr = $otpInt.ToString().PadLeft($Length, '0') Write-Output $otpStr } |