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.

Comments (0)

Files changed (4)

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

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]
+}