github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/vfile/vfile_test.go (about) 1 // Copyright 2020 the u-root 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 5 package vfile 6 7 import ( 8 "crypto/sha256" 9 "fmt" 10 "io/ioutil" 11 "os" 12 "path/filepath" 13 "reflect" 14 "strings" 15 "syscall" 16 "testing" 17 18 "golang.org/x/crypto/openpgp" 19 "golang.org/x/crypto/openpgp/errors" 20 ) 21 22 type signedFile struct { 23 signers []*openpgp.Entity 24 content string 25 } 26 27 func (s signedFile) write(path string) error { 28 f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0600) 29 if err != nil { 30 return err 31 } 32 defer f.Close() 33 34 if _, err := f.Write([]byte(s.content)); err != nil { 35 return err 36 } 37 38 sigf, err := os.OpenFile(fmt.Sprintf("%s.sig", path), os.O_RDWR|os.O_CREATE, 0600) 39 if err != nil { 40 return err 41 } 42 defer sigf.Close() 43 for _, signer := range s.signers { 44 if err := openpgp.DetachSign(sigf, signer, strings.NewReader(s.content), nil); err != nil { 45 return err 46 } 47 } 48 return nil 49 } 50 51 type normalFile struct { 52 content string 53 } 54 55 func (n normalFile) write(path string) error { 56 return ioutil.WriteFile(path, []byte(n.content), 0600) 57 } 58 59 func writeHashedFile(path, content string) ([]byte, error) { 60 c := []byte(content) 61 if err := ioutil.WriteFile(path, c, 0600); err != nil { 62 return nil, err 63 } 64 hash := sha256.Sum256(c) 65 return hash[:], nil 66 } 67 68 func TestOpenSignedFile(t *testing.T) { 69 key, err := openpgp.NewEntity("goog", "goog", "goog@goog", nil) 70 if err != nil { 71 t.Fatal(err) 72 } 73 ring := openpgp.EntityList{key} 74 75 key2, err := openpgp.NewEntity("goog2", "goog2", "goog@goog", nil) 76 if err != nil { 77 t.Fatal(err) 78 } 79 80 dir, err := ioutil.TempDir("", "opensignedfile") 81 if err != nil { 82 t.Fatal(err) 83 } 84 defer os.RemoveAll(dir) 85 86 signed := signedFile{ 87 signers: openpgp.EntityList{key}, 88 content: "foo", 89 } 90 signedPath := filepath.Join(dir, "signed_by_key") 91 if err := signed.write(signedPath); err != nil { 92 t.Fatal(err) 93 } 94 95 signed2 := signedFile{ 96 signers: openpgp.EntityList{key2}, 97 content: "foo", 98 } 99 signed2Path := filepath.Join(dir, "signed_by_key2") 100 if err := signed2.write(signed2Path); err != nil { 101 t.Fatal(err) 102 } 103 104 signed12 := signedFile{ 105 signers: openpgp.EntityList{key, key2}, 106 content: "foo", 107 } 108 signed12Path := filepath.Join(dir, "signed_by_both.sig") 109 if err := signed12.write(signed12Path); err != nil { 110 t.Fatal(err) 111 } 112 113 normalPath := filepath.Join(dir, "unsigned") 114 if err := ioutil.WriteFile(normalPath, []byte("foo"), 0777); err != nil { 115 t.Fatal(err) 116 } 117 118 for _, tt := range []struct { 119 desc string 120 path string 121 keyring openpgp.KeyRing 122 want error 123 isSignatureValid bool 124 }{ 125 { 126 desc: "signed file", 127 keyring: ring, 128 path: signedPath, 129 want: nil, 130 isSignatureValid: true, 131 }, 132 { 133 desc: "signed file w/ two signatures (key1 ring)", 134 keyring: ring, 135 path: signed12Path, 136 want: nil, 137 isSignatureValid: true, 138 }, 139 { 140 desc: "signed file w/ two signatures (key2 ring)", 141 keyring: openpgp.EntityList{key2}, 142 path: signed12Path, 143 want: nil, 144 isSignatureValid: true, 145 }, 146 { 147 desc: "nil keyring", 148 keyring: nil, 149 path: signed2Path, 150 want: ErrUnsigned{ 151 Path: signed2Path, 152 Err: ErrNoKeyRing, 153 }, 154 isSignatureValid: false, 155 }, 156 { 157 desc: "non-nil empty keyring", 158 keyring: openpgp.EntityList{}, 159 path: signed2Path, 160 want: ErrUnsigned{ 161 Path: signed2Path, 162 Err: errors.ErrUnknownIssuer, 163 }, 164 isSignatureValid: false, 165 }, 166 { 167 desc: "signed file does not match keyring", 168 keyring: openpgp.EntityList{key2}, 169 path: signedPath, 170 want: ErrUnsigned{ 171 Path: signedPath, 172 Err: errors.ErrUnknownIssuer, 173 }, 174 isSignatureValid: false, 175 }, 176 { 177 desc: "unsigned file", 178 keyring: ring, 179 path: normalPath, 180 want: ErrUnsigned{ 181 Path: normalPath, 182 Err: &os.PathError{ 183 Op: "open", 184 Path: fmt.Sprintf("%s.sig", normalPath), 185 Err: syscall.ENOENT, 186 }, 187 }, 188 isSignatureValid: false, 189 }, 190 { 191 desc: "file does not exist", 192 keyring: ring, 193 path: filepath.Join(dir, "foo"), 194 want: &os.PathError{ 195 Op: "open", 196 Path: filepath.Join(dir, "foo"), 197 Err: syscall.ENOENT, 198 }, 199 isSignatureValid: false, 200 }, 201 } { 202 t.Run(tt.desc, func(t *testing.T) { 203 f, gotErr := OpenSignedSigFile(tt.keyring, tt.path) 204 if !reflect.DeepEqual(gotErr, tt.want) { 205 t.Errorf("openSignedFile(%v, %q) = %v, want %v", tt.keyring, tt.path, gotErr, tt.want) 206 } 207 208 if isSignatureValid := (gotErr == nil); isSignatureValid != tt.isSignatureValid { 209 t.Errorf("isSignatureValid(%v) = %v, want %v", gotErr, isSignatureValid, tt.isSignatureValid) 210 } 211 212 // Make sure that the file is readable from position 0. 213 if f != nil { 214 content, err := ioutil.ReadAll(f) 215 if err != nil { 216 t.Errorf("Could not read content: %v", err) 217 } 218 if got := string(content); got != "foo" { 219 t.Errorf("ReadAll = %v, want \"foo\"", got) 220 } 221 } 222 }) 223 } 224 } 225 226 func TestOpenHashedFile(t *testing.T) { 227 dir, err := ioutil.TempDir("", "openhashedfile") 228 if err != nil { 229 t.Fatal(err) 230 } 231 defer os.RemoveAll(dir) 232 233 hashedPath := filepath.Join(dir, "hashed") 234 hash, err := writeHashedFile(hashedPath, "foo") 235 if err != nil { 236 t.Fatal(err) 237 } 238 239 emptyPath := filepath.Join(dir, "empty") 240 emptyHash, err := writeHashedFile(emptyPath, "") 241 if err != nil { 242 t.Fatal(err) 243 } 244 245 for _, tt := range []struct { 246 desc string 247 path string 248 hash []byte 249 want error 250 isHashValid bool 251 wantContent string 252 }{ 253 { 254 desc: "correct hash", 255 path: hashedPath, 256 hash: hash, 257 want: nil, 258 isHashValid: true, 259 wantContent: "foo", 260 }, 261 { 262 desc: "wrong hash", 263 path: hashedPath, 264 hash: []byte{0x99, 0x77}, 265 want: ErrInvalidHash{ 266 Path: hashedPath, 267 Err: ErrHashMismatch{ 268 Got: hash, 269 Want: []byte{0x99, 0x77}, 270 }, 271 }, 272 isHashValid: false, 273 wantContent: "foo", 274 }, 275 { 276 desc: "no hash", 277 path: hashedPath, 278 hash: []byte{}, 279 want: ErrInvalidHash{ 280 Path: hashedPath, 281 Err: ErrNoExpectedHash, 282 }, 283 isHashValid: false, 284 wantContent: "foo", 285 }, 286 { 287 desc: "empty file", 288 path: emptyPath, 289 hash: emptyHash, 290 want: nil, 291 isHashValid: true, 292 wantContent: "", 293 }, 294 { 295 desc: "nonexistent file", 296 path: filepath.Join(dir, "doesnotexist"), 297 hash: nil, 298 want: &os.PathError{ 299 Op: "open", 300 Path: filepath.Join(dir, "doesnotexist"), 301 Err: syscall.ENOENT, 302 }, 303 isHashValid: false, 304 }, 305 } { 306 t.Run(tt.desc, func(t *testing.T) { 307 f, err := OpenHashedFile256(tt.path, tt.hash) 308 if !reflect.DeepEqual(err, tt.want) { 309 t.Errorf("OpenHashedFile256(%s, %x) = %v, want %v", tt.path, tt.hash, err, tt.want) 310 } 311 312 if isHashValid := (err == nil); isHashValid != tt.isHashValid { 313 t.Errorf("isHashValid(%v) = %v, want %v", err, isHashValid, tt.isHashValid) 314 } 315 316 // Make sure that the file is readable from position 0. 317 if f != nil { 318 content, err := ioutil.ReadAll(f) 319 if err != nil { 320 t.Errorf("Could not read content: %v", err) 321 } 322 if got := string(content); got != tt.wantContent { 323 t.Errorf("ReadAll = %v, want %s", got, tt.wantContent) 324 } 325 } 326 }) 327 } 328 }