Documentation
¶
Overview ¶
Package otp : The OTP package provides implementation of One Time Password (OTP) services as defined by RFCs 4226 (HOTP), 6238 (TOTP).
One time password implemenatation
This package implements RFC 6238 "Totp: Time-Based One-Time Password Algorithm" and RFC 4226 HOTP: "An HMAC-Based One-Time Password Algorithm" - OTP() implements the lower level common layer - Totp() (time based OTP) and - Hotp (counter based OTP) implement the upper layers.
Comments:
- Illegal operations: 1.1. An empty secret key 1.2. Code length other than 6-8 digits (not protected by the code) 1.3. Digest other than MD4, MD5, SHA1, SHA256 or SHA512 (not defined by the RFC) (not protected by the code)
- The encoding scheme of the secret key is not define by the RFC. It's the user's responsability to use a legal secret key. The most common encoding scheme is base32 (as used by the Google Authenticator), therefor the testing of the code includes converting a string to a legal base32 encoded string.
- The option for resetting a HOTP counter to another value of counter is currently not implemented as it is defined as an extension in the RFC
Package otp : A One Time Password (OTP) is a password that is valid for only one login session or transaction (and may be limited for a specific time period). The most important advantage that is addressed by OTPs is that, in contrast to static passwords, they are not vulnerable to replay attacks. A second major advantage is that a user who uses the same (or similar) password for multiple systems, is not made vulnerable on all of them, if the password for one of these is gained by an attacker. We implemented the 2 possible OTP implementations: A time based one time password algorithm (TOTP) and HMAC-based one time password algorithm (HOTP). Our OTP implementation is based on RFC 2289 for OTP in general, RFC 4226 for HOTP, and RFC 6238 for TOTP.
The OTP implementation has three layers:
- The base layer includes the secret (e.g. SHA256, SHA1) and the number of digits in the digest.
- The second layer is the digest algorithm which is time based for TOTP and counter based for HOTP.
- The topmost layer includes the policy of handing unsuccessful authentication attempts. This includes blocking and throttling. The blocking mechanism allows blocking users for a given duration of time (or until a manual unblock) after they pass a cliff which a limit for the number of allowed consecutive unsuccessful authentication attempts. The throttling mechanism controls the delay between the authentication request and the response. This delay is increased as the number of consecutive unsuccessful attempts grows to avoid brute force password attacks. This layer includes also a time window for avoiding clock drifting errors when TOTPs are used.
OTP per user:
Each OTP property has the following fields: - Secret - the password itself - Blocked - a flag indicating if the user is blocked - Throttling parameters - Handling of all the throttle parameters (details below), including: - HOTP information: Current counter and OTP data (details below) - TOTP information: Interval, the time interval in seconds which is counted as a 'tick' (e.g. 30 seconds) and OTP data (details bellow)
Index ¶
- type Hotp
- type Otp
- type Serializer
- func (s Serializer) AddToStorage(prefix string, data interface{}, storage *ss.SecureStorage) error
- func (s Serializer) IsEqualProperties(da1 interface{}, da2 interface{}) bool
- func (s Serializer) PrintProperties(data interface{}) string
- func (s Serializer) ReadFromStorage(key string, storage *ss.SecureStorage) (interface{}, error)
- type Totp
- type TypeOfOtp
- type UserInfoOtp
- func (u *UserInfoOtp) IsEqual(u1 interface{}) bool
- func (u UserInfoOtp) IsOtpUserBlocked() (bool, error)
- func (u *UserInfoOtp) SetOtpUserBlockedState(block bool) error
- func (u UserInfoOtp) String() string
- func (u *UserInfoOtp) VerifyCode(code string, otpType TypeOfOtp) (bool, error)
- func (u *UserInfoOtp) VerifyOtpUserCode(code string, otpType TypeOfOtp) (bool, error)
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Hotp ¶
Hotp : Event-based HMAC One Time Password
Example ¶
The HOTP example demonstrates the following secenarios: Adding 2 users, each with a different initial counter and Repeat the following 10 times:
randomly select one of the users, verify that the calculated HOTP is as expected by the provider and then increase the internal counter of the randomly selected user by one
package main import ( "fmt" "math/rand" defs "github.com/ibm-security-innovation/libsecurity-go/defs" en "github.com/ibm-security-innovation/libsecurity-go/entity" "github.com/ibm-security-innovation/libsecurity-go/otp" ) var ( entityManager *en.EntityManager secret = []byte("AaB1@CDABCD12341234") ) func addOtpUser(id string, secret []byte, startCounter int64) { entityManager.AddUser(id) otpUser, _ := otp.NewOtpUser(secret, true, false, 10, 1, 100, 2, 0, startCounter) entityManager.AddPropertyToEntity(id, defs.OtpPropertyName, otpUser) } type HotpUser struct { name string counter int64 hotp *otp.Hotp } func (u HotpUser) String() string { ret := fmt.Sprintf("Entity id: %v, counter %v", u.name, u.counter) return ret } func initHotpUserslist(users []HotpUser, secret []byte) { for i, user := range users { addOtpUser(user.name, secret, user.counter) hotp, err := otp.NewHotp(secret, user.counter) if err != nil { fmt.Println("HOTP can't be initialized, error: ", err) } users[i].hotp = hotp } } func main() { users := []HotpUser{{name: "camera", counter: 2000}, {name: "cell-phone", counter: 1000}} initHotpUserslist(users, secret) for i := 0; i < 10; i++ { idx := rand.Int() % len(users) hotp := users[idx].hotp code, err := hotp.Next() if err != nil { fmt.Println("Can't generate HOTP, error: ", err) } else { entity, _ := entityManager.GetPropertyAttachedToEntity(users[idx].name, defs.OtpPropertyName) if entity != nil { ok, err := entity.(*otp.UserInfoOtp).VerifyOtpUserCode(code, otp.HotpType) if ok { fmt.Printf("HOTP: %v, Code: %v as expected\n", users[idx], code) users[idx].counter++ } else { fmt.Printf("HOTP: %v code %v is not as expected, error: %v\n", users[idx], code, err) } } } } }
Output:
type Otp ¶
type Otp struct { Secret []byte // Assume a legal Secret key Digits int // Number of digits in the code. default is 6 // contains filtered or unexported fields }
Otp : One Time Password structure
func NewOtp ¶
NewOtp : The default OTP: sha1 with 6 digits Any number of digits and any (hash) function are allowed
func NewOtpAdvance ¶
NewOtpAdvance : Generate Otp with the given parameters
func (Otp) Generate ¶
Generate : Return the OTP for a given input Input may either be time (for Totp) or integer (for Hotp)
func (Otp) GenerateHmac ¶
GenerateHmac : Return the OTP for a given input Input is a byte array
type Serializer ¶
type Serializer struct{}
Serializer : virtual set of functions that must be implemented by each module
func (Serializer) AddToStorage ¶
func (s Serializer) AddToStorage(prefix string, data interface{}, storage *ss.SecureStorage) error
AddToStorage : Add the OTP property information to the secure_storage
func (Serializer) IsEqualProperties ¶
func (s Serializer) IsEqualProperties(da1 interface{}, da2 interface{}) bool
IsEqualProperties : Compare 2 OTP properties (without parts of OTP that are not saved)
func (Serializer) PrintProperties ¶
func (s Serializer) PrintProperties(data interface{}) string
PrintProperties : Print the OTP property data
func (Serializer) ReadFromStorage ¶
func (s Serializer) ReadFromStorage(key string, storage *ss.SecureStorage) (interface{}, error)
ReadFromStorage : Return the entity OTP data read from the secure storage (in JSON format)
type Totp ¶
type Totp struct { Interval time.Duration // The time interval in seconds for OT, The default is 30 seconds (the standard) BaseOtp *Otp }
Totp : Time-based One Time Password
Example ¶
The TOTP example demonstrates the following secenario: Verify that the calculated TOTP for the current time is as expected by the provider
package main import ( "fmt" defs "github.com/ibm-security-innovation/libsecurity-go/defs" en "github.com/ibm-security-innovation/libsecurity-go/entity" "github.com/ibm-security-innovation/libsecurity-go/otp" ) const entityName = "Entity1" var ( entityManager *en.EntityManager secret = []byte("AaB1@CDABCD12341234") ) func addOtpUser(id string, secret []byte, startCounter int64) { entityManager.AddUser(id) otpUser, _ := otp.NewOtpUser(secret, true, false, 10, 1, 100, 2, 0, startCounter) entityManager.AddPropertyToEntity(id, defs.OtpPropertyName, otpUser) } func main() { addOtpUser(entityName, secret, 0) totp, err := otp.NewTotp(secret) if err != nil { fmt.Println("TOTP can't be initialized, Error: ", err) } code, err := totp.Now() if err != nil { fmt.Println("Can't generate TOTP, error: ", err) } else { e, _ := entityManager.GetPropertyAttachedToEntity(entityName, defs.OtpPropertyName) if e == nil { return } entity, ok := e.(*otp.UserInfoOtp) if ok == true { ok, err := entity.VerifyOtpUserCode(code, otp.TotpType) if ok { fmt.Println("TOTP: Entity:", entityName, "Code:", code, "as expected") } else { fmt.Println("TOTP: Entity:", entityName, "Code:", code, "is not as expected, error:", err) } } } }
Output:
type UserInfoOtp ¶
type UserInfoOtp struct { Secret []byte Blocked bool Throttle throtteling // Handle all the throttle parameters BaseHotp *Hotp BaseTotp *Totp }
UserInfoOtp : structure that holds all the properties associated to a user
func NewOtpUser ¶
func NewOtpUser(secret []byte, checkSecretStrength bool, lock bool, cliffLen int32, thrTimeSec time.Duration, autoUnblockSec time.Duration, hotpWindowSize time.Duration, totpWindowSize time.Duration, startCount int64) (*UserInfoOtp, error)
NewOtpUser : generate a new otp user with the given parameters
func NewSimpleOtpUser ¶
func NewSimpleOtpUser(secret []byte, checkSecretStrength bool) (*UserInfoOtp, error)
NewSimpleOtpUser : generate a new otp user with the default parameters
func (*UserInfoOtp) IsEqual ¶
func (u *UserInfoOtp) IsEqual(u1 interface{}) bool
IsEqual : compare 2 OTP structures
func (UserInfoOtp) IsOtpUserBlocked ¶
func (u UserInfoOtp) IsOtpUserBlocked() (bool, error)
IsOtpUserBlocked : check if the given user account is blocked
func (*UserInfoOtp) SetOtpUserBlockedState ¶
func (u *UserInfoOtp) SetOtpUserBlockedState(block bool) error
SetOtpUserBlockedState : set the user account status to the given status
func (UserInfoOtp) String ¶
func (u UserInfoOtp) String() string
func (*UserInfoOtp) VerifyCode ¶
func (u *UserInfoOtp) VerifyCode(code string, otpType TypeOfOtp) (bool, error)
VerifyCode : Verify that the given code is the expected one, if so, increment the internal counter (for hotp) or block the same code (for totp) The upper layer shell take the action to blocl the user (the upper layer can take more information before blockingthe user) the differences between hotp and totp are: the code check and the action if the code was found
func (*UserInfoOtp) VerifyOtpUserCode ¶
func (u *UserInfoOtp) VerifyOtpUserCode(code string, otpType TypeOfOtp) (bool, error)
VerifyOtpUserCode : Verify that a given code is as expected, If the user is blocked, return an error If the code is as expected, the counter code will be incremented (for HOTP) and saved to avoid replay attack (TOTP) If the code dosn't match and the number of consecutive errors pass the Throtlling parameter for this user, the user acount will be blocked till manuel or automatic unblock