From 6e8770158f7d6adfe96a9cbac484015d3080fbee Mon Sep 17 00:00:00 2001 From: silenteh Date: Wed, 5 Aug 2015 23:53:06 +0200 Subject: [PATCH] Improved readme - added info about new encryption with cryptoengine --- README.md | 9 +++++---- totp.go | 42 ++++++++++++++++++++++++++++++++++++------ totp_test.go | 4 ++-- 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index c44bd58..512fd7a 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/totp.go b/totp.go index 2434e77..c258257 100644 --- a/totp.go +++ b/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 } diff --git a/totp_test.go b/totp_test.go index cc87d16..c362040 100644 --- a/totp_test.go +++ b/totp_test.go @@ -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) }