x/lib/ibe: Implementation of Fujisaki-Okamoto transformation for BB-IBE

THIS IS DAVID WU's CL.

I am simply uploading it as part of a multipart
patchset that fixes all the dependant code.

MultiPart: 1/2
Change-Id: I9c005af809ee5e338247b3a411d4ed053f427d2a
diff --git a/ibe/.api b/ibe/.api
index d2dd141..e561c43 100644
--- a/ibe/.api
+++ b/ibe/.api
@@ -1,5 +1,3 @@
-pkg ibe, const CiphertextSize ideal-int
-pkg ibe, const PlaintextSize ideal-int
 pkg ibe, func MarshalParams(Params) ([]byte, error)
 pkg ibe, func MarshalPrivateKey(PrivateKey) ([]byte, error)
 pkg ibe, func SetupBB1() (Master, error)
@@ -8,7 +6,9 @@
 pkg ibe, type Master interface { Extract, Params }
 pkg ibe, type Master interface, Extract(string) (PrivateKey, error)
 pkg ibe, type Master interface, Params() Params
-pkg ibe, type Params interface { Encrypt }
+pkg ibe, type Params interface { CiphertextOverhead, Encrypt }
+pkg ibe, type Params interface, CiphertextOverhead() int
 pkg ibe, type Params interface, Encrypt(string, []byte, []byte) error
-pkg ibe, type PrivateKey interface { Decrypt }
+pkg ibe, type PrivateKey interface { Decrypt, Params }
 pkg ibe, type PrivateKey interface, Decrypt([]byte, []byte) error
+pkg ibe, type PrivateKey interface, Params() Params
diff --git a/ibe/.godepcop b/ibe/.godepcop
index e1e57c2..adb8771 100644
--- a/ibe/.godepcop
+++ b/ibe/.godepcop
@@ -1,3 +1,6 @@
 <godepcop>
   <pkg allow="golang.org/x/crypto/bn256"/>
+  <pkg allow="golang.org/x/crypto/nacl/secretbox"/>
+  <pkg allow="golang.org/x/crypto/poly1305"/>
+  <pkg allow="golang.org/x/crypto/salsa20/salsa"/>
 </godepcop>
diff --git a/ibe/bb1.go b/ibe/bb1.go
index 2f1351a..5e1c8d6 100644
--- a/ibe/bb1.go
+++ b/ibe/bb1.go
@@ -3,15 +3,22 @@
 // license that can be found in the LICENSE file.
 
 // This file defines an implementation of the IBE interfaces using the
-// Boneh-Boyen scheme. The paper defining this algorithm (see comments for
+// Boneh-Boyen scheme. The Fujisaki-Okamoto transformation is applied to
+// obtain CCA2-security. The paper defining this algorithm (see comments for
 // SetupBB1) uses multiplicative groups while the bn256 package used in the
 // implementation here defines an additive group. The comments follow the
 // notation in the paper while the code uses the bn256 library. For example,
 // g^i corresponds to G1.ScalarBaseMult(i).
 
+// The ciphertexts in the resulting CCA2-secure IBE scheme will consist of
+// two parts: an IBE encryption of a symmetric key (called the 'kem' - key
+// encapsulation mechanism) and a symmetric encryption of the payload
+// (called the 'dem' - data encapsulation mechanism).
+
 package ibe
 
 import (
+	"bytes"
 	"crypto/rand"
 	"crypto/sha256"
 	"errors"
@@ -19,6 +26,7 @@
 	"math/big"
 
 	"golang.org/x/crypto/bn256"
+	"golang.org/x/crypto/nacl/secretbox"
 )
 
 var errBadCiphertext = errors.New("invalid ciphertext")
@@ -27,6 +35,24 @@
 	marshaledG1Size = 2 * 32
 	marshaledG2Size = 4 * 32
 	marshaledGTSize = 12 * 32
+	nonceSize       = 24
+	encKeySize      = sha256.Size                    // size of encapsulated key = 32 bytes
+	kemSize         = encKeySize + 2*marshaledG1Size // 160 bytes
+)
+
+// In the construction, we require several independent hash functions for
+// hashing the different quantities (in the security reduction, these
+// hash functions are modeled as random oracles). In the implementation,
+// we use SHA-256 as the underlying hash function, but concatenate a prefix
+// to differentiate the different hash functions. For example, we have
+// H1(x) = SHA256(00 || x), H2(x) = SHA256(01 || x), and so on. If we model
+// SHA256 as a random oracle, then H1, H2, ... are also independent random
+// oracles.
+var (
+	idPrefix  = [1]byte{0x00}
+	kemPrefix = [1]byte{0x01}
+	demPrefix = [1]byte{0x02}
+	ibePrefix = [1]byte{0x03}
 )
 
 // Setup creates an ibe.Master based on the BB1 scheme described in "Efficient
@@ -34,6 +60,13 @@
 // Xavier Boyen (http://crypto.stanford.edu/~dabo/papers/bbibe.pdf).
 //
 // Specifically, Section 4.3 of the paper is implemented.
+//
+// In addition, we apply the Fujisaki-Okamoto transformation to the BB-IBE
+// scheme (http://link.springer.com/chapter/10.1007%2F3-540-48405-1_34)
+// in order to obtain CCA2-security (in the random oracle model). The resulting
+// scheme is a CCA2-secure hybrid encryption scheme where BB-IBE is used to
+// encrypt a nonce used to derive a symmetric key, and NaCl/secretbox is used
+// to encrypt the data under the symmetric key.
 func SetupBB1() (Master, error) {
 	var (
 		m     = &bb1master{params: new(bb1params)}
@@ -91,7 +124,7 @@
 		g1Hat = &(m.params.g1Hat)
 		g0Hat = &(m.g0Hat)
 		hHat  = &(m.params.hHat)
-		i     = id2bignum(id)
+		i     = val2bignum(idPrefix, []byte(id))
 	)
 	// ret.d0 = g0Hat * (g1Hat^i * hHat)^r
 	d0.ScalarMult(g1Hat, i)
@@ -110,74 +143,168 @@
 	v                 *bn256.GT
 }
 
-func (e *bb1params) Encrypt(id string, m, C []byte) error {
-	if err := checkSizes(m, C); err != nil {
-		return err
+// Helper method that checks that the ciphertext slice for a given message has
+// the correct size: len(C) = len(m) + CiphertextOverhead()
+func (e *bb1params) checkSizes(m, C []byte) error {
+	if msize, Csize := len(m), len(C); Csize != msize+e.CiphertextOverhead() {
+		return fmt.Errorf("provided plaintext and ciphertext are of sizes (%d, %d), ciphertext size should be %d", msize, Csize, e.CiphertextOverhead())
 	}
-	s, err := random()
-	if err != nil {
-		return err
+	return nil
+}
+
+// Helper method that constructs the first two components of the BB-IBE
+// ciphertext. These components are re-computed during decryption to verify
+// proper generation of the ciphertext. This is the Fujisaki-Okamoto transformation.
+// The ciphertext C that is passed in must have size exactly
+// encKeySize + marshaledG1Size.
+func (e *bb1params) encapsulateKeyStart(sigma *[encKeySize]byte, s *big.Int, C []byte) error {
+	if len(C) != encKeySize+marshaledG1Size {
+		return fmt.Errorf("provided buffer has size %d, must be %d", len(C), encKeySize+marshaledG1Size)
 	}
 
 	var (
 		vs    bn256.GT
 		tmpG1 bn256.G1
-		// Ciphertext C = (A, B, C1)
-		A  = C[0:len(m)]
-		B  = C[len(m) : len(m)+marshaledG1Size]
-		C1 = C[len(m)+marshaledG1Size:]
+		// Ciphertext C = (A, B, C1) - this method computes the first two components
+		A = C[0:encKeySize]
+		B = C[encKeySize : encKeySize+marshaledG1Size]
 	)
 	vs.ScalarMult(e.v, s)
-	pad := sha256.Sum256(vs.Marshal())
-	// A = m ⊕ H(v^s)
-	for i := range m {
-		A[i] = m[i] ^ pad[i]
+	pad := hashval(ibePrefix, vs.Marshal())
+	// A = sigma ⊕ H(v^s)
+	for i := range sigma {
+		A[i] = sigma[i] ^ pad[i]
 	}
 	// B = g^s
 	if err := marshalG1(B, tmpG1.ScalarBaseMult(s)); err != nil {
 		return err
 	}
+
+	return nil
+}
+
+// Compute the symmetric key used for data encapsulation (used to encrypt the payload
+// during Encrypt and for verification during Decrypt)
+func computeDemKey(sigma *[encKeySize]byte) *[encKeySize]byte {
+	return hashval(demPrefix, sigma[:])
+}
+
+// Computes the randomness used to encrypt the symmetric key. The randomness is given
+// by H(sigma || m).
+func computeKemRandomness(sigma *[encKeySize]byte, m []byte) *big.Int {
+	// Note: append(sigma[:], m) will allocate a new slice and copy the data
+	// since sigma is a fixed-size array (not a slice with larger capacity). However,
+	// if this changes in the future, this should be changed to ensure thread-safety.
+	return val2bignum(kemPrefix, append(sigma[:], m...))
+}
+
+func (e *bb1params) Encrypt(id string, m, C []byte) error {
+	if err := e.checkSizes(m, C); err != nil {
+		return err
+	}
+
+	// Choose a random nonce for the Fujisaki-Okamoto transform
+	var sigma [encKeySize]byte
+	if _, err := rand.Read(sigma[:]); err != nil {
+		return err
+	}
+
+	var (
+		s      = computeKemRandomness(&sigma, m) // H_1(sigma, m)
+		symKey = computeDemKey(&sigma)           // H_2(sigma)
+
+		tmpG1 bn256.G1
+		// Ciphertext C = (kem, dem)
+		kem = C[0:kemSize]
+		dem = C[kemSize:]
+	)
+
+	// kem = (A, B, C1). Invoke encasulateKeyStart to compute (A, B)
+	if err := e.encapsulateKeyStart(&sigma, s, kem[0:encKeySize+marshaledG1Size]); err != nil {
+		return err
+	}
+	C1 := kem[encKeySize+marshaledG1Size:]
+
 	// C1 = (g1^H(id) h)^s
-	tmpG1.ScalarMult(&e.g1, id2bignum(id))
+	tmpG1.ScalarMult(&e.g1, val2bignum(idPrefix, []byte(id)))
 	tmpG1.Add(&tmpG1, &e.h)
 	tmpG1.ScalarMult(&tmpG1, s)
 	if err := marshalG1(C1, &tmpG1); err != nil {
 		return err
 	}
+
+	// Nonce for symmetric ecnryption can be all-zeroes string, because
+	// we only require one-time semantic security of the underlying symmetric
+	// scheme in the Fujisaki-Okamoto transformation.
+	var nonce [nonceSize]byte
+	if tmp := secretbox.Seal(dem[0:0], m, &nonce, symKey); &tmp[0] != &dem[0] {
+		return fmt.Errorf("output of Seal has unexpected length: expected %d, received %d", len(dem), len(tmp))
+	}
+
 	return nil
 }
 
+func (e *bb1params) CiphertextOverhead() int {
+	return kemSize + secretbox.Overhead
+}
+
 type bb1PrivateKey struct {
 	params *bb1params // public parameters
 	d0, d1 bn256.G2
 }
 
 func (k *bb1PrivateKey) Decrypt(C, m []byte) error {
-	if err := checkSizes(m, C); err != nil {
+	if err := k.params.checkSizes(m, C); err != nil {
 		return err
 	}
 	var (
-		A     = C[0:len(m)]
+		A     = C[0:encKeySize]
 		B, C1 bn256.G1
+		D     = C[kemSize:]
 	)
-	if _, ok := B.Unmarshal(C[len(m) : len(m)+marshaledG1Size]); !ok {
+	if _, ok := B.Unmarshal(C[encKeySize : encKeySize+marshaledG1Size]); !ok {
 		return errBadCiphertext
 	}
-	if _, ok := C1.Unmarshal(C[len(m)+marshaledG1Size:]); !ok {
+	if _, ok := C1.Unmarshal(C[encKeySize+marshaledG1Size : encKeySize+2*marshaledG1Size]); !ok {
 		return errBadCiphertext
 	}
-	// M = A ⊕ H(e(B, d0)/e(C1,d1))
+	// sigma = A ⊕ H(e(B, d0)/e(C1,d1))
 	var (
 		numerator   = bn256.Pair(&B, &k.d0)
 		denominator = bn256.Pair(&C1, &k.d1)
-		hash        = sha256.Sum256(numerator.Add(numerator, denominator.Neg(denominator)).Marshal())
+		hash        = hashval(ibePrefix, numerator.Add(numerator, denominator.Neg(denominator)).Marshal())
 	)
-	for i := range m {
-		m[i] = A[i] ^ hash[i]
+	var sigma [encKeySize]byte
+	for i := range sigma {
+		sigma[i] = A[i] ^ hash[i]
+	}
+
+	symKey := computeDemKey(&sigma)
+
+	var nonce [nonceSize]byte
+	if tmp, success := secretbox.Open(m[0:0], D, &nonce, symKey); !success || (&tmp[0] != &m[0]) {
+		return errBadCiphertext
+	}
+
+	// Check that consistent randomness was used to encrypt the symmetric key. It suffices
+	// to check that the first two components of the KEM portion matches, since given the
+	// message and the secret identity key, the first two components uniquely determine the third.
+
+	// First, derive the randomness used for the KEM.
+	s := computeKemRandomness(&sigma, m)
+
+	// Check the first two components
+	var kemChkBuf [encKeySize + marshaledG1Size]byte
+	k.params.encapsulateKeyStart(&sigma, s, kemChkBuf[:])
+
+	if !bytes.Equal(kemChkBuf[:], C[0:encKeySize+marshaledG1Size]) {
+		return errBadCiphertext
 	}
 	return nil
 }
 
+func (k *bb1PrivateKey) Params() Params { return k.params }
+
 // random returns a positive integer in the range [1, bn256.Order)
 // (denoted by Zp in http://crypto.stanford.edu/~dabo/papers/bbibe.pdf).
 //
@@ -197,9 +324,23 @@
 	}
 }
 
-func id2bignum(id string) *big.Int {
-	h := sha256.Sum256([]byte(id))
-	k := new(big.Int).SetBytes(h[:])
+// Hash a particular message with the given prefix. Specifically, this computes
+// SHA256(prefix || data) where prefix is a fixed-length string.
+func hashval(prefix [1]byte, data []byte) *[sha256.Size]byte {
+	hasher := sha256.New()
+	hasher.Write(prefix[:])
+	hasher.Write(data)
+
+	var ret [sha256.Size]byte
+	copy(ret[:], hasher.Sum(nil))
+
+	return &ret
+}
+
+// Hashes a value SHA256(prefix || data) where prefix is a fixed-length
+// string.  The hashed value is then converted  to a value modulo the group order.
+func val2bignum(prefix [1]byte, data []byte) *big.Int {
+	k := new(big.Int).SetBytes(hashval(prefix, data)[:])
 	return k.Mod(k, bn256.Order)
 }
 
@@ -212,10 +353,3 @@
 	copy(dst, src)
 	return nil
 }
-
-func checkSizes(m, C []byte) error {
-	if msize, Csize := len(m), len(C); msize != PlaintextSize || Csize != CiphertextSize {
-		return fmt.Errorf("provided plaintext and ciphertext are of sizes (%d, %d), want (%d, %d)", msize, Csize, PlaintextSize, CiphertextSize)
-	}
-	return nil
-}
diff --git a/ibe/bb1_test.go b/ibe/bb1_test.go
index b6218c0..fd13fa7 100644
--- a/ibe/bb1_test.go
+++ b/ibe/bb1_test.go
@@ -11,6 +11,24 @@
 	"testing"
 )
 
+func TestHashVal(t *testing.T) {
+	var (
+		prefix0 = [1]byte{0x00}
+		prefix1 = [1]byte{0x01}
+		msg0    = []byte("message 0")
+		msg1    = []byte("message 1")
+	)
+
+	// Hashes of distinct messages (with the same prefix) should be different
+	if bytes.Equal(hashval(prefix0, msg0)[:], hashval(prefix0, msg1)[:]) {
+		t.Errorf("Hashing two distinct values produced same output")
+	}
+	// Hashes of identical messages with different prefixes should be different
+	if bytes.Equal(hashval(prefix0, msg0)[:], hashval(prefix1, msg1)[:]) {
+		t.Errorf("Hashing two messages with different prefixes produced same output")
+	}
+}
+
 func TestBB1Correctness(t *testing.T) {
 	master, err := SetupBB1()
 	if err != nil {
@@ -38,11 +56,10 @@
 
 	// Encrypt
 	m := []byte("AThirtyTwoBytePieceOfTextThisIs!")
-	C := make([]byte, CiphertextSize)
-	C2 := make([]byte, CiphertextSize)
-	if msize := len(m); msize != PlaintextSize {
-		t.Fatalf("Test string must be %d bytes, not %d", PlaintextSize, msize)
-	}
+	overhead := master.Params().CiphertextOverhead()
+	C := make([]byte, len(m)+overhead)
+	C2 := make([]byte, len(m)+overhead)
+
 	if err := master.Params().Encrypt(alice, m, C); err != nil {
 		t.Fatal(err)
 	}
@@ -55,7 +72,7 @@
 
 	// Decrypt
 	decrypt := func(sk PrivateKey) ([]byte, error) {
-		ret := make([]byte, PlaintextSize)
+		ret := make([]byte, len(C)-overhead)
 		if err := sk.Decrypt(C, ret); err != nil {
 			return nil, err
 		}
@@ -67,17 +84,52 @@
 	if decrypted, err := decrypt(aliceSK2); err != nil || !bytes.Equal(decrypted, m) {
 		t.Errorf("Got (%v, %v), want (%v, nil)", decrypted, err, m[:])
 	}
-	if decrypted, _ := decrypt(bobSK); bytes.Equal(decrypted[:], m[:]) {
+	if _, err := decrypt(bobSK); err == nil {
 		t.Errorf("Decrypted message with a different PrivateKey")
 	}
 }
 
-var (
-	bb1   Master
-	bb1SK PrivateKey
+// Applying the Fujisaki-Okamoto transformation to the BB1 IBE
+// scheme yields a CCA2-secure encryption scheme. Since CCA2-security
+// implies non-malleability, we verify that a tampered ciphertext
+// does not properly decrypt in this test case.
+func TestBB1NonMalleability(t *testing.T) {
+	master, err := SetupBB1()
+	if err != nil {
+		t.Fatal(err)
+	}
+	const alice = "alice"
+	aliceSK, err := master.Extract(alice)
+	if err != nil {
+		t.Fatal(err)
+	}
 
-	benchmarkm = make([]byte, PlaintextSize)
-	bb1C       = make([]byte, CiphertextSize)
+	m := []byte("01234567899876543210123456789012")
+	overhead := master.Params().CiphertextOverhead()
+	C := make([]byte, len(m)+overhead)
+
+	if err := master.Params().Encrypt(alice, m, C); err != nil {
+		t.Fatal(err)
+	}
+
+	out := make([]byte, len(C)-overhead)
+	// Test that an untampered C can be decrypted successfully.
+	if err := aliceSK.Decrypt(C, out); err != nil || !bytes.Equal(out, m) {
+		t.Fatal(err)
+	}
+	// Test that a tampered C cannot be decrypted successfully.
+	C[0] = C[0] ^ byte(1)
+	if err := aliceSK.Decrypt(C, out); err == nil {
+		t.Fatalf("successfully decrypted a tampered ciphetext: %v", err)
+	}
+}
+
+var (
+	bb1           Master
+	bb1SK         PrivateKey
+	benchmarkmlen = 64
+	benchmarkm    = make([]byte, benchmarkmlen)
+	bb1C          []byte
 )
 
 func TestBB1Marshaling(t *testing.T) {
@@ -91,11 +143,12 @@
 		t.Fatal(err)
 	}
 	m := []byte("01234567899876543210123456789012")
+	overhead := bb1P.CiphertextOverhead()
 	var (
-		C1 = make([]byte, CiphertextSize)
-		C2 = make([]byte, CiphertextSize)
-		m1 = make([]byte, PlaintextSize)
-		m2 = make([]byte, PlaintextSize)
+		C1 = make([]byte, len(m)+overhead)
+		C2 = make([]byte, len(m)+overhead)
+		m1 = make([]byte, len(m))
+		m2 = make([]byte, len(m))
 	)
 	// Encrypt with the original params, decrypt with the unmarshaled key.
 	if err := bb1P.Encrypt("alice", m, C1); err != nil {
@@ -158,6 +211,8 @@
 	if _, err := rand.Read(benchmarkm[:]); err != nil {
 		panic(err)
 	}
+	overhead := bb1.Params().CiphertextOverhead()
+	bb1C = make([]byte, benchmarkmlen+overhead)
 	if err := bb1.Params().Encrypt("alice", benchmarkm, bb1C); err != nil {
 		panic(err)
 	}
@@ -173,7 +228,7 @@
 
 func BenchmarkEncryptBB(b *testing.B) {
 	p := bb1.Params()
-	C := make([]byte, CiphertextSize)
+	C := make([]byte, benchmarkmlen+p.CiphertextOverhead())
 	for i := 0; i < b.N; i++ {
 		if err := p.Encrypt("alice", benchmarkm, C); err != nil {
 			b.Fatal(err)
@@ -182,7 +237,7 @@
 }
 
 func BenchmarkDecryptBB(b *testing.B) {
-	m := make([]byte, PlaintextSize)
+	m := make([]byte, benchmarkmlen)
 	for i := 0; i < b.N; i++ {
 		if err := bb1SK.Decrypt(bb1C, m); err != nil {
 			b.Fatal(err)
diff --git a/ibe/ibe.go b/ibe/ibe.go
index 0d7c60e..4808b3c 100644
--- a/ibe/ibe.go
+++ b/ibe/ibe.go
@@ -32,11 +32,6 @@
 // systems (at the time of this writing, only for the Boneh-Boyen scheme).
 package ibe
 
-const (
-	PlaintextSize  = 32
-	CiphertextSize = 160
-)
-
 // Master is the interface used to extract private keys for arbitrary identities.
 type Master interface {
 	Extract(id string) (PrivateKey, error)
@@ -48,16 +43,25 @@
 type Params interface {
 	// Encrypt encrypts m into C for the identity id.
 	//
-	// The slices m and C must of PlaintextSize and CiphertextSize respectively,
-	// and must not overlap.
+	// The slice C must be of size len(m) + CiphertextOverhead(), and the two
+	// slices must not overlap.
 	Encrypt(id string, m, C []byte) error
+
+	// The additional space required to encrypt a message, that is,
+	// if a message has length m, the size of the ciphertext is
+	// m + CiphertextOverhead().
+	CiphertextOverhead() int
 }
 
 // PrivateKey is the interface used to decrypt encrypted messages.
 type PrivateKey interface {
 	// Decrypt decrypts ciphertext C into m.
 	//
-	// The slices m and C must of PlaintextSize and CiphertextSize respectively,
-	// and must not overlap.
+	// The slice m must be of size len(C) - CiphertextOverhead(), and the two
+	// sices must not overlap.
 	Decrypt(C, m []byte) error
+
+	// Params returns the global system parameters of the Master that
+	// was used to extract this private key.
+	Params() Params
 }