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 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
|
||||
|
||||
* 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.**
|
||||
> 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 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`
|
||||
|
||||
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,
|
||||
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 is used to derive tokens.
|
||||
Once more the secret key needs to be safely stored.
|
||||
|
||||
### Upcoming features
|
||||
|
||||
|
42
totp.go
42
totp.go
@ -13,6 +13,7 @@ import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/sec51/cryptoengine"
|
||||
"hash"
|
||||
"io"
|
||||
"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
|
||||
// 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
|
||||
// The data is encrypted using the cryptoengine library (which is a wrapper around the golang NaCl library)
|
||||
// TODO:
|
||||
// 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) {
|
||||
|
||||
// check Totp initialization
|
||||
@ -448,16 +449,45 @@ func (otp *Totp) ToBytes() ([]byte, error) {
|
||||
}
|
||||
}
|
||||
|
||||
//fmt.Println("Total bytes", len(buffer.Bytes()))
|
||||
return buffer.Bytes(), nil
|
||||
// encrypt the TOTP bytes
|
||||
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
|
||||
// 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
|
||||
func TOTPFromBytes(data []byte) (*Totp, error) {
|
||||
// fmt.Println("Bytes", len(data))
|
||||
func TOTPFromBytes(encryptedMessage []byte, issuer string) (*Totp, error) {
|
||||
|
||||
// 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
|
||||
reader := bytes.NewReader(data)
|
||||
|
||||
@ -466,7 +496,7 @@ func TOTPFromBytes(data []byte) (*Totp, error) {
|
||||
|
||||
// get the lenght
|
||||
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 {
|
||||
return otp, err
|
||||
}
|
||||
|
@ -166,7 +166,7 @@ func TestVerificationFailures(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
restoredOtp, err := TOTPFromBytes(data)
|
||||
restoredOtp, err := TOTPFromBytes(data, otp.issuer)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -235,7 +235,7 @@ func TestSerialization(t *testing.T) {
|
||||
}
|
||||
|
||||
// Convert it back from bytes to TOTP
|
||||
deserializedOTP, err := TOTPFromBytes(otpData)
|
||||
deserializedOTP, err := TOTPFromBytes(otpData, otp.issuer)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user