Initial commit of stable v1.0
This commit is contained in:
245
README.md
245
README.md
@@ -1,3 +1,244 @@
|
||||
# zotp-bash
|
||||
# zOTP Bash - Generates/Tests One Time Pins in Bash Scripts
|
||||
|
||||
zOTP Bash - Generates/Tests One Time Pins in Bash Scripts
|
||||
- Current Version : 1.0
|
||||
- Author : Ze'ev Schurmann
|
||||
- Git Repo : https://git.3volve.net.za/thisiszeev/zotp-bash
|
||||
- Reddit : u/thisiszeev
|
||||
- License : GPL3
|
||||
|
||||
One Time Pins generated using newOTP are unique to the minute. So from minute to minute the OTPs will be different. When using testOTP to verify an OTP, you can assign a validity time in minutes from 1 minute to 120 minutes.
|
||||
|
||||
> You can demo zOTP Bash with the two files **demo-makeotp.sh** and **demo-verifyotp.sh**. These two files will create a time based OTP that is unique to a user and verify the same OTP against the same user. With the demo scripts the OTP is valid for 10 minutes. Run **demo-makeotp.sh** and give it a string unique to a hypothetical user such as an email address. Example `./demo-makeotp.sh "user@domain.tld"`. It will output an OTP as well as the expiry time of the OTP. Then you can run **demo-verifyotp.sh** and again give it the same string unique to the hypothetical user as well as the OTP. Example `./demoverify.sh "user@domain.tld" 123456`. Try different OTPs and different strings and see how it works. After the expiry time has passed and it enters into the next minute, the correct OTP and string will result in the output saying the OTP is invalid or expired.
|
||||
|
||||
Either copy and paste the functions to your project or use `source /path/to/zotp.inc.sh` above your main code. If you copy and paste the functions in your project, please also include the commented out text at the top of the file zotp.inc.za.
|
||||
|
||||
Don't forget to declare "thesalt" and "theseed" in your code before you call the functions newOTP and testOTP.
|
||||
|
||||
**"thesalt"** must be a string unique to your project or to each instance of the project. This avoids other projects getting the same OTP at the same time.
|
||||
|
||||
```
|
||||
declare thesalt="Name of project or service"
|
||||
```
|
||||
|
||||
*Only needs to be declared once, ideally at the start of your code*
|
||||
|
||||
|
||||
**"theseed"** must be a string unique to the user the OTP is for. This avoids other users getting the same OTP at the same time.
|
||||
|
||||
```
|
||||
declare theseed="user@domain.tld"
|
||||
```
|
||||
|
||||
*Needs to be declared each time the current user changes*
|
||||
|
||||
I do plan to port these functions to Perl, Python3 and PHP. I will make sure the OTPs created in one language can be verified in another. If there is demand for other languages then I am willing to put it on my to-do list. Send me a chat request on Reddit. Or maybe you keen to do a port to another language, send me a chat request and we can chat. I will link your port in the Git Repo for this project.
|
||||
|
||||
## function genHash
|
||||
|
||||
**genHash** will calculate a hexidecimal hash string from a provided sting of any length or format using one of several Hashing Algorithms. Outputs the hash as a string of hexadecimal digits.
|
||||
|
||||
> Used by the functions newOTP and testOTP.
|
||||
|
||||
**Usage:** `genHash "{input-string}" {hash}`
|
||||
|
||||
```
|
||||
{input-string} - A string of any kind. Supports ESCAPE codes such as \n \t \r \$ etc. [REQUIRED - MUST BE ENCLOSED IN SINGLE OR DOULBE QUOTES]
|
||||
{hash-algorithm} - The hashing algorithm used for generating the hexadecimal hash string [OPTIONAL - Defaults to MD5 if omitted]
|
||||
```
|
||||
|
||||
> The above must be given in the order of input-string first and optional hash-algorithm after. The input-string is case sensitive but the hash-algorithm is not case sensitive
|
||||
|
||||
**Example - Calculate a Blake2 Hash using a short string if text:**
|
||||
|
||||
```
|
||||
variablename="$(genHash "This is a short string of text." b2)"
|
||||
```
|
||||
|
||||
> See the file genhash-singleline.sh to see it in action
|
||||
|
||||
**Example - Calculate a SHA512 Hash using a multiline string of text:**
|
||||
|
||||
```
|
||||
variablename="$(genHash "
|
||||
This is the first line of text.
|
||||
This is the second line of text.
|
||||
This is the third line of text.
|
||||
" sha512)"
|
||||
```
|
||||
|
||||
> See the file genhash-multiline.sh to see it in action
|
||||
|
||||
**Supported Hashing Algorithms:**
|
||||
|
||||
```
|
||||
b2 - Blake2 (up to 512bit) - 128 digits - Very High Entropy - Fast
|
||||
md5 - Message Digest Algorithm 5 (128bit) - 32 digits - Very Low Entropy - Fast
|
||||
sha - Secure Hash Algorithm (160bit) - 40 digits - Low Entropy - Fast
|
||||
sha1 - Secure Hash Algorithm (160bit) - 40 digits - Low Entropy - Fast
|
||||
sha224 - Secure Hash Algorithm (224bit) - 56 digits - Medium Entropy - Slow
|
||||
sha256 - Secure Hash Algorithm (256bit) - 64 digits - High Entropy - Slow
|
||||
sha384 - Secure Hash Algorithm (384bit) - 96 digits - Very High Entropy - Medium
|
||||
sha512 - Secure Hash Algorithm (512bit) - 128 digits - Very High Entropy - Medium
|
||||
```
|
||||
|
||||
> **sha** and **sha1** are the same algorithm, but I've included both variants as some systems execute it as shasum and others as sha1sum. Not all of these algorithms are available in Bash on all systems. Please test first. The function will output an error message to the terminal and give an exit code 3 if the chosen algorithm is not found on your system.
|
||||
|
||||
**Exit Codes:**
|
||||
|
||||
```
|
||||
0 - Hash successfully calculated
|
||||
11 - Function encounted an error and could not calculate the Hash (No data given)
|
||||
23 - Function encounted an error and could not calculate the Hash (Invalid hashing algorithm given)
|
||||
29 - Function encounted an error and could not calculate the Hash (Hashing Algorithm not found)
|
||||
```
|
||||
|
||||
## function genOTP
|
||||
|
||||
**genOTP** will calculate an OTP using 1 to 3 hexadecimal hash strings of 32 digits or more. Outputs the OTP as text digits.
|
||||
|
||||
> Used by the functions newOTP and testOTP.
|
||||
|
||||
**Usage:** `genOTP {hash-one} {hash-two} {hash-three} {number-of-digits}`
|
||||
|
||||
```
|
||||
{hash-one} - A hexadecimal hash string of 32 digits or more [REQUIRED]
|
||||
{hash-two} - A hexadecimal hash string of 32 digits or more [OPTIONAL - Defaults to a string of zeros equal in length to hash-one if omitted]
|
||||
{hash-three} - A hexadecimal hash string of 32 digits or more [OPTIONAL - Defaults to a string of zeros equal in length to hash-one o hash-two, which ever is shorter, if omitted]
|
||||
{number-of-digits} - An integer from 4 to 16 [OPTIONAL - Defaults to 6 if omitted]
|
||||
```
|
||||
|
||||
> The above can be given in any order and is not case sensitive**
|
||||
|
||||
**Example - Calculate a custom 5 digit OTP using a single hash:**
|
||||
|
||||
```
|
||||
variablename="$(genOTP d03d5fc2f2b73c917cee8d0bc1153b5affe4a243 5)"
|
||||
```
|
||||
|
||||
> See the file genotp-onehash.sh to see it in action
|
||||
|
||||
**Example - Calculate a custom 8 digit OTP using three hashes:**
|
||||
|
||||
```
|
||||
variablename="$(genOTP 09bf8c7563e7d577e07cca414b37c572a5e1676d 7540acafd1bb0ce0383e38859379e2c062cac45c 316fa3932ec66d9778be4df6132b4544d46ab146 8)"
|
||||
```
|
||||
|
||||
> See the file genotp-threehash.sh to see it in action
|
||||
|
||||
**Exit Codes:**
|
||||
|
||||
```
|
||||
0 - OTP successfully calculated
|
||||
11 - Function encounted an error and could not calculate OTP (No data given)
|
||||
12 - Function encounted an error and could not calculate OTP (Too much data given)
|
||||
13 - Function encounted an error and could not calculate OTP (Invalid data given)
|
||||
72 - Function encounted an error and could not calculate OTP (Too many OTP size integers given)
|
||||
81 - Function encounted an error and could not calculate OTP (No hexadecimal hash strings given)
|
||||
82 - Function encounted an error and could not calculate OTP (Too many hexadecimal hash strings given)
|
||||
84 - Function encounted an error and could not calculate OTP (Given hexadecimal hash string is too short)
|
||||
```
|
||||
|
||||
## function newOTP
|
||||
|
||||
newOTP will create a new OTP based on the current time (to the minute). Outputs the OTP as text digits.
|
||||
|
||||
**Usage:** `newOTP {number-of-digits} {hash-algorithm}`
|
||||
|
||||
```
|
||||
{number-of-digits} - An integer from 4 to 16 [OPTIONAL - Defaults to 6 if omitted]
|
||||
{hash-algorithm} - The hashing algorithm used for generating OTPs [OPTIONAL - Defaults to MD5 if omitted]
|
||||
See comments provided in the function genHash
|
||||
```
|
||||
|
||||
> The above can be given in any order and is not case sensitive
|
||||
|
||||
**Example - Create a new 7 digit OTP using Default Hashing Agorithm:**
|
||||
|
||||
```
|
||||
declare thesalt="Name of project or service"
|
||||
declare theseed="user@domain.tld"
|
||||
variablename="$(newOTP 7)"
|
||||
```
|
||||
|
||||
> See the file newotp-default.sh to see it in action
|
||||
|
||||
**Example - Create a new 4 digit OTP using SHA384:**
|
||||
|
||||
```
|
||||
declare thesalt="Name of project or service"
|
||||
declare theseed="user@domain.tld"
|
||||
variablename="$(newOTP 4 sha384)"
|
||||
```
|
||||
|
||||
> See the file newotp-sha384.sh to see it in action
|
||||
|
||||
**Exit Codes:**
|
||||
|
||||
```
|
||||
0 - OTP successfully created
|
||||
12 - Function encounted an error and could not create OTP (Too much data given)
|
||||
13 - Function encounted an error and could not create OTP (Invalid data given)
|
||||
22 - Function encounted an error and could not create OTP (Too many hash algorithm strings given)
|
||||
72 - Function encounted an error and could not create OTP (Too many OTP size integers given)
|
||||
```
|
||||
|
||||
## function testOTP
|
||||
|
||||
testOTP will verify if an OTP is still valid and correct. Outputs the text "VALID" (exit code 0) or "INVALID" (exit code 255).
|
||||
|
||||
**Usage:** `testOTP {OTP} {valid-minutes} {hash-algorithm} {quiet}`
|
||||
|
||||
```
|
||||
{OTP} - A 4 to 16 digit OTP to be verified [REQUIRED]
|
||||
{valid-minutes} - The time in minutes (from 1 to 120) the OTP is valid for [OPTIONAL - Defaults to 30 minutes if omitted]
|
||||
{hash-algorithm} - The hashing algorithm used for generating OTPs [OPTIONAL - Defaults to MD5 if omitted]
|
||||
See comments provided in the function genHash
|
||||
{quiet} - Disables text output when verifying allowing you to rely on exit codes instead. [OPTIONAL]
|
||||
You can use just the letter "q" as an alternative to "quiet"
|
||||
```
|
||||
|
||||
> The above can be given in any order and is not case sensitive
|
||||
|
||||
**Example - Using text output for validation over 15 minutes**
|
||||
|
||||
```
|
||||
declare thesalt="Name of project or service"
|
||||
declare theseed="user@domain.tld"
|
||||
if [[ "$(testOTP 123456 15)" == "VALID" ]]; then
|
||||
echo "OTP is valid..."
|
||||
else
|
||||
echo "OTP is invalid or expired..."
|
||||
fi
|
||||
```
|
||||
|
||||
> See the file testotp-textoutput.sh to see it in action
|
||||
|
||||
**Example - Using exit code for validation over 45 minutes**
|
||||
|
||||
```
|
||||
declare thesalt="Name of project or service"
|
||||
declare theseed="user@domain.tld"
|
||||
if testOTP 123456 45 q; then
|
||||
echo "OTP is valid..."
|
||||
else
|
||||
echo "OTP is invalid or expired..."
|
||||
fi
|
||||
```
|
||||
|
||||
> See the file testotp-exitcode.sh to see it in action
|
||||
|
||||
**Exit Codes:**
|
||||
|
||||
```
|
||||
0 - OTP is VALID
|
||||
11 - Function encounted an error and could not verify OTP (No data given)
|
||||
12 - Function encounted an error and could not verify OTP (Too much data given)
|
||||
13 - Function encounted an error and could not verify OTP (Invalid data given)
|
||||
33 - Function encounted an error and could not verify OTP (Too many hash algorithm strings given)
|
||||
41 - Function encounted an error and could not verify OTP (No OTP given)
|
||||
43 - Function encounted an error and could not verify OTP (Too many OTPs given)
|
||||
53 - Function encounted an error and could not verify OTP (Too many minute integers given)
|
||||
55 - Function encounted an error and could not verify OTP (Minute integer given to high)
|
||||
63 - Function encounted an error and could not verify OTP (Too many "quiet" strings given)
|
||||
255 - OTP is INVALID or EXPIRED
|
||||
```
|
||||
|
||||
22
demo-makeotp.sh
Normal file
22
demo-makeotp.sh
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
## zOTP Bash Demo for making a 6 digit OTP using Blake2 and being valid for 10 minutes
|
||||
|
||||
## Usage:
|
||||
## ./demo-makeotp.sh "{string-unique-to-user}"
|
||||
|
||||
declare thesalt="zOTP by Ze'ev Schurmann"
|
||||
|
||||
source zotp.inc.sh
|
||||
|
||||
if [[ -z "${1}" ]]; then
|
||||
echo "Usage:" >&2
|
||||
echo " $0 \"{string-unique-to-user}\"" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
declare theseed="${1}"
|
||||
|
||||
variablename="$(newOTP b2)"
|
||||
|
||||
echo "OTP is ${variablename} it will expire at $(date -d "+10 minutes")"
|
||||
24
demo-verifyotp.sh
Normal file
24
demo-verifyotp.sh
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
## zOTP Bash Demo for making a 6 digit OTP using Blake2 and being valid for 10 minutes
|
||||
|
||||
## Usage:
|
||||
## ./demo-makeotp.sh "{string-unique-to-user}" {otp}
|
||||
|
||||
declare thesalt="zOTP by Ze'ev Schurmann"
|
||||
|
||||
source zotp.inc.sh
|
||||
|
||||
if [[ -z "${1}" ]] || [[ -z "${2}" ]]; then
|
||||
echo "Usage:" >&2
|
||||
echo " $0 \"{string-unique-to-user}\" {otp}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
declare theseed="${1}"
|
||||
|
||||
if testOTP ${2} 10 b2 q; then
|
||||
echo "OTP is valid..."
|
||||
else
|
||||
echo "OTP is invalid or expired..."
|
||||
fi
|
||||
15
genhash-multiline.sh
Normal file
15
genhash-multiline.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
## zOTP Bash Example for Function genHash - Calculate a SHA512 Hash using a multiline string of text
|
||||
|
||||
declare thesalt="zOTP by Ze'ev Schurmann"
|
||||
|
||||
source zotp.inc.sh
|
||||
|
||||
variablename="$(genHash "
|
||||
This is the first line of text.
|
||||
This is the second line of text.
|
||||
This is the third line of text.
|
||||
" sha512)"
|
||||
|
||||
echo "${variablename}"
|
||||
11
genhash-singleline.sh
Normal file
11
genhash-singleline.sh
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
## zOTP Bash Example for Function genHash - Calculate a Blake2 Hash using a short string if text
|
||||
|
||||
declare thesalt="zOTP by Ze'ev Schurmann"
|
||||
|
||||
source zotp.inc.sh
|
||||
|
||||
variablename="$(genHash "This is a short string of text." b2)"
|
||||
|
||||
echo "${variablename}"
|
||||
11
genotp-onehash.sh
Normal file
11
genotp-onehash.sh
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
## zOTP Bash Example for Function genOTP - Calculate a custom 5 digit OTP using a single hash
|
||||
|
||||
declare thesalt="zOTP by Ze'ev Schurmann"
|
||||
|
||||
source zotp.inc.sh
|
||||
|
||||
variablename="$(genOTP d03d5fc2f2b73c917cee8d0bc1153b5affe4a243 5)"
|
||||
|
||||
echo "${variablename}"
|
||||
11
genotp-threehash.sh
Normal file
11
genotp-threehash.sh
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
## zOTP Bash Example for Function genOTP - Calculate a custom 8 digit OTP using three hashes
|
||||
|
||||
declare thesalt="zOTP by Ze'ev Schurmann"
|
||||
|
||||
source zotp.inc.sh
|
||||
|
||||
variablename="$(genOTP 09bf8c7563e7d577e07cca414b37c572a5e1676d 7540acafd1bb0ce0383e38859379e2c062cac45c 316fa3932ec66d9778be4df6132b4544d46ab146 8)"
|
||||
|
||||
echo "${variablename}"
|
||||
13
newotp-default.sh
Normal file
13
newotp-default.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
## zOTP Bash Example for Function newOTP - Create a new 7 digit OTP using Default Hashing Agorithm
|
||||
|
||||
declare thesalt="zOTP by Ze'ev Schurmann"
|
||||
|
||||
source zotp.inc.sh
|
||||
|
||||
declare theseed="user@domain.tld"
|
||||
|
||||
variablename="$(newOTP 7)"
|
||||
|
||||
echo "${variablename}"
|
||||
13
newotp-sha384.sh
Normal file
13
newotp-sha384.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
## zOTP Bash Example for Function newOTP - Create a new 4 digit OTP using SHA384
|
||||
|
||||
declare thesalt="zOTP by Ze'ev Schurmann"
|
||||
|
||||
source zotp.inc.sh
|
||||
|
||||
declare theseed="user@domain.tld"
|
||||
|
||||
variablename="$(newOTP 4 sha384)"
|
||||
|
||||
echo "${variablename}"
|
||||
15
testotp-exitcode.sh
Normal file
15
testotp-exitcode.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
## zOTP Bash Example for Function testOTP - Using exit code for validation over 45 minutes
|
||||
|
||||
declare thesalt="zOTP by Ze'ev Schurmann"
|
||||
|
||||
source zotp.inc.sh
|
||||
|
||||
declare theseed="user@domain.tld"
|
||||
|
||||
if testOTP 123456 45 q; then
|
||||
echo "OTP is valid..."
|
||||
else
|
||||
echo "OTP is invalid or expired..."
|
||||
fi
|
||||
15
testotp-textoutput.sh
Normal file
15
testotp-textoutput.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
## zOTP Bash Example for Function testOTP - Using text output for validation over 15 minutes
|
||||
|
||||
declare thesalt="zOTP by Ze'ev Schurmann"
|
||||
|
||||
source zotp.inc.sh
|
||||
|
||||
declare theseed="user@domain.tld"
|
||||
|
||||
if [[ "$(testOTP 123456 15)" == "VALID" ]]; then
|
||||
echo "OTP is valid..."
|
||||
else
|
||||
echo "OTP is invalid or expired..."
|
||||
fi
|
||||
519
zotp.inc.sh
Normal file
519
zotp.inc.sh
Normal file
@@ -0,0 +1,519 @@
|
||||
#!/bin/bash
|
||||
|
||||
## zOTP Bash - Generates/Tests One Time Pins in Bash Scripts
|
||||
## Version : 1.0
|
||||
## Author : Ze'ev Schurmann
|
||||
## Git Repo : https://git.3volve.net.za/thisiszeev/zotp-bash
|
||||
## Reddit : u/thisiszeev
|
||||
## License : GPL3
|
||||
##
|
||||
## One Time Pins generated using newOTP are unique to the minute. So from minute to minute the OTPs will be different.
|
||||
## When using testOTP to verify an OTP, you can assign a validity time in minutes from 1 minute to 120 minutes.
|
||||
##
|
||||
## Either copy and paste the functions to your project or use "source /path/to/zotp.inc.sh" above your main code.
|
||||
## If you copy and paste the functions in your project, please also include this commented out text.
|
||||
##
|
||||
## Don't forget to declare "thesalt" and "theseed" in your code before you call the functions newOTP and testOTP.
|
||||
##
|
||||
## "thesalt" must be a string unique to your project or to each instance of the project. This avoids other projects getting the same OTP at the same time.
|
||||
## Example:
|
||||
## declare thesalt="Name of project or service"
|
||||
## **Only needs to be declared once, ideally at the start of your code**
|
||||
##
|
||||
## "theseed" must be a string unique to the user the OTP is for. This avoids other users getting the same OTP at the same time.
|
||||
## Example:
|
||||
## declare theseed="user@domain.tld"
|
||||
## **Needs to be declared each time the current user changes**
|
||||
##
|
||||
## I do plan to port these functions to Perl, Python3 and PHP. I will make sure the OTPs created in one language can be verified in another.
|
||||
## If there is demand for other languages then I am willing to put it on my to-do list. Send me a chat request on Reddit.
|
||||
## Or maybe you keen to do a port to another language, send me a chat request and we can chat. I will link your port in the Git Repo for this project.
|
||||
|
||||
function cmdAwk1 {
|
||||
## cmdAwk1 is a pre/post command pipeline used by the function genHash.
|
||||
awk '{print $1}'
|
||||
}
|
||||
|
||||
function genHash {
|
||||
## genHash will calculate a hexidecimal hash string from a provided sting of any length or format using one of several Hashing Algorithms.
|
||||
## Outputs the hash as a string of hexadecimal digits. Used by the functions newOTP and testOTP.
|
||||
##
|
||||
## Usage:
|
||||
## genHash "{input-string}" {hash}
|
||||
## {input-string} - A string of any kind. Supports ESCAPE codes such as \n \t \r \$ etc. [REQUIRED - MUST BE ENCLOSED IN SINGLE OR DOULBE QUOTES]
|
||||
## {hash-algorithm} - The hashing algorithm used for generating the hexadecimal hash string [OPTIONAL - Defaults to MD5 if omitted]
|
||||
## **The above must be given in the order of input-string first and optional hash-algorithm after. The input-string is case sensitive but the hash-algorithm is not case sensitive**
|
||||
##
|
||||
## Examples:
|
||||
## Calculate a Blake2 Hash using a short string if text:
|
||||
## variablename="$(genHash "This is a short string of text." b2)"
|
||||
##
|
||||
## Calculate a SHA512 Hash using a multiline string of text:
|
||||
## variablename="$(genHash "
|
||||
## This is the first line of text.
|
||||
## This is the second line of text.
|
||||
## This is the third line of text.
|
||||
## " sha512)"
|
||||
##
|
||||
## Supported Hashing Algorithms:
|
||||
## b2 - Blake2 (up to 512bit) - 128 digits - Very High Entropy - Fast
|
||||
## md5 - Message Digest Algorithm 5 (128bit) - 32 digits - Very Low Entropy - Fast
|
||||
## sha - Secure Hash Algorithm (160bit) - 40 digits - Low Entropy - Fast
|
||||
## sha1 - Secure Hash Algorithm (160bit) - 40 digits - Low Entropy - Fast
|
||||
## sha224 - Secure Hash Algorithm (224bit) - 56 digits - Medium Entropy - Slow
|
||||
## sha256 - Secure Hash Algorithm (256bit) - 64 digits - High Entropy - Slow
|
||||
## sha384 - Secure Hash Algorithm (384bit) - 96 digits - Very High Entropy - Medium
|
||||
## sha512 - Secure Hash Algorithm (512bit) - 128 digits - Very High Entropy - Medium
|
||||
## **sha and sha1 are the same algorithm, but I've included both variants as some systems execute it as shasum and others as sha1sum.**
|
||||
## **Not all of these algorithms are available in Bash on all systems. Please test first. The function will output an error message**
|
||||
## **to the terminal and give an exit code 3 if the chosen algorithm is not found on your system.**
|
||||
##
|
||||
## Exit Codes:
|
||||
## 0 - Hash successfully calculated
|
||||
## 11 - Function encounted an error and could not calculate the Hash (No data given)
|
||||
## 23 - Function encounted an error and could not calculate the Hash (Invalid hashing algorithm given)
|
||||
## 29 - Function encounted an error and could not calculate the Hash (Hashing Algorithm not found)
|
||||
|
||||
local -A hashcmds=(
|
||||
[b2]="b2sum"
|
||||
[md5]="md5sum"
|
||||
[sha]="shasum"
|
||||
[sha1]="sha1sum"
|
||||
[sha224]="sha224sum"
|
||||
[sha256]="sha256sum"
|
||||
[sha384]="sha384sum"
|
||||
[sha512]="sha512sum"
|
||||
)
|
||||
|
||||
local -A precmds=(
|
||||
[b2]=""
|
||||
[md5]=""
|
||||
[sha]=""
|
||||
[sha1]=""
|
||||
[sha224]=""
|
||||
[sha256]=""
|
||||
[sha384]=""
|
||||
[sha512]=""
|
||||
)
|
||||
|
||||
local -A postcmds=(
|
||||
[b2]="cmdAwk1"
|
||||
[md5]="cmdAwk1"
|
||||
[sha]="cmdAwk1"
|
||||
[sha1]="cmdAwk1"
|
||||
[sha224]="cmdAwk1"
|
||||
[sha256]="cmdAwk1"
|
||||
[sha384]="cmdAwk1"
|
||||
[sha512]="cmdAwk1"
|
||||
)
|
||||
|
||||
if [[ -z "${1}" ]]; then
|
||||
echo "ERROR: function genHash - No data given... Must give string input..." >&2
|
||||
return 11
|
||||
fi
|
||||
|
||||
if [[ -z "${2}" ]]; then
|
||||
local hashcmd="md5sum"
|
||||
local precmd=""
|
||||
local postcmd="cmdAwk1"
|
||||
else
|
||||
if [[ -z "${hashcmds[${2,,}]}" ]]; then
|
||||
echo "ERROR: function genHash - Invalid hashing algorithm given..." >&2
|
||||
return 23
|
||||
else
|
||||
local hashcmd="${hashcmds[${2,,}]}"
|
||||
local precmd="${precmds[${2,,}]}"
|
||||
local postcmd="${postcmds[${2,,}]}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$(whereis "${hashcmd}" | awk '{print $2}')" ]]; then
|
||||
echo "ERROR: function genHash - Hashing Algorithm not found... ${hashcmd} is not supported on this system..." >&2
|
||||
return 29
|
||||
fi
|
||||
|
||||
if [[ -z "${precmd}" ]] && [[ -z "${postcmd}" ]]; then
|
||||
echo -e "${1}" | ${hashcmd}
|
||||
elif [[ -z "${precmd}" ]] && [[ -n "${postcmd}" ]]; then
|
||||
echo -e "${1}" | ${hashcmd} | ${postcmd}
|
||||
elif [[ -n "${precmd}" ]] && [[ -z "${postcmd}" ]]; then
|
||||
echo -e "${1}" | ${precmd} | ${hashcmd}
|
||||
elif [[ -n "${precmd}" ]] && [[ -n "${postcmd}" ]]; then
|
||||
echo -e "${1}" | ${precmd} | ${hashcmd} | ${postcmd}
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function genOTP {
|
||||
## genOTP will calculate an OTP using 1 to 3 hexadecimal hash strings of 32 digits or more. Outputs the OTP as text digits. Used by the functions newOTP and testOTP.
|
||||
##
|
||||
## Usage:
|
||||
## genOTP {hash-one} {hash-two} {hash-three} {number-of-digits}
|
||||
## {hash-one} - A hexadecimal hash string of 32 digits or more [REQUIRED]
|
||||
## {hash-two} - A hexadecimal hash string of 32 digits or more [OPTIONAL - Defaults to a string of zeros equal in length to hash-one if omitted]
|
||||
## {hash-three} - A hexadecimal hash string of 32 digits or more [OPTIONAL - Defaults to a string of zeros equal in length to hash-one o hash-two, which ever is shorter, if omitted]
|
||||
## {number-of-digits} - An integer from 4 to 16 [OPTIONAL - Defaults to 6 if omitted]
|
||||
## **The above can be given in any order and is not case sensitive**
|
||||
##
|
||||
## Examples:
|
||||
## Calculate a custom 5 digit OTP using a single hash:
|
||||
## variablename="$(genOTP d03d5fc2f2b73c917cee8d0bc1153b5affe4a243 5)"
|
||||
##
|
||||
## Calculate a custom 8 digit OTP using three hashes:
|
||||
## variablename="$(genOTP 09bf8c7563e7d577e07cca414b37c572a5e1676d 7540acafd1bb0ce0383e38859379e2c062cac45c 316fa3932ec66d9778be4df6132b4544d46ab146 8)"
|
||||
##
|
||||
## Exit Codes:
|
||||
## 0 - OTP successfully calculated
|
||||
## 11 - Function encounted an error and could not calculate OTP (No data given)
|
||||
## 12 - Function encounted an error and could not calculate OTP (Too much data given)
|
||||
## 13 - Function encounted an error and could not calculate OTP (Invalid data given)
|
||||
## 72 - Function encounted an error and could not calculate OTP (Too many OTP size integers given)
|
||||
## 81 - Function encounted an error and could not calculate OTP (No hexadecimal hash strings given)
|
||||
## 82 - Function encounted an error and could not calculate OTP (Too many hexadecimal hash strings given)
|
||||
## 84 - Function encounted an error and could not calculate OTP (Given hexadecimal hash string is too short)
|
||||
|
||||
local optdigits=1
|
||||
local opthashes=3
|
||||
local digits=6
|
||||
local -a hashes
|
||||
|
||||
if [[ $# -eq 0 ]]; then
|
||||
echo "ERROR: function genOTP - No data given... Must give at least one hexadecimal hash string of at least 32 digits..." >&2
|
||||
return 11
|
||||
fi
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
if [[ ${optdigits} -eq 0 ]] && [[ ${opthashes} -eq 0 ]]; then
|
||||
echo "ERROR: function genOTP - Too much data given... Only one integer from 4 to 16 for number of OTP digits and one to three hexadecimal hash strings of 32 digits or more can be given..." >&2
|
||||
return 12
|
||||
fi
|
||||
if [[ "${1}" =~ ^([4-9]|1[0-6])$ ]]; then
|
||||
if [[ ${optdigits} -gt 0 ]]; then
|
||||
digits=${1}
|
||||
((optdigits--))
|
||||
else
|
||||
echo "ERROR: function genOTP - Too many OTP size integers given... Only one integer from 4 to 16 for number of OTP digits can be given..." >&2
|
||||
return 72
|
||||
fi
|
||||
elif [[ "${1}" =~ ^[0-9a-fA-F]+$ ]]; then
|
||||
if [[ ${opthashes} -gt 0 ]]; then
|
||||
if [[ ${#1} -lt 32 ]]; then
|
||||
echo "ERROR: function genOTP - Given hexadecimal hash string is too short... Only hexadecimal hash strings of 32 digits or more can be given..." >&2
|
||||
return 84
|
||||
fi
|
||||
hashes+=("${1,,}")
|
||||
((opthashes--))
|
||||
else
|
||||
echo "ERROR: function genOTP - Too many hexadecimal hash strings given... Only one to three hexadecimal hash strings of 32 digits or more can be given..." >&2
|
||||
return 82
|
||||
fi
|
||||
elif [[ "${1}" == "" ]]; then
|
||||
local donothing=0
|
||||
else
|
||||
echo "ERROR: function genOTP - Invalid data given... Only one integer from 4 to 16 for number of OTP digits and one to three hexadecimal hash strings of 32 digits or more can be given..." >&2
|
||||
return 13
|
||||
fi
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ ${#hashes[@]} -eq 0 ]]; then
|
||||
echo "ERROR: function genOTP - No hexadecimal hash strings given... Must give at least one hexadecimal string of at least 32 digits..." >&2
|
||||
return 81
|
||||
fi
|
||||
|
||||
local hashlength=${#hashes[0]}
|
||||
|
||||
while [[ ${#hashes[@]} -lt 3 ]]; do
|
||||
hashes+=("$(printf '%0*d\n' "${hashlength}" 0)")
|
||||
done
|
||||
|
||||
local n
|
||||
|
||||
for ((n=1; n<3; n++)); do
|
||||
if [[ ${#hashes[${n}]} -lt ${hashlength} ]]; then
|
||||
hashlength=${#hashes[${n}]}
|
||||
fi
|
||||
done
|
||||
|
||||
local -A hex2dec=(
|
||||
[0]=0
|
||||
[1]=1
|
||||
[2]=2
|
||||
[3]=3
|
||||
[4]=4
|
||||
[5]=5
|
||||
[6]=6
|
||||
[7]=7
|
||||
[8]=8
|
||||
[9]=9
|
||||
[a]=10
|
||||
[b]=11
|
||||
[c]=12
|
||||
[d]=13
|
||||
[e]=14
|
||||
[f]=15
|
||||
)
|
||||
|
||||
local -a values
|
||||
local position
|
||||
local sum
|
||||
|
||||
for ((position=0; position<hashlength; position++)); do
|
||||
(( sum =
|
||||
hex2dec[${hashes[0]:position:1}] +
|
||||
hex2dec[${hashes[1]:position:1}] +
|
||||
hex2dec[${hashes[2]:position:1}]
|
||||
))
|
||||
(( value = (sum + sum % 3 + 1) % 16 ))
|
||||
values+=(${value})
|
||||
done
|
||||
|
||||
local offset=$((hashlength/digits))
|
||||
local value
|
||||
|
||||
for ((digit=0; digit<digits; digit++)); do
|
||||
value=0
|
||||
for ((position=0; position<offset; position++)); do
|
||||
value=$((value+${values[$((position*digits+digit))]}))
|
||||
done
|
||||
value=$((value%10))
|
||||
echo -n ${value}
|
||||
done
|
||||
echo
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function newOTP {
|
||||
## newOTP will create a new OTP based on the current time (to the minute). Outputs the OTP as text digits.
|
||||
##
|
||||
## Usage:
|
||||
## newOTP {number-of-digits} {hash-algorithm}
|
||||
## {number-of-digits} - An integer from 4 to 16 [OPTIONAL - Defaults to 6 if omitted]
|
||||
## {hash-algorithm} - The hashing algorithm used for generating OTPs [OPTIONAL - Defaults to MD5 if omitted]
|
||||
## See comments provided in the function genHash
|
||||
## **The above can be given in any order and is not case sensitive**
|
||||
##
|
||||
## Examples:
|
||||
## Create a new 7 digit OTP using Default Hashing Agorithm:
|
||||
## declare thesalt="Name of project or service"
|
||||
## declare theseed="user@domain.tld"
|
||||
## variablename="$(newOTP 7)"
|
||||
##
|
||||
## Create a new 4 digit OTP using SHA384:
|
||||
## declare thesalt="Name of project or service"
|
||||
## declare theseed="user@domain.tld"
|
||||
## variablename="$(newOTP 4 sha384)"
|
||||
##
|
||||
## Exit Codes:
|
||||
## 0 - OTP successfully created
|
||||
## 12 - Function encounted an error and could not create OTP (Too much data given)
|
||||
## 13 - Function encounted an error and could not create OTP (Invalid data given)
|
||||
## 22 - Function encounted an error and could not create OTP (Too many hash algorithm strings given)
|
||||
## 72 - Function encounted an error and could not create OTP (Too many OTP size integers given)
|
||||
|
||||
local optdigits=1
|
||||
local optsums=1
|
||||
local digits=6
|
||||
local hashsum="md5"
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
if [[ ${optdigits} -eq 0 ]] && [[ ${optsums} -eq 0 ]]; then
|
||||
echo "ERROR: function newOTP - Too much data given... Only one integer from 4 to 16 for number of OTP digits and one string of 2 characters or more for Hash Algorithm can be given..." >&2
|
||||
return 12
|
||||
fi
|
||||
if [[ "${1}" =~ ^([4-9]|1[0-6])$ ]]; then
|
||||
if [[ ${optdigits} -gt 0 ]]; then
|
||||
digits=${1}
|
||||
((optdigits--))
|
||||
else
|
||||
echo "ERROR: function newOTP - Too many OTP size integers given... Only one integer from 4 to 16 for number of OTP digits can be given..." >&2
|
||||
return 72
|
||||
fi
|
||||
elif [[ "${1}" =~ ^[0-9a-zA-Z][0-9a-zA-Z]+$ ]]; then
|
||||
if [[ ${optsums} -gt 0 ]]; then
|
||||
hashsum="${1,,}"
|
||||
((optsums--))
|
||||
else
|
||||
echo "ERROR: function newOTP - Too many hash algorithm strings given... Only one string of 2 characters or more for Hash Algorithm can be given..." >&2
|
||||
return 22
|
||||
fi
|
||||
elif [[ "${1}" == "" ]]; then
|
||||
local donothing=0
|
||||
else
|
||||
echo "ERROR: function newOTP - Invalid data given... Only one integer from 4 to 16 for number of OTP digits and one string of 2 characters or more for Hash Algorithm can be given..." >&2
|
||||
return 13
|
||||
fi
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ -z "${thesalt}" ]] || [[ "${thesalt}" == "" ]]; then
|
||||
local salthash=""
|
||||
echo "WARNING: Variable \${thesalt} has not been declared with a value... Assign it a string unique to the service the OTP is being used for to avoid other services getting the same OTP..." >&2
|
||||
else
|
||||
local salthash="$(genHash "${thesalt}" "${hashsum}")"
|
||||
fi
|
||||
if [[ -z "${theseed}" ]] || [[ "${theseed}" == "" ]]; then
|
||||
local seedhash=""
|
||||
echo "WARNING: Variable \${theseed} has not been declared with a value... Assign it a string unique to the user account the OTP is being used for to avoid other users getting the same OTP..." >&2
|
||||
else
|
||||
local seedhash="$(genHash "${theseed}" "${hashsum}")"
|
||||
fi
|
||||
|
||||
local timehash="$(genHash "$(date -u "+%Y%B%d%H%M%A%U%u")" "${hashsum}")"
|
||||
|
||||
genOTP "${seedhash}" "${salthash}" "${timehash}" ${digits}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function testOTP {
|
||||
## testOTP will verify if an OTP is still valid and correct. Outputs the text "VALID" (exit code 0) or "INVALID" (exit code 255).
|
||||
##
|
||||
## Usage:
|
||||
## testOTP {OTP} {valid-minutes} {hash-algorithm} {quiet}
|
||||
## {OTP} - A 4 to 16 digit OTP to be verified [REQUIRED]
|
||||
## {valid-minutes} - The time in minutes (from 1 to 120) the OTP is valid for [OPTIONAL - Defaults to 30 minutes if omitted]
|
||||
## {hash-algorithm} - The hashing algorithm used for generating OTPs [OPTIONAL - Defaults to MD5 if omitted]
|
||||
## See comments provided in the function genHash
|
||||
## {quiet} - Disables text output when verifying allowing you to rely on exit codes instead. [OPTIONAL]
|
||||
## You can use just the letter "q" as an alternative to "quiet"
|
||||
## **The above can be given in any order and is not case sensitive**
|
||||
##
|
||||
## Examples:
|
||||
## Using text output for validation over 15 minutes:
|
||||
## declare thesalt="Name of project or service"
|
||||
## declare theseed="user@domain.tld"
|
||||
## if [[ "$(testOTP 123456 15)" == "VALID" ]]; then
|
||||
## echo "OTP is valid..."
|
||||
## else
|
||||
## echo "OTP is invalid or expired..."
|
||||
## fi
|
||||
##
|
||||
## Using exit code for validation over 45 minutes:
|
||||
## declare thesalt="Name of project or service"
|
||||
## declare theseed="user@domain.tld"
|
||||
## if testOTP 123456 45 q; then
|
||||
## echo "OTP is valid..."
|
||||
## else
|
||||
## echo "OTP is invalid or expired..."
|
||||
## fi
|
||||
##
|
||||
## Exit Codes:
|
||||
## 0 - OTP is VALID
|
||||
## 11 - Function encounted an error and could not verify OTP (No data given)
|
||||
## 12 - Function encounted an error and could not verify OTP (Too much data given)
|
||||
## 13 - Function encounted an error and could not verify OTP (Invalid data given)
|
||||
## 33 - Function encounted an error and could not verify OTP (Too many hash algorithm strings given)
|
||||
## 41 - Function encounted an error and could not verify OTP (No OTP given)
|
||||
## 43 - Function encounted an error and could not verify OTP (Too many OTPs given)
|
||||
## 53 - Function encounted an error and could not verify OTP (Too many minute integers given)
|
||||
## 55 - Function encounted an error and could not verify OTP (Minute integer given to high)
|
||||
## 63 - Function encounted an error and could not verify OTP (Too many "quiet" strings given)
|
||||
## 255 - OTP is INVALID or EXPIRED
|
||||
|
||||
local optotps=1
|
||||
local optvalids=1
|
||||
local optsums=1
|
||||
local optquiets=1
|
||||
local theotp
|
||||
local quiet=false
|
||||
local validminutes=30
|
||||
local hashsum="md5"
|
||||
local digits=0
|
||||
|
||||
if [[ $# -eq 0 ]]; then
|
||||
echo "ERROR: function testOTP - No data given... Must at least give one OTP of 4 to 16 digits..." >&2
|
||||
return 11
|
||||
fi
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
if [[ ${optotps} -eq 0 ]] && [[ ${optvalids} -eq 0 ]] && [[ ${optsums} -eq 0 ]] && [[ ${optquiets} -eq 0 ]]; then
|
||||
echo "ERROR: function testOTP - Too much data given... Only one OTP of 4 to 16 digits, one integer from 1 to 120 for valid time in minutes, one string of 2 characters or more for Hash Algorithm can be given and optional string \"quiet\" for silent mode relying on exit code 0 for MATCH and 255 for NO MATCH..." >&2
|
||||
return 12
|
||||
fi
|
||||
if [[ "${1}" =~ ^[0-9]+$ ]] && [[ ${#1} -ge 4 ]] && [[ ${#1} -le 16 ]]; then
|
||||
if [[ ${optotps} -gt 0 ]]; then
|
||||
theotp=${1}
|
||||
digits=${#theotp}
|
||||
((optotps--))
|
||||
else
|
||||
echo "ERROR: function testOTP - Too many OTPs given... Only one OTP of 4 to 16 digits can be given..." >&2
|
||||
return 43
|
||||
fi
|
||||
elif [[ "${1}" =~ ^([1-9]|[1-9][0-9]+)$ ]]; then
|
||||
if [[ ${optvalids} -gt 0 ]]; then
|
||||
if [[ ${1} -gt 120 ]]; then
|
||||
echo "ERROR: function testOTP - Minute integer given to high... Integer must be from 1 to 120 for valid time in minutes can be given..." >&2
|
||||
return 55
|
||||
fi
|
||||
validminutes=${1}
|
||||
((optvalids--))
|
||||
else
|
||||
echo "ERROR: function testOTP - Too many minute integers given... Only one integer from 1 to 120 for valid time in minutes can be given..." >&2
|
||||
return 53
|
||||
fi
|
||||
elif [[ "${1,,}" =~ ^(q|quiet)$ ]]; then
|
||||
if [[ ${optquiets} -gt 0 ]]; then
|
||||
quiet=true
|
||||
((optquiets--))
|
||||
else
|
||||
echo "ERROR: function testOTP - Too many \"quiet\" strings given... Only one optional string \"quiet\" for silent mode can be given relying on exit code 0 for MATCH and 255 for NO MATCH..." >&2
|
||||
return 63
|
||||
fi
|
||||
elif [[ "${1}" =~ ^[0-9a-zA-Z][0-9a-zA-Z]+$ ]]; then
|
||||
if [[ ${optsums} -gt 0 ]]; then
|
||||
hashsum="${1,,}"
|
||||
((optsums--))
|
||||
else
|
||||
echo "ERROR: function testOTP - Too many hash algorithm strings given... Only one string of 2 characters or more for Hash Algorithm can be given..." >&2
|
||||
return 33
|
||||
fi
|
||||
elif [[ "${1}" == "" ]]; then
|
||||
local donothing=0
|
||||
else
|
||||
echo "ERROR: function testOTP - Invalid data given... Only one OTP of 4 to 16 digits, one integer from 1 to 120 for valid time in minutes, one string of 2 characters or more for Hash Algorithm can be given and optional string \"quiet\" for silent mode relying on exit code 0 for MATCH and 255 for NO MATCH..." >&2
|
||||
return 13
|
||||
fi
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ ${digits} -eq 0 ]]; then
|
||||
echo "ERROR: function testOTP - No OTP given... Must give one OTP of 4 to 16 digits..." >&2
|
||||
return 41
|
||||
fi
|
||||
|
||||
if [[ -z "${thesalt}" ]] || [[ "${thesalt}" == "" ]]; then
|
||||
local salthash=""
|
||||
echo "WARNING: Variable \${thesalt} has not been declared with a value... Assign it a string unique to the service the OTP is being used for to avoid other services getting the same OTP..." >&2
|
||||
else
|
||||
local salthash="$(genHash "${thesalt}" "${hashsum}")"
|
||||
fi
|
||||
if [[ -z "${theseed}" ]] || [[ "${theseed}" == "" ]]; then
|
||||
local seedhash=""
|
||||
echo "WARNING: Variable \${theseed} has not been declared with a value... Assign it a string unique to the user account the OTP is being used for to avoid other users getting the same OTP..." >&2
|
||||
else
|
||||
local seedhash="$(genHash "${theseed}" "${hashsum}")"
|
||||
fi
|
||||
|
||||
local n
|
||||
|
||||
((validminutes++))
|
||||
for ((n=0; n<${validminutes}; n++)); do
|
||||
local timehash="$(genHash "$(date -u -d "${n} minutes ago" "+%Y%B%d%H%M%A%U%u")" "${hashsum}")"
|
||||
genOTP "${seedhash}" "${salthash}" "${timehash}" ${digits}
|
||||
done | grep -q ^${theotp}$
|
||||
|
||||
if [[ $? -eq 0 ]]; then
|
||||
if [[ ${quiet} == false ]]; then
|
||||
echo "VALID"
|
||||
fi
|
||||
return 0
|
||||
else
|
||||
if [[ ${quiet} == false ]]; then
|
||||
echo "INVALID"
|
||||
fi
|
||||
return 255
|
||||
fi
|
||||
}
|
||||
Reference in New Issue
Block a user