Skip to content

Commit

Permalink
Enable htop sync (#1)
Browse files Browse the repository at this point in the history
* add sync

* update readme
  • Loading branch information
sebastian-mora committed Feb 7, 2024
1 parent d4fa562 commit 3950368
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 50 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ The BasicOTP library aims to simplify the generation and validation of one-time
- **Configurable Hash Algorithms**: Users can choose from different hash algorithms including SHA1, SHA256, and SHA512 according to their security requirements.
- **Customizable Code Length**: BasicOTP allows customization of the length of generated OTP codes to meet specific application needs.
- **URI Generation**: BasicOTP provides a convenient method for generating URIs according to the Google Authenticator Key URI Format, facilitating integration with OTP token apps.
- **Synchronization in HOTP Validation**: BasicOTP supports synchronization in HOTP validation, allowing the Validate() function to look ahead and fast forward the counter to the client's counter if a valid token is found.

## Use Cases

Expand All @@ -29,15 +30,15 @@ BasicOTP is suitable for a variety of use cases including:

## Dependencies

BasicOTP is built using Go and leverages GO standard cryptographic libraries for hash functions and HMAC calculation. It is compatible with modern Go development environments and has no third part dependencies.
BasicOTP is built using Go and leverages Go standard cryptographic libraries for hash functions and HMAC calculation. It is compatible with modern Go development environments and has no third-party dependencies.

## Installation

To use BasicOTP in your Go project, you can import it using Go modules:

```bash
go get github.com/sebatian-mora/basicOTP
```
````bash
go get github.com/sebastian-mora/basicOTP


## Example

Expand Down Expand Up @@ -76,4 +77,4 @@ func main() {
}
```
````
38 changes: 28 additions & 10 deletions hotp.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,32 @@ import (

// hotp represents a Sequence-based One-Time Password generator.
type hotp struct {
otp OTP
Counter int
otp OTP
Counter int
synchronizationLimit int
}

// HOTPConfig holds configuration parameters for HOTP generation.
type HOTPConfig struct {
CodeLength int // CodeLength is the length of the generated OTP code.
HashType HashType // HashType is the hash algorithm used for OTP generation.
Secret []byte // Secret is the shared secret key used for OTP generation.
Counter int // Counter is the initial counter value for HOTP generation.
CodeLength int // CodeLength is the length of the generated OTP code.
HashType HashType // HashType is the hash algorithm used for OTP generation.
Secret []byte // Secret is the shared secret key used for OTP generation.
Counter int // Counter is the initial counter value for HOTP generation.
SynchronizationLimit int // SynchronizationLimit sets the limit for synchronization in HOTP validation.

// SynchronizationLimit specifies the maximum number of steps to look ahead during OTP validation.
// If SynchronizationLimit is 0 or negative, no synchronization is performed, and the Counter value remains unchanged.
// If SynchronizationLimit is greater than 0, Validate() will check HOTPs ahead of the current Counter value up to the specified limit.
// If a valid OTP is found, Validate() returns true and advances the Counter value to synchronize with the client.
}

// NewHTOP creates a new instance of hopt based on the provided HOTPConfig.
func NewHTOP(config HOTPConfig) *hotp {

return &hotp{
otp: NewOTP(config.Secret, config.HashType, config.CodeLength),
Counter: config.Counter,
otp: NewOTP(config.Secret, config.HashType, config.CodeLength),
Counter: config.Counter,
synchronizationLimit: config.SynchronizationLimit,
}
}

Expand All @@ -37,10 +46,19 @@ func (h *hotp) Generate() string {

// Validate validates an input OTP code against the current counter value.
func (h *hotp) Validate(input string) bool {
if h.otp.Generate(h.Counter) == input {
h.Counter = h.Counter + 1
// If synchronization limit is 0 or negative, perform a single validation
if h.synchronizationLimit <= 0 && h.otp.Generate(h.Counter) == input {
h.Counter++
return true
}

for i := 0; i < h.synchronizationLimit; i++ {
if h.otp.Generate(h.Counter+i) == input {
h.Counter += i // Fast-forward counter to sync
return true
}
}

return false
}

Expand Down
104 changes: 69 additions & 35 deletions hotp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ func TestHTOPValidate(t *testing.T) {
// Define the secret and initial counter for the server and client instances
secret := []byte("12345678901234567890") // Sample secret, replace with actual secret
serverCounter := 0
clientCounter := 0

// Create server and client instances of hopt with the same secret and initial counters
serverConfig := basicOTP.HOTPConfig{
Expand All @@ -75,42 +74,11 @@ func TestHTOPValidate(t *testing.T) {
Secret: secret,
Counter: serverCounter,
}
serverHTOP := basicOTP.NewHTOP(serverConfig)

clientConfig := basicOTP.HOTPConfig{
CodeLength: 6,
HashType: basicOTP.SHA1,
Secret: secret,
Counter: clientCounter,
}
clientHTOP := basicOTP.NewHTOP(clientConfig)
hotp := basicOTP.NewHTOP(serverConfig)

for _, tc := range testCases {

previousServerCounter := serverHTOP.Counter
previousClientCounter := clientHTOP.Counter

// Generate the OTP for the current test case on the server side
clientOTPCode := clientHTOP.Generate()

// Validate the OTP on the client side
valid := serverHTOP.Validate(clientOTPCode)

// Ensure the validation result matches the expectation
if valid && clientOTPCode != tc.Expected {
t.Errorf("Validation failed for count %d, expected %s, got %s", tc.Counter, tc.Expected, clientOTPCode)
} else if !valid && clientOTPCode == tc.Expected {
t.Errorf("Validation passed for count %d, expected validation to fail", tc.Counter)
}

// Ensure the server counter is updated correctly after validation
if valid && serverHTOP.Counter != previousServerCounter+1 {
t.Errorf("Server counter not updated correctly after validation for count %d", tc.Counter)
}

// The client counter should incremented even if rejected
if !valid && clientHTOP.Counter != previousClientCounter {
t.Errorf("Client counter not updated correctly after failing validation. Got %d Expected %d", clientHTOP.Counter, previousClientCounter+1)
if !hotp.Validate(tc.Expected) {
t.Errorf("Failed to validate code: %s, counter: %d", tc.Expected, hotp.Counter)
}
}
}
Expand Down Expand Up @@ -143,6 +111,72 @@ func TestHTOPInvalidCodes(t *testing.T) {
}
}

func TestHOTPSuccessfulValidationOfOutOfSync(t *testing.T) {

/*
The test cases are valid for C=0, setting C=-3 represents
the server is 3 codes behind the client. The Sync limit is 10
so all codes should be valid.
*/
config := basicOTP.HOTPConfig{
CodeLength: 6,
HashType: basicOTP.SHA1,
Secret: []byte("12345678901234567890"), // Sample secret, replace with actual secret
Counter: -3,
SynchronizationLimit: 10,
}
hopt := basicOTP.NewHTOP(config)

for _, tc := range testCases {
t.Run(fmt.Sprintf("Count_%d", tc.Counter), func(t *testing.T) {
result := hopt.Validate(tc.Expected)

// Check if the forward lookup worked and returned true
if !result {
t.Error("Validation failed, forward look up did not work")
}

// Ensure the server synced with the client
if result && hopt.Counter != tc.Counter {
t.Errorf("The counter was not synced correctly, Expected %d, Got: %d", 0, hopt.Counter)
}

// Pass all other validations after sync
if !hopt.Validate(tc.Expected) {
t.Error("Validation failed after sync")
}
})
}
}

func TestHOTPSFailedValidationOfOutOfSync(t *testing.T) {

/*
The test cases are valid for C=0, setting C=-300 represents
the server is 300 codes behind the client. The Sync limit is 10
so all codes should be invalid.
*/
config := basicOTP.HOTPConfig{
CodeLength: 6,
HashType: basicOTP.SHA1,
Secret: []byte("12345678901234567890"), // Sample secret, replace with actual secret
Counter: -300,
SynchronizationLimit: 10,
}
hopt := basicOTP.NewHTOP(config)

for _, tc := range testCases {
t.Run(fmt.Sprintf("Count_%d", tc.Counter), func(t *testing.T) {
result := hopt.Validate(tc.Expected)

// Check if the forward lookup worked and returned true
if result {
t.Error("Validation failed, did not stop forward lookup")
}
})
}
}

func TestHOPTURI(t *testing.T) {
secretKey := []byte("Hello!")

Expand Down

0 comments on commit 3950368

Please sign in to comment.