libDiscolibdisco is a Golang library which can be used to create secure connections. It is based on the STROBE protocol framework [specification]. Symmetric cryptographic primitives rely on SHA-3 permutation, and asymmetric cryptographic primitives on X25519 and ed25519. STROBE defines a range of cryptographic protocols to integrate with IoT devices. In order to reduce processing, energy and memory requirements, it only uses Keccak (SHA-3) to encrypt and authenticate messages [paper]. |
Outline
For some reason, security is often an afterthought in creating IoT infrastructures, and the implementation of cryptography, especially, is often poorly defined. Here is my wi-fi kettle which uses Telnet — an insecure protocol — to connect to:
But it doesn’t have to be like this, as there are a whole lot of standards out there which can simply the life of a developer. With the STROBE protocol framework [specification] we implement a single cryptographic primitive for symmetric key encryption. Unlike Openssl — which tries to implement so many protocols and methods — the STROBE library is fairly small and can fit on most IoT devices. Along with this, it builds onto a TLS-like framework, and makes sure that all of the messages are linked to previous ones.
There’s a whole lot of libraries out there that developers can use for the STROBE protocol framework. libdisco, for example, is a Golang library and is based on the STROBE protocol framework. The symmetric cryptographic primitives rely on SHA-3 permutation (Keccak), and asymmetric cryptographic primitives on X25519 and ed25519. STROBE thus defines a range of cryptographic protocols to integrate with IoT devices. In order to reduce processing, energy and memory requirements, it only uses Keccak (SHA-3) to encrypt and authenticate messages [paper].
The underlying cryptography method is the sponge function and which provides us with permutation-based cryptography. With a permutation we take a given input and then permutate it to produce an output. We do this with AES, where a given input will always give a certain output. SHA-3 (or Keccak) uses a sponge function to its permutation. Where AES-128 has 10 rounds, Keccak-f[1600] has 24 rounds and has an input/output size of 1600 bits. The great thing about this sponge function is that we can store states within it, and where each new state is dependent on previous states.
SHA-3 was known as Keccak and is a hash function designed by Guido Bertoni, Joan Daemen, Michaël Peeters, and Gilles Van Assche. MD5 and SHA-0 have been shown to be susceptible to attacks, along with theoretical attacks on SHA-1. NIST thus defined there was a need for a new hashing method which did not use the existing methods for hashing, and a competition for competing algorithms.
In October 2012, Keccak won the NIST hash function competition and is proposed as the SHA-3 standard. It should be noted that it is not replacement SHA-2, which is currently a secure method. Overall Keccak uses the sponge construction where the message blocks are XORed into the initial bits of the state, and then inevitably permuted.
The sponge function takes a simple function f and involves a number of stages, and where we create a fixed output (dependent on the bit length of the hash function). Simple operations of XOR, AND, and bit shifts are used, and which leads to a fast generation of the hash function:
The f permutation function takes a variable-length input and produces an arbitrary output length. A is the bit rate, and each f function operates on b bits, and where a capacity is defined as c = b — r.
The SHAKE method is useful as it can be used to create a hash method of a variable length. For the 128-bit version will produce a hash value is 32 hex characters.
Coding
The following is some sample Go code:
package main import( "github.com/mimoo/disco/libdisco" "fmt" "crypto/sha256" "encoding/hex" "crypto/rand" "os" "golang.org/x/crypto/curve25519" ) func getSHA256(s string) []byte { hasher := sha256.New() hasher.Write([]byte(s)) key:=hasher.Sum(nil) return key } func randomHex(n int) (string) { bytes := make([]byte, n) rand.Read(bytes) return hex.EncodeToString(bytes) } func dh(privKey [32]byte, publicKey [32]byte) (shared [32]byte) { curve25519.ScalarMult(&shared,&privKey, &publicKey) return } func main() { password:="Hello" message:="A message" argCount := len(os.Args[1:]) if (argCount>0) {message = string(os.Args[1])} if (argCount>1) {password = string(os.Args[2])} input := []byte(message) fmt.Printf("Message: %s\n",input) fmt.Printf("Password: %s\n",password) fmt.Printf("\n\n=== Digest ===\n") digest := libdisco.Hash(input, 32) fmt.Printf("Digest (message): %x\n",digest) fmt.Printf("\n\n=== Encryption ===\n") key := getSHA256(password) ciphertext := libdisco.Encrypt(key, input) decrypted, _ := libdisco.Decrypt(key, ciphertext) fmt.Printf("\nCipher: %x\nDecrypted: %s\n",ciphertext,decrypted) fmt.Printf("\n\n=== Integrity Check ===\n") payload := libdisco.ProtectIntegrity(key, input) fmt.Printf("%x",payload) retrievedMessage, err := libdisco.VerifyIntegrity(key, payload) if err != nil { fmt.Printf("Message has been tampered with") } fmt.Printf("\nRetrived: %s",string(retrievedMessage)) fmt.Printf("\n\n=== Key generation ===\n") keyinput, _ := hex.DecodeString(randomHex(16)) keys := libdisco.DeriveKeys(keyinput, 64) key1 := keys[:32] key2 := keys[32:] fmt.Printf("\nKey1: %x\nKey2: %x",key1,key2) fmt.Printf("\n\n=== Key pair (X25519 key pair) ===\n") key_pair:=libdisco.GenerateKeypair(nil) fmt.Printf("\nBob Public Key: %x\nPrivate Key: %x",key_pair.PublicKey,key_pair.PrivateKey) key_pair2:=libdisco.GenerateKeypair(nil) fmt.Printf("\nAlice Public Key: %x\nPrivate Key: %x",key_pair2.PublicKey,key_pair2.PrivateKey) sharedkeyBob:=dh(key_pair.PrivateKey,key_pair2.PublicKey) sharedkeyAlice:=dh(key_pair2.PrivateKey,key_pair.PublicKey) fmt.Printf("\nShared key (Bob): %x\n",sharedkeyBob) fmt.Printf("\nShared key (Alice): %x\n",sharedkeyAlice) }
A sample run is:
Message: Hello Password: qwerty === Digest === Digest (message): 1c38d06d9edd0e3271dd11cf39721630d48e9411c959b60e250c9a003629832e === Encryption === Cipher: fe04f8a8574c60506b30b84034d1742538e54d735140d8a9d3994c5d7ebd0c93e966cdb71f4777726f9e4d8618 Decrypted: Hello === Integrity Check === 48656c6c6f2618278cf5d9c679620caf534b65eeef Retrived: Hello === Key generation === Key1: 4815c5cbbd27c1589098b99983b4cfca6496b7235ef83fd34198b6302a4e03f6 Key2: c01863cfdc4a1a61c30735c62d9a366f1e518681dba7c7df1075a3941f25cb18 === Key pair (X25519 key pair) === Bob Public Key: d43ef61b11c3d8b72e53483f1ca8d76cf803ebe35368db1b0b3c41b881e32845 Private Key: 11af0d74ad7e9c200138ec1a9b2ed2d3a6e55ff52b70663162b6ad6566287998 Alice Public Key: 18db6d38162a33443c56cffe4c1d100d949f0098dc234b4691668fd4b384075b Private Key: 1e84885188e55366972da26d315f10e09de0c08d249b6b50b6d651a01a02735f Shared key (Bob): ac0a6ab7a68358f92737fa631c179aeabe0ca082e8d7c0d4bda2809e9e1bf458 Shared key (Alice): ac0a6ab7a68358f92737fa631c179aeabe0ca082e8d7c0d4bda2809e9e1bf458