| // Copyright 2015 The Vanadium 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 bcrypter |
| |
| import ( |
| "bytes" |
| "fmt" |
| "reflect" |
| "testing" |
| |
| "v.io/v23/context" |
| "v.io/v23/security" |
| "v.io/v23/verror" |
| |
| "v.io/x/lib/ibe" |
| ) |
| |
| func newRoot(name string) *Root { |
| master, err := ibe.SetupBB1() |
| if err != nil { |
| panic(err) |
| } |
| return NewRoot(name, master) |
| } |
| |
| func newPlaintext() []byte { |
| return []byte("AThirtyTwoBytePieceOfTextThisIs!") |
| } |
| |
| func TestExtract(t *testing.T) { |
| ctx, shutdown := context.RootContext() |
| defer shutdown() |
| |
| googleYoutube := newRoot("google:youtube") |
| |
| // googleYoutube shoud not be able to extract a key for the blessing "google". |
| if _, err := googleYoutube.Extract(ctx, "google"); err == nil { |
| t.Fatal("extraction for google unexpectedly succeeded") |
| } |
| |
| // googleYoutube should be able to extract keys for the following blessings. |
| blessings := []string{"google:youtube", "google:youtube:alice", "google:youtube:bob", "google:youtube:alice:phone"} |
| for _, b := range blessings { |
| key, err := googleYoutube.Extract(ctx, b) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if got := key.Blessing(); got != b { |
| t.Fatalf("extracted key is for blessing %v, want key for blessing %v", got, b) |
| } |
| if got, want := key.Params(), googleYoutube.Params(); !reflect.DeepEqual(got, want) { |
| t.Fatalf("extract key is for params %v, want key for params %v", got, want) |
| } |
| } |
| // Every key should be unique. |
| key1, _ := googleYoutube.Extract(ctx, "google:youtube:alice") |
| key2, _ := googleYoutube.Extract(ctx, "google:youtube:alice") |
| if reflect.DeepEqual(key1, key2) { |
| t.Fatal("Two Extract operations yielded the same PrivateKey") |
| } |
| } |
| |
| func TestEncrypt(t *testing.T) { |
| ctx, shutdown := context.RootContext() |
| defer shutdown() |
| |
| var ( |
| googleYoutube = newRoot("google:youtube") |
| google = newRoot("google") |
| |
| encrypter = NewCrypter() |
| ptxt = newPlaintext() |
| ) |
| |
| // empty encrypter should not be able to encrypt for any pattern. |
| if _, err := encrypter.Encrypt(ctx, "google:youtube:alice", ptxt); verror.ErrorID(err) != ErrNoParams.ID { |
| t.Fatalf("Got error %v, wanted error with ID %v", err, ErrNoParams.ID) |
| } |
| |
| // add googleYoutube's params to the encrypter. |
| if err := encrypter.AddParams(ctx, googleYoutube.Params()); err != nil { |
| t.Fatal(err) |
| } |
| // encrypting for "google:youtube:alice" should now succeed. |
| if _, err := encrypter.Encrypt(ctx, "google:youtube:alice", ptxt); err != nil { |
| t.Fatal(err) |
| } |
| |
| // encrypting for pattern "google" should still fail as the encrypter |
| // does not have params that are authoritative on all blessings matching |
| // the pattern "google" (the googleYoutube params are authoritative on |
| // blessings matching "google:youtube"). |
| if _, err := encrypter.Encrypt(ctx, "google", ptxt); verror.ErrorID(err) != ErrNoParams.ID { |
| t.Fatalf("Got error %v, wanted error with ID %v", err, ErrNoParams.ID) |
| } |
| // add google's params to the encrypter. |
| if err := encrypter.AddParams(ctx, google.Params()); err != nil { |
| t.Fatal(err) |
| } |
| // encrypting for "google" should now succeed. |
| if _, err := encrypter.Encrypt(ctx, "google", ptxt); err != nil { |
| t.Fatal(err) |
| } |
| |
| // Encryption should succeed for all of the following patterns |
| patterns := []security.BlessingPattern{"google", "google:$", "google:alice", "google:bob", "google:bob:phone"} |
| for _, p := range patterns { |
| if _, err := encrypter.Encrypt(ctx, p, ptxt); err != nil { |
| t.Fatal(err) |
| } |
| } |
| |
| // Every ciphertext should be unique. |
| ctxt1, _ := encrypter.Encrypt(ctx, "google", ptxt) |
| ctxt2, _ := encrypter.Encrypt(ctx, "google", ptxt) |
| if reflect.DeepEqual(ctxt1, ctxt2) { |
| t.Fatal("Two Encrypt operations yielded the same Ciphertext") |
| } |
| } |
| |
| func TestDecrypt(t *testing.T) { |
| ctx, shutdown := context.RootContext() |
| defer shutdown() |
| |
| addParams := func(c *Crypter, params Params) { |
| if err := c.AddParams(ctx, params); err != nil { |
| t.Fatal(err) |
| } |
| } |
| extract := func(r *Root, b string) *PrivateKey { |
| key, err := r.Extract(ctx, b) |
| if err != nil { |
| t.Fatal(err) |
| } |
| return key |
| } |
| var ( |
| // Create two roots for the name "google". |
| google1 = newRoot("google") |
| google2 = newRoot("google") |
| |
| encrypter = NewCrypter() |
| ptxt = newPlaintext() |
| |
| googleAlice1 = extract(google1, "google:alice:phone") |
| googleAlice2 = extract(google2, "google:alice:tablet:app") |
| ) |
| |
| // Add roots google1 and google2 to the encrypter. |
| addParams(encrypter, google1.Params()) |
| addParams(encrypter, google2.Params()) |
| // encrypt for the pattern "google:alice" |
| ctxt, err := encrypter.Encrypt(ctx, "google:alice", ptxt) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| // A decrypter without any private key should not be able |
| // to decrypt this ciphertext. |
| decrypter := NewCrypter() |
| if _, err := decrypter.Decrypt(ctx, ctxt); verror.ErrorID(err) != ErrPrivateKeyNotFound.ID { |
| t.Fatalf("Got error %v, wanted error with ID %v", err, ErrPrivateKeyNotFound.ID) |
| } |
| |
| // Add key googleAlice1 to the decrypter. |
| if err := decrypter.AddKey(ctx, googleAlice1); err != nil { |
| t.Fatal(err) |
| } |
| // Decryption should now succeed. |
| if got, err := decrypter.Decrypt(ctx, ctxt); err != nil { |
| t.Fatal(err) |
| } else if !bytes.Equal(got, ptxt) { |
| t.Fatalf("Got plaintext %v, want %v", got, ptxt) |
| } |
| |
| // Decryption should have succeeded had the decrypter only contained |
| // googleAlice2. |
| decrypter = NewCrypter() |
| if err := decrypter.AddKey(ctx, googleAlice2); err != nil { |
| t.Fatal(err) |
| } |
| if got, err := decrypter.Decrypt(ctx, ctxt); err != nil { |
| t.Fatal(err) |
| } else if !bytes.Equal(got, ptxt) { |
| t.Fatalf("Got plaintext %v, want %v", got, ptxt) |
| } |
| |
| // Decryption should fail for ciphertexts encrypted for the following |
| // patterns (At this point the decrypter only has a key for the blessing |
| // "google:alice:tablet:app" from the root google2). |
| patterns := []security.BlessingPattern{"google:alice:$", "google:bob", "google:alice:tablet:$", "google:bob:tablet"} |
| for _, p := range patterns { |
| ctxt, err := encrypter.Encrypt(ctx, p, ptxt) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := decrypter.Decrypt(ctx, ctxt); verror.ErrorID(err) != ErrPrivateKeyNotFound.ID { |
| t.Fatalf("Got error %v, wanted error with ID %v", err, ErrPrivateKeyNotFound.ID) |
| } |
| } |
| |
| // Adding the private key googleAlice2 should have also added |
| // google's public params. Thus encrypting for the following |
| // patterns should succeed. |
| patterns = []security.BlessingPattern{"google", "google:$", "google:alice", "google:bob", "google:bob:phone"} |
| for _, p := range patterns { |
| if _, err := decrypter.Encrypt(ctx, p, ptxt); err != nil { |
| t.Fatal(err) |
| } |
| } |
| // But encrypting for the following patterns should fail. |
| patterns = []security.BlessingPattern{"youtube", "youtube:$", "youtube:alice"} |
| for _, p := range patterns { |
| if _, err := decrypter.Encrypt(ctx, p, ptxt); verror.ErrorID(err) != ErrNoParams.ID { |
| t.Fatalf("Got error %v, wanted error with ID %v", err, ErrNoParams.ID) |
| } |
| } |
| } |
| |
| func TestWireCiphertext(t *testing.T) { |
| ctx, shutdown := context.RootContext() |
| defer shutdown() |
| ptxt := newPlaintext() |
| |
| encrypt := func(params Params, pattern security.BlessingPattern) *Ciphertext { |
| enc := NewCrypter() |
| if err := enc.AddParams(ctx, params); err != nil { |
| t.Fatal(err) |
| } |
| ctxt, err := enc.Encrypt(ctx, pattern, ptxt) |
| if err != nil { |
| t.Fatal(err) |
| } |
| return ctxt |
| } |
| decryptAndVerify := func(ctxt *Ciphertext, key *PrivateKey) error { |
| dec := NewCrypter() |
| if err := dec.AddKey(ctx, key); err != nil { |
| return err |
| } |
| if got, err := dec.Decrypt(ctx, ctxt); err != nil { |
| return err |
| } else if !bytes.Equal(got, ptxt) { |
| return fmt.Errorf("got plaintext %v, want %v", got, ptxt) |
| } |
| return nil |
| } |
| |
| var ( |
| google = newRoot("google") |
| params = google.Params() |
| key, _ = google.Extract(ctx, "google") |
| ctxt = encrypt(params, "google:$") |
| ) |
| // Verify that the ciphertext 'ctxt' can be decrypted using |
| // private key 'key' into the desired plaintext. |
| if err := decryptAndVerify(ctxt, key); err != nil { |
| t.Fatal(err) |
| } |
| |
| // Marshal and Unmarshal ciphertext 'ctxt' and verify that decryption |
| // with private key 'key' still succeeds. |
| var ( |
| newCtxt Ciphertext |
| wireCtxt WireCiphertext |
| ) |
| ctxt.ToWire(&wireCtxt) |
| newCtxt.FromWire(wireCtxt) |
| if err := decryptAndVerify(&newCtxt, key); err != nil { |
| t.Fatal(err) |
| } |
| |
| // Marshal and Unmarshal the private key 'key' and verify that decryption |
| // of 'ctxt' still succeeds. |
| var ( |
| newKey PrivateKey |
| wireKey WirePrivateKey |
| ) |
| if err := key.ToWire(&wireKey); err != nil { |
| t.Fatal(err) |
| } else if err = newKey.FromWire(wireKey); err != nil { |
| t.Fatal(err) |
| } |
| if err := decryptAndVerify(ctxt, &newKey); err != nil { |
| t.Fatal(err) |
| } |
| |
| // Marshal and Unmarshal the root params and verify that encryption |
| // still results in a ciphertext that can be decrypted with the private |
| // key 'key'. |
| var ( |
| newParams Params |
| wireParams WireParams |
| ) |
| if err := params.ToWire(&wireParams); err != nil { |
| t.Fatal(err) |
| } else if err = newParams.FromWire(wireParams); err != nil { |
| t.Fatal(err) |
| } |
| ctxt = encrypt(newParams, "google:$") |
| if err := decryptAndVerify(ctxt, key); err != nil { |
| t.Fatal(err) |
| } |
| } |