forked from dedis/kyber
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Identity-based encryption features to support timelock encryption (#28)
migrated from the `timelock` branch where work was done previously, but stripped out the aggregation code and added a few tests Co-authored-by: nikkolasg <[email protected]>
- Loading branch information
1 parent
a60ddc0
commit 0482f4b
Showing
5 changed files
with
320 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
package ibe | ||
|
||
import ( | ||
"crypto/rand" | ||
"errors" | ||
"fmt" | ||
|
||
"golang.org/x/crypto/blake2s" | ||
|
||
"github.com/drand/kyber" | ||
"github.com/drand/kyber/pairing" | ||
"github.com/drand/kyber/util/random" | ||
) | ||
|
||
type Ciphertext struct { | ||
// Random point rP | ||
U kyber.Point | ||
// Sigma attached to ID: sigma XOR H(rG_id) | ||
V []byte | ||
// ciphertext of the message M XOR H(sigma) | ||
W []byte | ||
} | ||
|
||
// H2Tag is the domain separation tag for the H2 hash function | ||
func H2Tag() []byte { | ||
return []byte("IBE-H2") | ||
} | ||
|
||
// H3Tag is the domain separation tag for the H3 hash function | ||
func H3Tag() []byte { | ||
return []byte("IBE-H3") | ||
} | ||
|
||
// H4Tag is the domain separation tag for the H4 hash function | ||
func H4Tag() []byte { | ||
return []byte("IBE-H4") | ||
} | ||
|
||
// Encrypt implements the cca identity based encryption scheme from | ||
// https://crypto.stanford.edu/~dabo/pubs/papers/bfibe.pdf for more information | ||
// about the scheme. | ||
// - master is the master key 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 | ||
// The suite must produce points which implements the `HashablePoint` interface. | ||
func Encrypt(s pairing.Suite, master kyber.Point, ID, msg []byte) (*Ciphertext, error) { | ||
if len(msg)>>16 > 0 { | ||
// we're using blake2 as XOF which only outputs 2^16-1 length | ||
return nil, errors.New("plaintext too long for blake2") | ||
} | ||
// 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 from sigma and msg | ||
r, err := h3(s, sigma, msg) | ||
if err != nil { | ||
return nil, err | ||
} | ||
// 4. Compute U = rP | ||
U := s.G1().Point().Mul(r, s.G1().Point().Base()) | ||
|
||
// 5. Compute V = sigma XOR H2(rGid) | ||
rGid := Gid.Mul(r, Gid) // even in Gt, it's additive notation | ||
hrGid, err := gtToHash(rGid, len(msg), H2Tag()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
V := xor(sigma, hrGid) | ||
|
||
// 6. Compute M XOR H(sigma) | ||
hsigma, err := h4(sigma, len(msg)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
W := xor(msg, hsigma) | ||
|
||
return &Ciphertext{ | ||
U: U, | ||
V: V, | ||
W: W, | ||
}, nil | ||
} | ||
|
||
func Decrypt(s pairing.Suite, private kyber.Point, c *Ciphertext) ([]byte, error) { | ||
// 1. Compute sigma = V XOR H2(e(rP,private)) | ||
gidt := s.Pair(c.U, private) | ||
hgidt, err := gtToHash(gidt, len(c.W), H2Tag()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if len(hgidt) != len(c.V) { | ||
return nil, fmt.Errorf("XorSigma is of invalid length: exp %d vs got %d", len(hgidt), len(c.V)) | ||
} | ||
sigma := xor(hgidt, c.V) | ||
|
||
// 2. Compute M = W XOR H4(sigma) | ||
hsigma, err := h4(sigma, len(c.W)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
msg := xor(hsigma, c.W) | ||
|
||
// 3. Check U = rP | ||
r, err := h3(s, sigma, msg) | ||
if err != nil { | ||
return nil, err | ||
} | ||
rP := s.G1().Point().Mul(r, s.G1().Point().Base()) | ||
if !rP.Equal(c.U) { | ||
return nil, fmt.Errorf("invalid proof: rP check failed") | ||
} | ||
return msg, nil | ||
|
||
} | ||
|
||
const maxSize = 1 << 10 | ||
|
||
// hash sigma and msg to get r | ||
func h3(s pairing.Suite, sigma, msg []byte) (kyber.Scalar, error) { | ||
h3, err := blake2s.NewXOF(maxSize, nil) | ||
if err != nil { | ||
panic(err) | ||
} | ||
if _, err := h3.Write(H3Tag()); err != nil { | ||
return nil, fmt.Errorf("err hashing h3 tag: %v", err) | ||
} | ||
if _, err := h3.Write(sigma); err != nil { | ||
return nil, fmt.Errorf("err hashing sigma to XOF: %v", err) | ||
} | ||
_, _ = h3.Write(msg) | ||
return s.G1().Scalar().Pick(random.New(h3)), nil | ||
} | ||
|
||
func h4(sigma []byte, length int) ([]byte, error) { | ||
h4, err := blake2s.NewXOF(maxSize, nil) | ||
if err != nil { | ||
panic(err) | ||
} | ||
if _, err := h4.Write(H4Tag()); err != nil { | ||
return nil, fmt.Errorf("err writing h4tag: %v", err) | ||
} | ||
if _, err := h4.Write(sigma); err != nil { | ||
return nil, fmt.Errorf("err writing sigma to h4: %v", err) | ||
} | ||
h4sigma := make([]byte, length) | ||
if _, err := h4.Read(h4sigma); err != nil { | ||
return nil, fmt.Errorf("err reading from h4: %v", err) | ||
} | ||
return h4sigma, nil | ||
} | ||
|
||
func gtToHash(gt kyber.Point, length int, dst []byte) ([]byte, error) { | ||
xof, err := blake2s.NewXOF(maxSize, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if _, err := xof.Write(dst); err != nil { | ||
return nil, errors.New("err writing dst to gtHash") | ||
} | ||
gt.MarshalTo(xof) | ||
var b = make([]byte, length) | ||
if _, err := xof.Read(b); err != nil { | ||
return nil, errors.New("couldn't read from xof") | ||
} | ||
return b[:], nil | ||
} | ||
|
||
func xor(a, b []byte) []byte { | ||
if len(a) != len(b) { | ||
panic("wrong xor input") | ||
} | ||
res := make([]byte, len(a)) | ||
for i := 0; i < len(a); i++ { | ||
res[i] = a[i] ^ b[i] | ||
} | ||
return res | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package ibe | ||
|
||
import ( | ||
"strings" | ||
"testing" | ||
|
||
"github.com/drand/kyber" | ||
bls "github.com/drand/kyber-bls12381" | ||
"github.com/drand/kyber/pairing" | ||
"github.com/drand/kyber/util/random" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func newSetting() (pairing.Suite, kyber.Point, []byte, kyber.Point) { | ||
suite := bls.NewBLS12381Suite() | ||
P := suite.G1().Point().Base() | ||
s := suite.G1().Scalar().Pick(random.New()) | ||
Ppub := suite.G1().Point().Mul(s, P) | ||
|
||
ID := []byte("passtherand") | ||
IDP := suite.G2().Point().(kyber.HashablePoint) | ||
Qid := IDP.Hash(ID) // public key | ||
sQid := Qid.Mul(s, Qid) // secret key | ||
return suite, Ppub, ID, sQid | ||
} | ||
|
||
func TestValidTimelockEncryptionDecryptsCorrectly(t *testing.T) { | ||
suite, Ppub, ID, sQid := newSetting() | ||
msg := []byte("Hello World\n") | ||
|
||
c, err := Encrypt(suite, Ppub, ID, msg) | ||
require.NoError(t, err) | ||
msg2, err := Decrypt(suite, sQid, c) | ||
require.NoError(t, err) | ||
require.Equal(t, msg, msg2) | ||
} | ||
|
||
func TestInvalidSigmaFailsDecryption(t *testing.T) { | ||
suite, Ppub, ID, sQid := newSetting() | ||
msg := []byte("Hello World\n") | ||
|
||
c, err := Encrypt(suite, Ppub, ID, msg) | ||
require.NoError(t, err) | ||
|
||
c.V = []byte("somenonsense") | ||
|
||
_, err = Decrypt(suite, sQid, c) | ||
require.Error(t, err) | ||
require.ErrorContains(t, err, "invalid proof") | ||
} | ||
|
||
func TestInvalidMessageFailsDecryption(t *testing.T) { | ||
suite, Ppub, ID, sQid := newSetting() | ||
msg := []byte("Hello World\n") | ||
|
||
c, err := Encrypt(suite, Ppub, ID, msg) | ||
require.NoError(t, err) | ||
|
||
c.W = []byte("somenonsense") | ||
_, err = Decrypt(suite, sQid, c) | ||
require.Error(t, err) | ||
require.ErrorContains(t, err, "invalid proof") | ||
} | ||
|
||
func TestVeryLongInputFailsEncryption(t *testing.T) { | ||
suite, Ppub, ID, _ := newSetting() | ||
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) | ||
require.Error(t, err) | ||
} | ||
|
||
func TestVeryLongCipherFailsDecryptionBecauseOfLength(t *testing.T) { | ||
suite, Ppub, ID, sQid := newSetting() | ||
msg := []byte("hello world") | ||
c, err := Encrypt(suite, Ppub, ID, msg) | ||
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) | ||
|
||
require.Error(t, err) | ||
require.ErrorContains(t, err, "XorSigma is of invalid length") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,21 @@ | ||
module github.com/drand/kyber | ||
|
||
go 1.13 | ||
go 1.17 | ||
|
||
require ( | ||
github.com/drand/kyber-bls12381 v0.2.1 | ||
github.com/jonboulle/clockwork v0.1.0 | ||
github.com/stretchr/testify v1.4.0 | ||
github.com/drand/kyber-bls12381 v0.2.2 | ||
github.com/jonboulle/clockwork v0.3.0 | ||
github.com/stretchr/testify v1.7.2 | ||
go.dedis.ch/fixbuf v1.0.3 | ||
go.dedis.ch/protobuf v1.0.11 | ||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a | ||
golang.org/x/sys v0.0.0-20200926100807-9d91bd62050c | ||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e | ||
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c | ||
) | ||
|
||
require ( | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/kilic/bls12-381 v0.1.0 // indirect | ||
github.com/kr/pretty v0.2.0 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.