Improved readme - added info about new encryption with cryptoengine

This commit is contained in:
silenteh 2015-08-05 23:53:06 +02:00
parent c413e28bb8
commit 6e8770158f
3 changed files with 43 additions and 12 deletions

View File

@ -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
View File

@ -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
} }

View File

@ -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)
} }