mirror of
https://github.com/1f349/twofactor.git
synced 2024-12-22 07:24:12 +00:00
Improved readme - added info about new encryption with cryptoengine
This commit is contained in:
parent
c413e28bb8
commit
6e8770158f
@ -15,6 +15,8 @@ This package implements the RFC 6238 OATH-TOTP algorithm;
|
|||||||
|
|
||||||
* Built-in support for secure crypto keys generation
|
* Built-in support for secure crypto keys generation
|
||||||
|
|
||||||
|
* Built in encryption of the secret keys when converted to bytes, so that they can be safely transmitted over the network, or stored in a DB
|
||||||
|
|
||||||
* Built-in back-off time when a user fails to authenticate more than 3 times
|
* Built-in back-off time when a user fails to authenticate more than 3 times
|
||||||
|
|
||||||
* Bult-in serialization and deserialization to store the one time token struct in a persistence layer
|
* Bult-in serialization and deserialization to store the one time token struct in a persistence layer
|
||||||
@ -32,19 +34,18 @@ This package implements the RFC 6238 OATH-TOTP algorithm;
|
|||||||
|
|
||||||
> **The key crerated is using go crypto random function and it's a cryptographic secret key.**
|
> **The key crerated is using go crypto random function and it's a cryptographic secret key.**
|
||||||
> It needs to be protected against unauthorized access and they cannot be leaked.
|
> It needs to be protected against unauthorized access and they cannot be leaked.
|
||||||
> In addition when shared with the client, the connection should be secured.
|
> In addition when the QR cide is shared with the client, the connection should be secured.
|
||||||
|
|
||||||
The `totp` struct can be easily serialized using the `ToBytes()` function.
|
The `totp` struct can be easily serialized using the `ToBytes()` function.
|
||||||
The bytes can then be stored on a persistent layer. Again the secret key needs to be protected.
|
The bytes can then be stored on a persistent layer. The bytes are encrypted using `cryptoengine` library (NaCl)
|
||||||
You can then retrieve the object back with the function: `TOTPFromBytes`
|
You can then retrieve the object back with the function: `TOTPFromBytes`
|
||||||
|
|
||||||
Again if you trannsfer those bytes via a network connection, this should be a secured one.
|
> You can transfer the bytes securely via a network connection because they are encrypted and authenticated.
|
||||||
|
|
||||||
The struct needs to be stored in a persistent layer becase its values, like last token verification time,
|
The struct needs to be stored in a persistent layer becase its values, like last token verification time,
|
||||||
max user authentication failures, etc.. needs to be preserved.
|
max user authentication failures, etc.. needs to be preserved.
|
||||||
The secret key needs to be preserved too, between the user accound and the user device.
|
The secret key needs to be preserved too, between the user accound and the user device.
|
||||||
The secret key is used to derive tokens.
|
The secret key is used to derive tokens.
|
||||||
Once more the secret key needs to be safely stored.
|
|
||||||
|
|
||||||
### Upcoming features
|
### Upcoming features
|
||||||
|
|
||||||
|
42
totp.go
42
totp.go
@ -13,6 +13,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/sec51/cryptoengine"
|
||||||
"hash"
|
"hash"
|
||||||
"io"
|
"io"
|
||||||
"net/url"
|
"net/url"
|
||||||
@ -334,9 +335,9 @@ func (otp *Totp) QR() ([]byte, error) {
|
|||||||
// Sizes: 4 4 N 8 4 4 N 4 N 4 4 4 8 4
|
// Sizes: 4 4 N 8 4 4 N 4 N 4 4 4 8 4
|
||||||
// Format: |total_bytes|key_size|key|counter|digits|issuer_size|issuer|account_size|account|steps|offset|total_failures|verification_time|hashFunction_type|
|
// Format: |total_bytes|key_size|key|counter|digits|issuer_size|issuer|account_size|account|steps|offset|total_failures|verification_time|hashFunction_type|
|
||||||
// hashFunction_type: 0 = SHA1; 1 = SHA256; 2 = SHA512
|
// hashFunction_type: 0 = SHA1; 1 = SHA256; 2 = SHA512
|
||||||
|
// The data is encrypted using the cryptoengine library (which is a wrapper around the golang NaCl library)
|
||||||
// TODO:
|
// TODO:
|
||||||
// 1- improve sizes. For instance the hashFunction_type could be a short.
|
// 1- improve sizes. For instance the hashFunction_type could be a short.
|
||||||
// 2- Encrypt the key, in case it's transferred in the network unsafely
|
|
||||||
func (otp *Totp) ToBytes() ([]byte, error) {
|
func (otp *Totp) ToBytes() ([]byte, error) {
|
||||||
|
|
||||||
// check Totp initialization
|
// check Totp initialization
|
||||||
@ -448,16 +449,45 @@ func (otp *Totp) ToBytes() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//fmt.Println("Total bytes", len(buffer.Bytes()))
|
// encrypt the TOTP bytes
|
||||||
return buffer.Bytes(), nil
|
engine, err := cryptoengine.InitCryptoEngine(otp.issuer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// encrypt the bytes
|
||||||
|
message, err := engine.NewEncryptedMessage(buffer.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return message.ToBytes()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TOTPFromBytes converts a byte array to a totp object
|
// TOTPFromBytes converts a byte array to a totp object
|
||||||
// it stores the state of the TOTP object, like the key, the current counter, the client offset,
|
// it stores the state of the TOTP object, like the key, the current counter, the client offset,
|
||||||
// the total amount of verification failures and the last time a verification happened
|
// the total amount of verification failures and the last time a verification happened
|
||||||
func TOTPFromBytes(data []byte) (*Totp, error) {
|
func TOTPFromBytes(encryptedMessage []byte, issuer string) (*Totp, error) {
|
||||||
// fmt.Println("Bytes", len(data))
|
|
||||||
|
// parse the data into a message
|
||||||
|
message, err := cryptoengine.MessageFromBytes(encryptedMessage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// init the cryptoengine
|
||||||
|
engine, err := cryptoengine.InitCryptoEngine(issuer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// decrypt the message
|
||||||
|
data, err := engine.Decrypt(message, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// new reader
|
// new reader
|
||||||
reader := bytes.NewReader(data)
|
reader := bytes.NewReader(data)
|
||||||
|
|
||||||
@ -466,7 +496,7 @@ func TOTPFromBytes(data []byte) (*Totp, error) {
|
|||||||
|
|
||||||
// get the lenght
|
// get the lenght
|
||||||
lenght := make([]byte, 4)
|
lenght := make([]byte, 4)
|
||||||
_, err := reader.Read(lenght) // read the 4 bytes for the total lenght
|
_, err = reader.Read(lenght) // read the 4 bytes for the total lenght
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
return otp, err
|
return otp, err
|
||||||
}
|
}
|
||||||
|
@ -166,7 +166,7 @@ func TestVerificationFailures(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
restoredOtp, err := TOTPFromBytes(data)
|
restoredOtp, err := TOTPFromBytes(data, otp.issuer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -235,7 +235,7 @@ func TestSerialization(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert it back from bytes to TOTP
|
// Convert it back from bytes to TOTP
|
||||||
deserializedOTP, err := TOTPFromBytes(otpData)
|
deserializedOTP, err := TOTPFromBytes(otpData, otp.issuer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user