From b4420620d383f1f32962620082886764330417c9 Mon Sep 17 00:00:00 2001 From: alienx5499 Date: Mon, 17 Nov 2025 20:44:19 +0530 Subject: [PATCH 1/2] add additional data support to IBE encryption for replay attack prevention --- encrypt/ibe/ibe.go | 186 +++++++++++++++++++++++++++++++++++++++- encrypt/ibe/ibe_test.go | 174 +++++++++++++++++++++++++++++++++++++ 2 files changed, 359 insertions(+), 1 deletion(-) diff --git a/encrypt/ibe/ibe.go b/encrypt/ibe/ibe.go index e768cbf0b..c1f6354fe 100644 --- a/encrypt/ibe/ibe.go +++ b/encrypt/ibe/ibe.go @@ -132,6 +132,94 @@ func DecryptCCAonG1(s pairing.Suite, private kyber.Point, c *Ciphertext) ([]byte return msg, nil } +// EncryptCCAonG1WithAD encrypts with additional data bound via commitment. +// different AD values produce different ciphertexts for replay attack prevention. +func EncryptCCAonG1WithAD(s pairing.Suite, master kyber.Point, ID, msg, additionalData []byte) (*Ciphertext, error) { + if len(msg) > s.Hash().Size() { + return nil, errors.New("plaintext too long for the hash function provided") + } + + // 1. Compute Gid = e(master,Q_id) + hG2, ok := s.G2().Point().(kyber.HashablePoint) + if !ok { + return nil, errors.New("point needs to implement `kyber.HashablePoint`") + } + Qid := hG2.Hash(ID) + Gid := s.Pair(master, Qid) + + // 2. Derive random sigma + sigma := make([]byte, len(msg)) + if _, err := rand.Read(sigma); err != nil { + return nil, fmt.Errorf("err reading rand sigma: %v", err) + } + // 3. derive r with additional data included in hash + r, err := h3WithAD(s, sigma, msg, additionalData) + if err != nil { + return nil, err + } + // 4. Compute U = rP + U := s.G1().Point().Mul(r, nil) + + // 5. Compute V = sigma XOR H2(rGid) + rGid := Gid.Mul(r, Gid) // even in Gt, it's additive notation + hrGid, err := gtToHash(s, rGid, len(msg)) + if err != nil { + return nil, err + } + V := xor(sigma, hrGid) + + // 6. Compute M XOR H(sigma) + hsigma, err := h4(s, sigma, len(msg)) + if err != nil { + return nil, err + } + W := xor(msg, hsigma) + + return &Ciphertext{ + U: U, + V: V, + W: W, + }, nil +} + +// DecryptCCAonG1WithAD decrypts with additional data verification. +// wrong AD causes cryptographic failure at rP check. +func DecryptCCAonG1WithAD(s pairing.Suite, private kyber.Point, c *Ciphertext, additionalData []byte) ([]byte, error) { + if len(c.W) > s.Hash().Size() { + return nil, errors.New("ciphertext too long for the hash function provided") + } + + // 1. Compute sigma = V XOR H2(e(rP,private)) + rGid := s.Pair(c.U, private) + hrGid, err := gtToHash(s, rGid, len(c.W)) + if err != nil { + return nil, err + } + if len(hrGid) != len(c.V) { + return nil, fmt.Errorf("XorSigma is of invalid length: exp %d vs got %d", len(hrGid), len(c.V)) + } + sigma := xor(hrGid, c.V) + + // 2. Compute M = W XOR H4(sigma) + hsigma, err := h4(s, sigma, len(c.W)) + if err != nil { + return nil, err + } + + msg := xor(hsigma, c.W) + + // 3. Check U = rP with additional data + r, err := h3WithAD(s, sigma, msg, additionalData) + if err != nil { + return nil, err + } + rP := s.G1().Point().Mul(r, nil) + if !rP.Equal(c.U) { + return nil, fmt.Errorf("invalid proof: rP check failed") + } + return msg, nil +} + // EncryptCCAonG2 implements the CCA identity-based encryption scheme from // https://crypto.stanford.edu/~dabo/pubs/papers/bfibe.pdf for more information // about the scheme. @@ -227,8 +315,101 @@ func DecryptCCAonG2(s pairing.Suite, private kyber.Point, c *Ciphertext) ([]byte return msg, nil } +// EncryptCCAonG2WithAD encrypts with additional data bound via commitment. +// different AD values produce different ciphertexts for replay attack prevention. +func EncryptCCAonG2WithAD(s pairing.Suite, master kyber.Point, ID, msg, additionalData []byte) (*Ciphertext, error) { + if len(msg) > s.Hash().Size() { + return nil, errors.New("plaintext too long for the hash function provided") + } + + // 1. Compute Gid = e(Q_id, master) + hG2, ok := s.G1().Point().(kyber.HashablePoint) + if !ok { + return nil, errors.New("point needs to implement `kyber.HashablePoint`") + } + Qid := hG2.Hash(ID) + Gid := s.Pair(Qid, master) + + // 2. Derive random sigma + sigma := make([]byte, len(msg)) + if _, err := rand.Read(sigma); err != nil { + return nil, fmt.Errorf("err reading rand sigma: %v", err) + } + // 3. Derive r from sigma, msg, and additionalData + r, err := h3WithAD(s, sigma, msg, additionalData) + if err != nil { + return nil, err + } + // 4. Compute U = rP + U := s.G2().Point().Mul(r, nil) + + // 5. Compute V = sigma XOR H2(rGid) + rGid := Gid.Mul(r, Gid) // even in Gt, it's additive notation + hrGid, err := gtToHash(s, rGid, len(msg)) + if err != nil { + return nil, err + } + V := xor(sigma, hrGid) + + // 6. Compute M XOR H(sigma) + hsigma, err := h4(s, sigma, len(msg)) + if err != nil { + return nil, err + } + W := xor(msg, hsigma) + + return &Ciphertext{ + U: U, + V: V, + W: W, + }, nil +} + +// DecryptCCAonG2WithAD decrypts with additional data verification. +// wrong AD causes cryptographic failure at rP check. +func DecryptCCAonG2WithAD(s pairing.Suite, private kyber.Point, c *Ciphertext, additionalData []byte) ([]byte, error) { + if len(c.W) > s.Hash().Size() { + return nil, errors.New("ciphertext too long for the hash function provided") + } + + // 1. Compute sigma = V XOR H2(e(rP,private)) + rGid := s.Pair(private, c.U) + hrGid, err := gtToHash(s, rGid, len(c.W)) + if err != nil { + return nil, err + } + if len(hrGid) != len(c.V) { + return nil, fmt.Errorf("XorSigma is of invalid length: exp %d vs got %d", len(hrGid), len(c.V)) + } + sigma := xor(hrGid, c.V) + + // 2. Compute M = W XOR H4(sigma) + hsigma, err := h4(s, sigma, len(c.W)) + if err != nil { + return nil, err + } + + msg := xor(hsigma, c.W) + + // 3. verify rP check with additional data + r, err := h3WithAD(s, sigma, msg, additionalData) + if err != nil { + return nil, err + } + rP := s.G2().Point().Mul(r, nil) + if !rP.Equal(c.U) { + return nil, fmt.Errorf("invalid proof: rP check failed on msg %s, r %x", msg, r) + } + return msg, nil +} + // hash sigma and msg to get r func h3(s pairing.Suite, sigma, msg []byte) (kyber.Scalar, error) { + return h3WithAD(s, sigma, msg, nil) +} + +// h3WithAD includes additional data in the hash for r generation +func h3WithAD(s pairing.Suite, sigma, msg, additionalData []byte) (kyber.Scalar, error) { h := s.Hash() if h.Size() != s.G1().ScalarLen() { @@ -242,7 +423,10 @@ func h3(s pairing.Suite, sigma, msg []byte) (kyber.Scalar, error) { return nil, fmt.Errorf("err hashing sigma: %v", err) } _, _ = h.Write(msg) - // we hash it a first time: buffer = hash("IBE-H3" || sigma || msg) + if len(additionalData) > 0 { + _, _ = h.Write(additionalData) + } + // hash("IBE-H3" || sigma || msg || additionalData) buffer := h.Sum(nil) hashable, ok := s.G1().Scalar().(*mod.Int) diff --git a/encrypt/ibe/ibe_test.go b/encrypt/ibe/ibe_test.go index 513a5d69d..9bec380ab 100644 --- a/encrypt/ibe/ibe_test.go +++ b/encrypt/ibe/ibe_test.go @@ -259,3 +259,177 @@ func TestCPAEncryptOnG1(t *testing.T) { require.NoError(t, err) require.Equal(t, msg, msg2) } + +func TestEncryptDecryptWithAdditionalData(t *testing.T) { + t.Run("OnG1_WithCorrectAD", func(t *testing.T) { + suite, Ppub, ID, sQid, _, _ := newSetting(1) + msg := []byte("Hello World\n") + additionalData := []byte("transaction_hash_0x1234") + + c, err := EncryptCCAonG1WithAD(suite, Ppub, ID, msg, additionalData) + require.NoError(t, err) + msg2, err := DecryptCCAonG1WithAD(suite, sQid, c, additionalData) + require.NoError(t, err) + require.Equal(t, msg, msg2) + }) + + t.Run("OnG2_WithCorrectAD", func(t *testing.T) { + suite, Ppub, ID, sQid, _, _ := newSetting(2) + msg := []byte("Hello World\n") + additionalData := []byte("transaction_hash_0x1234") + + c, err := EncryptCCAonG2WithAD(suite, Ppub, ID, msg, additionalData) + require.NoError(t, err) + msg2, err := DecryptCCAonG2WithAD(suite, sQid, c, additionalData) + require.NoError(t, err) + require.Equal(t, msg, msg2) + }) + + t.Run("OnG1_WithIncorrectAD_FailsDecryption", func(t *testing.T) { + suite, Ppub, ID, sQid, _, _ := newSetting(1) + msg := []byte("Hello World\n") + correctAD := []byte("transaction_hash_0x1234") + incorrectAD := []byte("transaction_hash_0x5678") + + c, err := EncryptCCAonG1WithAD(suite, Ppub, ID, msg, correctAD) + require.NoError(t, err) + + // Attempt to decrypt with incorrect additional data should fail + _, err = DecryptCCAonG1WithAD(suite, sQid, c, incorrectAD) + require.Error(t, err) + require.ErrorContains(t, err, "invalid proof") + }) + + t.Run("OnG2_WithIncorrectAD_FailsDecryption", func(t *testing.T) { + suite, Ppub, ID, sQid, _, _ := newSetting(2) + msg := []byte("Hello World\n") + correctAD := []byte("transaction_hash_0x1234") + incorrectAD := []byte("transaction_hash_0x5678") + + c, err := EncryptCCAonG2WithAD(suite, Ppub, ID, msg, correctAD) + require.NoError(t, err) + + // Attempt to decrypt with incorrect additional data should fail + _, err = DecryptCCAonG2WithAD(suite, sQid, c, incorrectAD) + require.Error(t, err) + require.ErrorContains(t, err, "invalid proof") + }) + + t.Run("OnG1_WithMissingAD_FailsDecryption", func(t *testing.T) { + suite, Ppub, ID, sQid, _, _ := newSetting(1) + msg := []byte("Hello World\n") + additionalData := []byte("transaction_hash_0x1234") + + c, err := EncryptCCAonG1WithAD(suite, Ppub, ID, msg, additionalData) + require.NoError(t, err) + + // Attempt to decrypt without additional data should fail + _, err = DecryptCCAonG1WithAD(suite, sQid, c, nil) + require.Error(t, err) + require.ErrorContains(t, err, "invalid proof") + }) + + t.Run("OnG2_WithMissingAD_FailsDecryption", func(t *testing.T) { + suite, Ppub, ID, sQid, _, _ := newSetting(2) + msg := []byte("Hello World\n") + additionalData := []byte("transaction_hash_0x1234") + + c, err := EncryptCCAonG2WithAD(suite, Ppub, ID, msg, additionalData) + require.NoError(t, err) + + // Attempt to decrypt without additional data should fail + _, err = DecryptCCAonG2WithAD(suite, sQid, c, nil) + require.Error(t, err) + require.ErrorContains(t, err, "invalid proof") + }) + + t.Run("OnG1_WithEmptyAD", func(t *testing.T) { + suite, Ppub, ID, sQid, _, _ := newSetting(1) + msg := []byte("Hello World\n") + emptyAD := []byte{} + + c, err := EncryptCCAonG1WithAD(suite, Ppub, ID, msg, emptyAD) + require.NoError(t, err) + msg2, err := DecryptCCAonG1WithAD(suite, sQid, c, emptyAD) + require.NoError(t, err) + require.Equal(t, msg, msg2) + }) + + t.Run("OnG2_WithEmptyAD", func(t *testing.T) { + suite, Ppub, ID, sQid, _, _ := newSetting(2) + msg := []byte("Hello World\n") + emptyAD := []byte{} + + c, err := EncryptCCAonG2WithAD(suite, Ppub, ID, msg, emptyAD) + require.NoError(t, err) + msg2, err := DecryptCCAonG2WithAD(suite, sQid, c, emptyAD) + require.NoError(t, err) + require.Equal(t, msg, msg2) + }) +} + +func TestDifferentADProducesDifferentCiphertexts(t *testing.T) { + t.Run("OnG1", func(t *testing.T) { + suite, Ppub, ID, _, _, _ := newSetting(1) + msg := []byte("Hello World\n") + ad1 := []byte("context1") + ad2 := []byte("context2") + + c1, err := EncryptCCAonG1WithAD(suite, Ppub, ID, msg, ad1) + require.NoError(t, err) + c2, err := EncryptCCAonG1WithAD(suite, Ppub, ID, msg, ad2) + require.NoError(t, err) + + // Different AD should produce different U points (due to different r values) + require.False(t, c1.U.Equal(c2.U), "Different AD should produce different ciphertexts") + }) + + t.Run("OnG2", func(t *testing.T) { + suite, Ppub, ID, _, _, _ := newSetting(2) + msg := []byte("Hello World\n") + ad1 := []byte("context1") + ad2 := []byte("context2") + + c1, err := EncryptCCAonG2WithAD(suite, Ppub, ID, msg, ad1) + require.NoError(t, err) + c2, err := EncryptCCAonG2WithAD(suite, Ppub, ID, msg, ad2) + require.NoError(t, err) + + // Different AD should produce different U points (due to different r values) + require.False(t, c1.U.Equal(c2.U), "Different AD should produce different ciphertexts") + }) +} + +func TestReplayAttackPrevention(t *testing.T) { + t.Run("OnG1_SameCiphertextDifferentContext", func(t *testing.T) { + suite, Ppub, ID, sQid, _, _ := newSetting(1) + msg := []byte("payment:100USD") + context1 := []byte("recipient:alice") + context2 := []byte("recipient:bob") + + // Encrypt payment for Alice + ciphertext, err := EncryptCCAonG1WithAD(suite, Ppub, ID, msg, context1) + require.NoError(t, err) + + // Try to "replay" the ciphertext in Bob's context - should fail + _, err = DecryptCCAonG1WithAD(suite, sQid, ciphertext, context2) + require.Error(t, err) + require.ErrorContains(t, err, "invalid proof") + }) + + t.Run("OnG2_SameCiphertextDifferentContext", func(t *testing.T) { + suite, Ppub, ID, sQid, _, _ := newSetting(2) + msg := []byte("payment:100USD") + context1 := []byte("recipient:alice") + context2 := []byte("recipient:bob") + + // Encrypt payment for Alice + ciphertext, err := EncryptCCAonG2WithAD(suite, Ppub, ID, msg, context1) + require.NoError(t, err) + + // Try to "replay" the ciphertext in Bob's context - should fail + _, err = DecryptCCAonG2WithAD(suite, sQid, ciphertext, context2) + require.Error(t, err) + require.ErrorContains(t, err, "invalid proof") + }) +} From b1fc8d280795dfd117a555441eda38f6243059f7 Mon Sep 17 00:00:00 2001 From: alienx5499 Date: Fri, 28 Nov 2025 22:40:13 +0530 Subject: [PATCH 2/2] refactor(ibe): eliminate duplication in AD support functions following h3 pattern --- encrypt/ibe/ibe.go | 190 +++++----------------------------------- encrypt/ibe/ibe_test.go | 50 +++++------ 2 files changed, 45 insertions(+), 195 deletions(-) diff --git a/encrypt/ibe/ibe.go b/encrypt/ibe/ibe.go index c1f6354fe..1c37265b7 100644 --- a/encrypt/ibe/ibe.go +++ b/encrypt/ibe/ibe.go @@ -45,9 +45,9 @@ func H4Tag() []byte { // - the Ciphertext.U point will be on G1 // - ID is the ID towards which we encrypt the message // - msg is the actual message -// - seed is the random seed to generate the random element (sigma) of the encryption +// - additionalData is optional additional data bound via commitment (nil for backward compatibility) // The suite must produce points which implements the `HashablePoint` interface. -func EncryptCCAonG1(s pairing.Suite, master kyber.Point, ID, msg []byte) (*Ciphertext, error) { +func EncryptCCAonG1(s pairing.Suite, master kyber.Point, ID, msg, additionalData []byte) (*Ciphertext, error) { if len(msg) > s.Hash().Size() { return nil, errors.New("plaintext too long for the hash function provided") } @@ -65,8 +65,8 @@ func EncryptCCAonG1(s pairing.Suite, master kyber.Point, ID, msg []byte) (*Ciphe if _, err := rand.Read(sigma); err != nil { return nil, fmt.Errorf("err reading rand sigma: %v", err) } - // 3. Derive r from sigma and msg - r, err := h3(s, sigma, msg) + // 3. Derive r from sigma, msg, and optional additionalData + r, err := h3WithAD(s, sigma, msg, additionalData) if err != nil { return nil, err } @@ -96,7 +96,8 @@ func EncryptCCAonG1(s pairing.Suite, master kyber.Point, ID, msg []byte) (*Ciphe } // DecryptCCAonG1 decrypts ciphertexts encrypted using EncryptCCAonG1 given a G2 "private" point -func DecryptCCAonG1(s pairing.Suite, private kyber.Point, c *Ciphertext) ([]byte, error) { +// - additionalData is optional additional data that must match the encryption context (nil for backward compatibility) +func DecryptCCAonG1(s pairing.Suite, private kyber.Point, c *Ciphertext, additionalData []byte) ([]byte, error) { if len(c.W) > s.Hash().Size() { return nil, errors.New("ciphertext too long for the hash function provided") } @@ -120,8 +121,8 @@ func DecryptCCAonG1(s pairing.Suite, private kyber.Point, c *Ciphertext) ([]byte msg := xor(hsigma, c.W) - // 3. Check U = rP - r, err := h3(s, sigma, msg) + // 3. Check U = rP with optional additionalData + r, err := h3WithAD(s, sigma, msg, additionalData) if err != nil { return nil, err } @@ -135,89 +136,13 @@ func DecryptCCAonG1(s pairing.Suite, private kyber.Point, c *Ciphertext) ([]byte // EncryptCCAonG1WithAD encrypts with additional data bound via commitment. // different AD values produce different ciphertexts for replay attack prevention. func EncryptCCAonG1WithAD(s pairing.Suite, master kyber.Point, ID, msg, additionalData []byte) (*Ciphertext, error) { - if len(msg) > s.Hash().Size() { - return nil, errors.New("plaintext too long for the hash function provided") - } - - // 1. Compute Gid = e(master,Q_id) - hG2, ok := s.G2().Point().(kyber.HashablePoint) - if !ok { - return nil, errors.New("point needs to implement `kyber.HashablePoint`") - } - Qid := hG2.Hash(ID) - Gid := s.Pair(master, Qid) - - // 2. Derive random sigma - sigma := make([]byte, len(msg)) - if _, err := rand.Read(sigma); err != nil { - return nil, fmt.Errorf("err reading rand sigma: %v", err) - } - // 3. derive r with additional data included in hash - r, err := h3WithAD(s, sigma, msg, additionalData) - if err != nil { - return nil, err - } - // 4. Compute U = rP - U := s.G1().Point().Mul(r, nil) - - // 5. Compute V = sigma XOR H2(rGid) - rGid := Gid.Mul(r, Gid) // even in Gt, it's additive notation - hrGid, err := gtToHash(s, rGid, len(msg)) - if err != nil { - return nil, err - } - V := xor(sigma, hrGid) - - // 6. Compute M XOR H(sigma) - hsigma, err := h4(s, sigma, len(msg)) - if err != nil { - return nil, err - } - W := xor(msg, hsigma) - - return &Ciphertext{ - U: U, - V: V, - W: W, - }, nil + return EncryptCCAonG1(s, master, ID, msg, additionalData) } // DecryptCCAonG1WithAD decrypts with additional data verification. // wrong AD causes cryptographic failure at rP check. func DecryptCCAonG1WithAD(s pairing.Suite, private kyber.Point, c *Ciphertext, additionalData []byte) ([]byte, error) { - if len(c.W) > s.Hash().Size() { - return nil, errors.New("ciphertext too long for the hash function provided") - } - - // 1. Compute sigma = V XOR H2(e(rP,private)) - rGid := s.Pair(c.U, private) - hrGid, err := gtToHash(s, rGid, len(c.W)) - if err != nil { - return nil, err - } - if len(hrGid) != len(c.V) { - return nil, fmt.Errorf("XorSigma is of invalid length: exp %d vs got %d", len(hrGid), len(c.V)) - } - sigma := xor(hrGid, c.V) - - // 2. Compute M = W XOR H4(sigma) - hsigma, err := h4(s, sigma, len(c.W)) - if err != nil { - return nil, err - } - - msg := xor(hsigma, c.W) - - // 3. Check U = rP with additional data - r, err := h3WithAD(s, sigma, msg, additionalData) - if err != nil { - return nil, err - } - rP := s.G1().Point().Mul(r, nil) - if !rP.Equal(c.U) { - return nil, fmt.Errorf("invalid proof: rP check failed") - } - return msg, nil + return DecryptCCAonG1(s, private, c, additionalData) } // EncryptCCAonG2 implements the CCA identity-based encryption scheme from @@ -228,9 +153,9 @@ func DecryptCCAonG1WithAD(s pairing.Suite, private kyber.Point, c *Ciphertext, a // - the Ciphertext.U point will be on G2 // - ID is the ID towards which we encrypt the message // - msg is the actual message -// - seed is the random seed to generate the random element (sigma) of the encryption +// - additionalData is optional additional data bound via commitment (nil for backward compatibility) // The suite must produce points which implements the `HashablePoint` interface. -func EncryptCCAonG2(s pairing.Suite, master kyber.Point, ID, msg []byte) (*Ciphertext, error) { +func EncryptCCAonG2(s pairing.Suite, master kyber.Point, ID, msg, additionalData []byte) (*Ciphertext, error) { if len(msg) > s.Hash().Size() { return nil, errors.New("plaintext too long for the hash function provided") } @@ -248,8 +173,8 @@ func EncryptCCAonG2(s pairing.Suite, master kyber.Point, ID, msg []byte) (*Ciphe if _, err := rand.Read(sigma); err != nil { return nil, fmt.Errorf("err reading rand sigma: %v", err) } - // 3. Derive r from sigma and msg - r, err := h3(s, sigma, msg) + // 3. Derive r from sigma, msg, and optional additionalData + r, err := h3WithAD(s, sigma, msg, additionalData) if err != nil { return nil, err } @@ -279,7 +204,8 @@ func EncryptCCAonG2(s pairing.Suite, master kyber.Point, ID, msg []byte) (*Ciphe } // DecryptCCAonG2 decrypts ciphertexts encrypted using EncryptCCAonG2 given a G1 "private" point -func DecryptCCAonG2(s pairing.Suite, private kyber.Point, c *Ciphertext) ([]byte, error) { +// - additionalData is optional additional data that must match the encryption context (nil for backward compatibility) +func DecryptCCAonG2(s pairing.Suite, private kyber.Point, c *Ciphertext, additionalData []byte) ([]byte, error) { if len(c.W) > s.Hash().Size() { return nil, errors.New("ciphertext too long for the hash function provided") } @@ -303,8 +229,8 @@ func DecryptCCAonG2(s pairing.Suite, private kyber.Point, c *Ciphertext) ([]byte msg := xor(hsigma, c.W) - // 3. Check U = rP - r, err := h3(s, sigma, msg) + // 3. Check U = rP with optional additionalData + r, err := h3WithAD(s, sigma, msg, additionalData) if err != nil { return nil, err } @@ -318,89 +244,13 @@ func DecryptCCAonG2(s pairing.Suite, private kyber.Point, c *Ciphertext) ([]byte // EncryptCCAonG2WithAD encrypts with additional data bound via commitment. // different AD values produce different ciphertexts for replay attack prevention. func EncryptCCAonG2WithAD(s pairing.Suite, master kyber.Point, ID, msg, additionalData []byte) (*Ciphertext, error) { - if len(msg) > s.Hash().Size() { - return nil, errors.New("plaintext too long for the hash function provided") - } - - // 1. Compute Gid = e(Q_id, master) - hG2, ok := s.G1().Point().(kyber.HashablePoint) - if !ok { - return nil, errors.New("point needs to implement `kyber.HashablePoint`") - } - Qid := hG2.Hash(ID) - Gid := s.Pair(Qid, master) - - // 2. Derive random sigma - sigma := make([]byte, len(msg)) - if _, err := rand.Read(sigma); err != nil { - return nil, fmt.Errorf("err reading rand sigma: %v", err) - } - // 3. Derive r from sigma, msg, and additionalData - r, err := h3WithAD(s, sigma, msg, additionalData) - if err != nil { - return nil, err - } - // 4. Compute U = rP - U := s.G2().Point().Mul(r, nil) - - // 5. Compute V = sigma XOR H2(rGid) - rGid := Gid.Mul(r, Gid) // even in Gt, it's additive notation - hrGid, err := gtToHash(s, rGid, len(msg)) - if err != nil { - return nil, err - } - V := xor(sigma, hrGid) - - // 6. Compute M XOR H(sigma) - hsigma, err := h4(s, sigma, len(msg)) - if err != nil { - return nil, err - } - W := xor(msg, hsigma) - - return &Ciphertext{ - U: U, - V: V, - W: W, - }, nil + return EncryptCCAonG2(s, master, ID, msg, additionalData) } // DecryptCCAonG2WithAD decrypts with additional data verification. // wrong AD causes cryptographic failure at rP check. func DecryptCCAonG2WithAD(s pairing.Suite, private kyber.Point, c *Ciphertext, additionalData []byte) ([]byte, error) { - if len(c.W) > s.Hash().Size() { - return nil, errors.New("ciphertext too long for the hash function provided") - } - - // 1. Compute sigma = V XOR H2(e(rP,private)) - rGid := s.Pair(private, c.U) - hrGid, err := gtToHash(s, rGid, len(c.W)) - if err != nil { - return nil, err - } - if len(hrGid) != len(c.V) { - return nil, fmt.Errorf("XorSigma is of invalid length: exp %d vs got %d", len(hrGid), len(c.V)) - } - sigma := xor(hrGid, c.V) - - // 2. Compute M = W XOR H4(sigma) - hsigma, err := h4(s, sigma, len(c.W)) - if err != nil { - return nil, err - } - - msg := xor(hsigma, c.W) - - // 3. verify rP check with additional data - r, err := h3WithAD(s, sigma, msg, additionalData) - if err != nil { - return nil, err - } - rP := s.G2().Point().Mul(r, nil) - if !rP.Equal(c.U) { - return nil, fmt.Errorf("invalid proof: rP check failed on msg %s, r %x", msg, r) - } - return msg, nil + return DecryptCCAonG2(s, private, c, additionalData) } // hash sigma and msg to get r diff --git a/encrypt/ibe/ibe_test.go b/encrypt/ibe/ibe_test.go index 9bec380ab..e3748e0a4 100644 --- a/encrypt/ibe/ibe_test.go +++ b/encrypt/ibe/ibe_test.go @@ -14,8 +14,8 @@ import ( func newSetting(i uint) ( pairing.Suite, kyber.Point, []byte, kyber.Point, - func(s pairing.Suite, master kyber.Point, ID []byte, msg []byte) (*Ciphertext, error), - func(s pairing.Suite, private kyber.Point, c *Ciphertext) ([]byte, error), + func(s pairing.Suite, master kyber.Point, ID []byte, msg []byte, additionalData []byte) (*Ciphertext, error), + func(s pairing.Suite, private kyber.Point, c *Ciphertext, additionalData []byte) ([]byte, error), ) { if !(i == 1 || i == 2) { panic("invalid test") @@ -50,9 +50,9 @@ func TestValidEncryptionDecrypts(t *testing.T) { suite, Ppub, ID, sQid, encrypt, decrypt := newSetting(1) msg := []byte("Hello World\n") - c, err := encrypt(suite, Ppub, ID, msg) + c, err := encrypt(suite, Ppub, ID, msg, nil) require.NoError(t, err) - msg2, err := decrypt(suite, sQid, c) + msg2, err := decrypt(suite, sQid, c, nil) require.NoError(t, err) require.Equal(t, msg, msg2) }) @@ -61,9 +61,9 @@ func TestValidEncryptionDecrypts(t *testing.T) { suite, Ppub, ID, sQid, encrypt, decrypt := newSetting(2) msg := []byte("Hello World\n") - c, err := encrypt(suite, Ppub, ID, msg) + c, err := encrypt(suite, Ppub, ID, msg, nil) require.NoError(t, err) - msg2, err := decrypt(suite, sQid, c) + msg2, err := decrypt(suite, sQid, c, nil) require.NoError(t, err) require.Equal(t, msg, msg2) }) @@ -75,12 +75,12 @@ func TestInvalidSigmaFailsDecryption(t *testing.T) { suite, Ppub, ID, sQid, encrypt, decrypt := newSetting(1) msg := []byte("Hello World\n") - c, err := encrypt(suite, Ppub, ID, msg) + c, err := encrypt(suite, Ppub, ID, msg, nil) require.NoError(t, err) c.V = []byte("somenonsense") - _, err = decrypt(suite, sQid, c) + _, err = decrypt(suite, sQid, c, nil) require.Error(t, err) require.ErrorContains(t, err, "invalid proof") }) @@ -90,12 +90,12 @@ func TestInvalidSigmaFailsDecryption(t *testing.T) { suite, Ppub, ID, sQid, encrypt, decrypt := newSetting(2) msg := []byte("Hello World\n") - c, err := encrypt(suite, Ppub, ID, msg) + c, err := encrypt(suite, Ppub, ID, msg, nil) require.NoError(t, err) c.V = []byte("somenonsense") - _, err = decrypt(suite, sQid, c) + _, err = decrypt(suite, sQid, c, nil) require.Error(t, err) require.ErrorContains(t, err, "invalid proof") }) @@ -106,11 +106,11 @@ func TestInvalidMessageFailsDecryption(t *testing.T) { suite, Ppub, ID, sQid, encrypt, decrypt := newSetting(1) msg := []byte("Hello World\n") - c, err := encrypt(suite, Ppub, ID, msg) + c, err := encrypt(suite, Ppub, ID, msg, nil) require.NoError(t, err) c.W = []byte("somenonsense") - _, err = decrypt(suite, sQid, c) + _, err = decrypt(suite, sQid, c, nil) require.Error(t, err) require.ErrorContains(t, err, "invalid proof") }) @@ -119,11 +119,11 @@ func TestInvalidMessageFailsDecryption(t *testing.T) { suite, Ppub, ID, sQid, encrypt, decrypt := newSetting(2) msg := []byte("Hello World\n") - c, err := encrypt(suite, Ppub, ID, msg) + c, err := encrypt(suite, Ppub, ID, msg, nil) require.NoError(t, err) c.W = []byte("somenonsense") - _, err = decrypt(suite, sQid, c) + _, err = decrypt(suite, sQid, c, nil) require.Error(t, err) require.ErrorContains(t, err, "invalid proof") }) @@ -133,14 +133,14 @@ func TestVeryLongInputFailsEncryption(t *testing.T) { t.Run("OnG1", func(t *testing.T) { suite, Ppub, ID, _, encrypt, _ := newSetting(1) msg := []byte(strings.Repeat("And you have to understand this, that a prince, especially a new one, cannot observe all those things for which men are esteemed", 1000)) - _, err := encrypt(suite, Ppub, ID, msg) + _, err := encrypt(suite, Ppub, ID, msg, nil) require.Error(t, err) }) t.Run("OnG2", func(t *testing.T) { suite, Ppub, ID, _, encrypt, _ := newSetting(2) msg := []byte(strings.Repeat("And you have to understand this, that a prince, especially a new one, cannot observe all those things for which men are esteemed", 1000)) - _, err := encrypt(suite, Ppub, ID, msg) + _, err := encrypt(suite, Ppub, ID, msg, nil) require.Error(t, err) }) } @@ -149,11 +149,11 @@ func TestVeryLongCipherFailsDecryptionBecauseOfLength(t *testing.T) { t.Run("OnG1", func(t *testing.T) { suite, Ppub, ID, sQid, encrypt, decrypt := newSetting(1) msg := []byte("hello world") - c, err := encrypt(suite, Ppub, ID, msg) + c, err := encrypt(suite, Ppub, ID, msg, nil) require.NoError(t, err) c.W = []byte(strings.Repeat("And you have to understand this, that a prince, especially a new one, cannot observe all those things for which men are esteemed", 1000)) - _, err = decrypt(suite, sQid, c) + _, err = decrypt(suite, sQid, c, nil) require.Error(t, err) require.ErrorContains(t, err, "ciphertext too long for the hash function provided") @@ -161,11 +161,11 @@ func TestVeryLongCipherFailsDecryptionBecauseOfLength(t *testing.T) { t.Run("OnG2", func(t *testing.T) { suite, Ppub, ID, sQid, encrypt, decrypt := newSetting(2) msg := []byte("hello world") - c, err := encrypt(suite, Ppub, ID, msg) + c, err := encrypt(suite, Ppub, ID, msg, nil) require.NoError(t, err) c.W = []byte(strings.Repeat("And you have to understand this, that a prince, especially a new one, cannot observe all those things for which men are esteemed", 1000)) - _, err = decrypt(suite, sQid, c) + _, err = decrypt(suite, sQid, c, nil) require.Error(t, err) require.ErrorContains(t, err, "ciphertext too long for the hash function provided") @@ -176,11 +176,11 @@ func TestInvalidWFailsDecryptionBecauseOfLength(t *testing.T) { t.Run("OnG1", func(t *testing.T) { suite, Ppub, ID, sQid, encrypt, decrypt := newSetting(1) msg := []byte("hello world") - c, err := encrypt(suite, Ppub, ID, msg) + c, err := encrypt(suite, Ppub, ID, msg, nil) require.NoError(t, err) c.W = []byte(strings.Repeat("A", 25)) - _, err = decrypt(suite, sQid, c) + _, err = decrypt(suite, sQid, c, nil) require.Error(t, err) require.ErrorContains(t, err, "XorSigma is of invalid length") @@ -188,11 +188,11 @@ func TestInvalidWFailsDecryptionBecauseOfLength(t *testing.T) { t.Run("OnG2", func(t *testing.T) { suite, Ppub, ID, sQid, encrypt, decrypt := newSetting(2) msg := []byte("hello world") - c, err := encrypt(suite, Ppub, ID, msg) + c, err := encrypt(suite, Ppub, ID, msg, nil) require.NoError(t, err) c.W = []byte(strings.Repeat("A", 25)) - _, err = decrypt(suite, sQid, c) + _, err = decrypt(suite, sQid, c, nil) require.Error(t, err) require.ErrorContains(t, err, "XorSigma is of invalid length") @@ -238,7 +238,7 @@ func TestBackwardsInteropWithTypescript(t *testing.T) { ciphertext := Ciphertext{U: U, W: W, V: V} - result, err := decrypt(suite, beacon, &ciphertext) + result, err := decrypt(suite, beacon, &ciphertext, nil) require.NoError(t, err) require.Equal(t, expectedFileKey, result) }