Commits

Eric Roshan Eisner committed 1aaccee Draft

add computationally intensive password utilities

This not only bakes in a bunch of cycles of pbkdf2, but uses the existing
random iv as the salt.

The pbkdf2 implementation is ripped directly out of the go crypto libraries.

  • Participants
  • Parent commits b6cf9ea

Comments (0)

Files changed (4)

File bytestream.go

 import (
 	"bytes"
 	"compress/zlib"
+	"crypto/aes"
 	"crypto/cipher"
 	"crypto/rand"
 	"crypto/sha1"
 	return data, nil
 }
 
-func CryptoEncodeStream(in []byte, c cipher.Block) []byte {
+func cryptoEncode(in []byte, c cipher.Block, iv []byte) []byte {
 	blocksize := c.BlockSize()
 
 	// Encode and pad to the blocksize
 		data = append(data, 0)
 	}
 
-	// Random iv
-	iv := make([]byte, blocksize)
-	rand.Read(iv)
-
 	crypter := cipher.NewCBCEncrypter(c, iv)
 	crypter.CryptBlocks(data, data)
 	return append(iv, data...)
 }
 
+func CryptoEncodeStream(in []byte, c cipher.Block) []byte {
+	// Random iv
+	iv := make([]byte, c.BlockSize())
+	rand.Read(iv)
+	return cryptoEncode(in, c, iv)
+}
+
 func CryptoDecodeStream(data []byte, c cipher.Block) ([]byte, error) {
 	blocksize := c.BlockSize()
 	if len(data) < 2*blocksize {
 
 	return DecodeStream(enc)
 }
+
+func generateKey(passphrase, salt []byte) []byte {
+	return pbkdf2(passphrase, salt, 1<<15, 32, sha1.New)
+}
+
+func PassphraseEncodeStream(in, passphrase []byte) []byte {
+	// Random iv
+	iv := make([]byte, aes.BlockSize)
+	rand.Read(iv)
+	// Create key from passphrase, reusing iv as salt.
+	c, _ := aes.NewCipher(generateKey(passphrase, iv))
+	return cryptoEncode(in, c, iv)
+}
+
+func PassphraseDecodeStream(data, passphrase []byte) ([]byte, error) {
+	const blocksize = aes.BlockSize
+	if len(data) < blocksize {
+		return nil, errors.New("data too short to decrypt anything")
+	}
+	iv := data[:blocksize]
+	c, _ := aes.NewCipher(generateKey(passphrase, iv))
+	return CryptoDecodeStream(data, c)
+}

File bytestream_test.go

 	cryptoRoundTrip(t, "short")
 	cryptoRoundTrip(t, compressible)
 }
+
+func passphraseRoundTrip(t *testing.T, s string) {
+	passphrase := []byte("setec astronomy")
+	start := []byte(s)
+	enc := PassphraseEncodeStream(start, passphrase)
+	end, err := PassphraseDecodeStream(enc, passphrase)
+	if err != nil {
+		t.Errorf("Error: %v", err)
+	}
+	if !bytes.Equal(start, end) {
+		t.Errorf("Input and output differ: %s, %s", start, end)
+	}
+}
+
+func TestPassphraseRoundTrip(t *testing.T) {
+	// Limited tests, as this is intentionally computationally expensive.
+	if testing.Short() {
+		return
+	}
+	passphraseRoundTrip(t, "short")
+}
 	}
 	return CryptoDecodeStream(stream, c)
 }
+
+// Hide the data encrypted with this passphrase
+func PassphraseHideData(bh BitHider, data, passphrase []byte) error {
+	stream := PassphraseEncodeStream(data, passphrase)
+	return bh.Embed(stream)
+}
+
+// Retrieve data hidden here under this passphrase, if any.
+func PassphraseFindData(bh BitHider, passphrase []byte) ([]byte, error) {
+	stream, err := bh.Reveal()
+	if err != nil {
+		return nil, err
+	}
+	return PassphraseDecodeStream(stream, passphrase)
+}
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package pbkdf2 implements the key derivation function PBKDF2 as defined in RFC
+2898 / PKCS #5 v2.0.
+
+A key derivation function is useful when encrypting data based on a password
+or any other not-fully-random data. It uses a pseudorandom function to derive
+a secure encryption key based on the password.
+
+While v2.0 of the standard defines only one pseudorandom function to use,
+HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved
+Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To
+choose, you can pass the `New` functions from the different SHA packages to
+pbkdf2.Key.
+*/
+package stego
+
+import (
+	"crypto/hmac"
+	"hash"
+)
+
+// pbkdf2 derives a key from the password, salt and iteration count, returning a
+// []byte of length keylen that can be used as cryptographic key. The key is
+// derived based on the method described as PBKDF2 with the HMAC variant using
+// the supplied hash function.
+//
+// For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you
+// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by
+// doing:
+//
+// 	dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New)
+//
+// Remember to get a good random salt. At least 8 bytes is recommended by the
+// RFC.
+//
+// Using a higher iteration count will increase the cost of an exhaustive
+// search but will also make derivation proportionally slower.
+func pbkdf2(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte {
+	prf := hmac.New(h, password)
+	hashLen := prf.Size()
+	numBlocks := (keyLen + hashLen - 1) / hashLen
+
+	var buf [4]byte
+	dk := make([]byte, 0, numBlocks*hashLen)
+	U := make([]byte, hashLen)
+	for block := 1; block <= numBlocks; block++ {
+		// N.B.: || means concatenation, ^ means XOR
+		// for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter
+		// U_1 = PRF(password, salt || uint(i))
+		prf.Reset()
+		prf.Write(salt)
+		buf[0] = byte(block >> 24)
+		buf[1] = byte(block >> 16)
+		buf[2] = byte(block >> 8)
+		buf[3] = byte(block)
+		prf.Write(buf[:4])
+		dk = prf.Sum(dk)
+		T := dk[len(dk)-hashLen:]
+		copy(U, T)
+
+		// U_n = PRF(password, U_(n-1))
+		for n := 2; n <= iter; n++ {
+			prf.Reset()
+			prf.Write(U)
+			U = U[:0]
+			U = prf.Sum(U)
+			for x := range U {
+				T[x] ^= U[x]
+			}
+		}
+	}
+	return dk[:keyLen]
+}