Jiri Simsa | d7616c9 | 2015-03-24 23:44:30 -0700 | [diff] [blame] | 1 | // Copyright 2015 The Vanadium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
Asim Shankar | ae8d4c5 | 2014-10-08 13:03:31 -0700 | [diff] [blame] | 5 | package security |
| 6 | |
| 7 | import ( |
Suharsh Sivakumar | aca1c32 | 2014-10-21 11:27:32 -0700 | [diff] [blame] | 8 | "crypto/ecdsa" |
| 9 | "crypto/elliptic" |
| 10 | "crypto/rand" |
Asim Shankar | ae8d4c5 | 2014-10-08 13:03:31 -0700 | [diff] [blame] | 11 | "io/ioutil" |
| 12 | "os" |
Suharsh Sivakumar | aca1c32 | 2014-10-21 11:27:32 -0700 | [diff] [blame] | 13 | "path" |
gauthamt | f826393 | 2014-12-16 10:59:09 -0800 | [diff] [blame] | 14 | "reflect" |
Asim Shankar | ae8d4c5 | 2014-10-08 13:03:31 -0700 | [diff] [blame] | 15 | "testing" |
gauthamt | f826393 | 2014-12-16 10:59:09 -0800 | [diff] [blame] | 16 | |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame] | 17 | "v.io/v23/security" |
Mike Burrows | 7f7088d | 2015-03-25 13:05:00 -0700 | [diff] [blame] | 18 | "v.io/v23/verror" |
Asim Shankar | ae8d4c5 | 2014-10-08 13:03:31 -0700 | [diff] [blame] | 19 | ) |
| 20 | |
Suharsh Sivakumar | aca1c32 | 2014-10-21 11:27:32 -0700 | [diff] [blame] | 21 | func TestLoadPersistentPrincipal(t *testing.T) { |
| 22 | // If the directory does not exist want os.IsNotExists. |
| 23 | _, err := LoadPersistentPrincipal("/tmp/fake/path/", nil) |
| 24 | if !os.IsNotExist(err) { |
| 25 | t.Errorf("invalid path should return does not exist error, instead got %v", err) |
| 26 | } |
| 27 | // If the key file exists and is unencrypted we should succeed. |
| 28 | dir := generatePEMFile(nil) |
| 29 | if _, err = LoadPersistentPrincipal(dir, nil); err != nil { |
| 30 | t.Errorf("unencrypted LoadPersistentPrincipal should have succeeded: %v", err) |
| 31 | } |
| 32 | os.RemoveAll(dir) |
| 33 | |
| 34 | // If the private key file exists and is encrypted we should succeed with correct passphrase. |
| 35 | passphrase := []byte("passphrase") |
| 36 | incorrect_passphrase := []byte("incorrect_passphrase") |
| 37 | dir = generatePEMFile(passphrase) |
| 38 | if _, err = LoadPersistentPrincipal(dir, passphrase); err != nil { |
| 39 | t.Errorf("encrypted LoadPersistentPrincipal should have succeeded: %v", err) |
| 40 | } |
| 41 | // and fail with an incorrect passphrase. |
| 42 | if _, err = LoadPersistentPrincipal(dir, incorrect_passphrase); err == nil { |
| 43 | t.Errorf("encrypted LoadPersistentPrincipal with incorrect passphrase should fail") |
| 44 | } |
Mike Burrows | 7f7088d | 2015-03-25 13:05:00 -0700 | [diff] [blame] | 45 | // and return ErrBadPassphrase if the passphrase is nil. |
| 46 | if _, err = LoadPersistentPrincipal(dir, nil); verror.ErrorID(err) != ErrBadPassphrase.ID { |
| 47 | t.Errorf("encrypted LoadPersistentPrincipal with nil passphrase should return ErrBadPassphrase: %v", err) |
Suharsh Sivakumar | aca1c32 | 2014-10-21 11:27:32 -0700 | [diff] [blame] | 48 | } |
| 49 | os.RemoveAll(dir) |
| 50 | } |
| 51 | |
| 52 | func TestCreatePersistentPrincipal(t *testing.T) { |
| 53 | tests := []struct { |
| 54 | Message, Passphrase []byte |
| 55 | }{ |
| 56 | {[]byte("unencrypted"), nil}, |
| 57 | {[]byte("encrypted"), []byte("passphrase")}, |
| 58 | } |
| 59 | for _, test := range tests { |
| 60 | testCreatePersistentPrincipal(t, test.Message, test.Passphrase) |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | func testCreatePersistentPrincipal(t *testing.T, message, passphrase []byte) { |
Asim Shankar | ae8d4c5 | 2014-10-08 13:03:31 -0700 | [diff] [blame] | 65 | // Persistence of the BlessingRoots and BlessingStore objects is |
| 66 | // tested in other files. Here just test the persistence of the key. |
Suharsh Sivakumar | aca1c32 | 2014-10-21 11:27:32 -0700 | [diff] [blame] | 67 | dir, err := ioutil.TempDir("", "TestCreatePersistentPrincipal") |
Asim Shankar | ae8d4c5 | 2014-10-08 13:03:31 -0700 | [diff] [blame] | 68 | if err != nil { |
| 69 | t.Fatal(err) |
| 70 | } |
| 71 | defer os.RemoveAll(dir) |
| 72 | |
Suharsh Sivakumar | aca1c32 | 2014-10-21 11:27:32 -0700 | [diff] [blame] | 73 | p, err := CreatePersistentPrincipal(dir, passphrase) |
Asim Shankar | ae8d4c5 | 2014-10-08 13:03:31 -0700 | [diff] [blame] | 74 | if err != nil { |
| 75 | t.Fatal(err) |
| 76 | } |
gauthamt | b7bb39b | 2014-11-10 11:40:41 -0800 | [diff] [blame] | 77 | _, err = CreatePersistentPrincipal(dir, passphrase) |
Ankur | 4704f5f | 2014-10-23 12:40:54 -0700 | [diff] [blame] | 78 | if err == nil { |
| 79 | t.Error("CreatePersistentPrincipal passed unexpectedly") |
| 80 | } |
Ankur | 4704f5f | 2014-10-23 12:40:54 -0700 | [diff] [blame] | 81 | |
Asim Shankar | ae8d4c5 | 2014-10-08 13:03:31 -0700 | [diff] [blame] | 82 | sig, err := p.Sign(message) |
| 83 | if err != nil { |
| 84 | t.Fatal(err) |
| 85 | } |
| 86 | |
Ankur | 4704f5f | 2014-10-23 12:40:54 -0700 | [diff] [blame] | 87 | p2, err := LoadPersistentPrincipal(dir, passphrase) |
Asim Shankar | ae8d4c5 | 2014-10-08 13:03:31 -0700 | [diff] [blame] | 88 | if err != nil { |
Suharsh Sivakumar | 8a7fba4 | 2014-10-27 12:40:48 -0700 | [diff] [blame] | 89 | t.Fatalf("%s failed: %v", message, err) |
Asim Shankar | ae8d4c5 | 2014-10-08 13:03:31 -0700 | [diff] [blame] | 90 | } |
Ankur | 4704f5f | 2014-10-23 12:40:54 -0700 | [diff] [blame] | 91 | if !sig.Verify(p2.PublicKey(), message) { |
Suharsh Sivakumar | aca1c32 | 2014-10-21 11:27:32 -0700 | [diff] [blame] | 92 | t.Errorf("%s failed: p.PublicKey=%v, p2.PublicKey=%v", message, p.PublicKey(), p2.PublicKey()) |
Asim Shankar | ae8d4c5 | 2014-10-08 13:03:31 -0700 | [diff] [blame] | 93 | } |
| 94 | } |
Suharsh Sivakumar | aca1c32 | 2014-10-21 11:27:32 -0700 | [diff] [blame] | 95 | |
| 96 | func generatePEMFile(passphrase []byte) (dir string) { |
| 97 | dir, err := ioutil.TempDir("", "TestLoadPersistentPrincipal") |
| 98 | if err != nil { |
| 99 | panic(err) |
| 100 | } |
| 101 | key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) |
| 102 | if err != nil { |
| 103 | panic(err) |
| 104 | } |
| 105 | f, err := os.Create(path.Join(dir, privateKeyFile)) |
| 106 | if err != nil { |
| 107 | panic(err) |
| 108 | } |
| 109 | defer f.Close() |
Ankur | 73e7a93 | 2014-10-24 15:57:03 -0700 | [diff] [blame] | 110 | if err = SavePEMKey(f, key, passphrase); err != nil { |
Suharsh Sivakumar | aca1c32 | 2014-10-21 11:27:32 -0700 | [diff] [blame] | 111 | panic(err) |
| 112 | } |
| 113 | return dir |
| 114 | } |
gauthamt | f826393 | 2014-12-16 10:59:09 -0800 | [diff] [blame] | 115 | |
| 116 | func TestPrincipalBlessingsByName(t *testing.T) { |
| 117 | var p1, p2, p3 security.Principal |
| 118 | var err error |
| 119 | |
| 120 | if p1, err = NewPrincipal(); err != nil { |
| 121 | t.Fatal(err) |
| 122 | } |
| 123 | if p2, err = NewPrincipal(); err != nil { |
| 124 | t.Fatal(err) |
| 125 | } |
| 126 | alice, err := p1.BlessSelf("alice") |
| 127 | if err != nil { |
| 128 | t.Fatal(err) |
| 129 | } |
| 130 | p2.AddToRoots(alice) |
| 131 | var aliceworkfriend, alicegymfriend, aliceworkboss security.Blessings |
| 132 | |
| 133 | if aliceworkfriend, err = p1.Bless(p2.PublicKey(), alice, "work/friend", security.UnconstrainedUse()); err != nil { |
| 134 | t.Errorf("Bless(work/friend) failed: %v", err) |
| 135 | } |
| 136 | p2.BlessingStore().Set(aliceworkfriend, "alice/work/friend") |
| 137 | if alicegymfriend, err = p1.Bless(p2.PublicKey(), alice, "gym/friend", security.UnconstrainedUse()); err != nil { |
| 138 | t.Errorf("Bless(gym/friend) failed: %v", err) |
| 139 | } |
| 140 | p2.BlessingStore().Set(alicegymfriend, "alice/gym/friend") |
| 141 | if aliceworkboss, err = p1.Bless(p2.PublicKey(), alice, "work/boss", security.UnconstrainedUse()); err != nil { |
| 142 | t.Errorf("Bless(work/friend) failed: %v", err) |
| 143 | } |
| 144 | p2.BlessingStore().Set(aliceworkboss, "alice/work/boss") |
| 145 | |
| 146 | // Blessing from an untrusted principal that should never be returned |
| 147 | if p3, err = NewPrincipal(); err != nil { |
| 148 | t.Fatal(err) |
| 149 | } |
| 150 | fake, err := p3.BlessSelf("alice") |
| 151 | if err != nil { |
| 152 | t.Fatal(err) |
| 153 | } |
| 154 | fakefriend, err := p3.Bless(p2.PublicKey(), fake, "work/friend", security.UnconstrainedUse()) |
| 155 | if err != nil { |
| 156 | t.Errorf("Bless(work/friend) failed: %v", err) |
| 157 | } |
| 158 | _, err = p2.BlessingStore().Set(fakefriend, "fake/work/friend") |
| 159 | |
| 160 | tests := []struct { |
| 161 | matched []security.Blessings |
| 162 | pattern security.BlessingPattern |
| 163 | }{ |
| 164 | { |
| 165 | matched: []security.Blessings{aliceworkfriend, aliceworkboss}, |
Ankur | 78b8b2a | 2015-02-04 20:16:28 -0800 | [diff] [blame] | 166 | pattern: "alice/work", |
gauthamt | f826393 | 2014-12-16 10:59:09 -0800 | [diff] [blame] | 167 | }, |
| 168 | { |
| 169 | matched: []security.Blessings{aliceworkfriend}, |
| 170 | pattern: "alice/work/friend", |
| 171 | }, |
| 172 | { |
| 173 | matched: []security.Blessings{alicegymfriend}, |
| 174 | pattern: "alice/gym/friend", |
| 175 | }, |
| 176 | { |
| 177 | matched: []security.Blessings{aliceworkfriend, alicegymfriend, aliceworkboss}, |
Ankur | 78b8b2a | 2015-02-04 20:16:28 -0800 | [diff] [blame] | 178 | pattern: "alice", |
gauthamt | f826393 | 2014-12-16 10:59:09 -0800 | [diff] [blame] | 179 | }, |
| 180 | { |
| 181 | matched: []security.Blessings{aliceworkfriend, alicegymfriend, aliceworkboss}, |
Ankur | 78b8b2a | 2015-02-04 20:16:28 -0800 | [diff] [blame] | 182 | pattern: security.AllPrincipals, |
gauthamt | f826393 | 2014-12-16 10:59:09 -0800 | [diff] [blame] | 183 | }, |
| 184 | { |
| 185 | matched: nil, |
Ankur | 78b8b2a | 2015-02-04 20:16:28 -0800 | [diff] [blame] | 186 | pattern: "alice/school", |
gauthamt | f826393 | 2014-12-16 10:59:09 -0800 | [diff] [blame] | 187 | }, |
| 188 | } |
| 189 | |
| 190 | for _, test := range tests { |
| 191 | matched := p2.BlessingsByName(test.pattern) |
| 192 | if len(matched) != len(test.matched) { |
| 193 | t.Errorf("BlessingsByName(%s) did not return expected number of matches wanted:%d got:%d", test.pattern, len(test.matched), len(matched)) |
| 194 | } |
| 195 | for _, m := range matched { |
| 196 | found := false |
| 197 | for _, tm := range test.matched { |
| 198 | if reflect.DeepEqual(m, tm) { |
| 199 | found = true |
| 200 | break |
| 201 | } |
| 202 | } |
| 203 | if !found { |
| 204 | t.Errorf("Invalid blessing was returned as a match:%v for pattern:%s", m, test.pattern) |
| 205 | } |
| 206 | } |
| 207 | } |
| 208 | } |