├── LICENSE ├── Makefile ├── README.md ├── asntest └── asntest.go ├── authenticode ├── authenticode.go ├── authenticode_test.go ├── checksum.go ├── checksum_test.go ├── multireader.go └── testdata │ ├── db.key │ ├── db.pem │ ├── old_authenticode_implementation.der │ ├── test.authenticode.signed │ ├── test.pecoff │ ├── test.pecoff.pk7 │ └── test.pecoff.signed ├── cmd ├── appendmicrosoft │ ├── certs │ │ ├── MicCorUEFCA2011_2011-06-27.crt │ │ └── MicWinProPCA2011_2011-10-19.crt │ └── main.go ├── efianalyze │ └── main.go ├── gosiglist │ └── main.go ├── gosign │ └── main.go ├── govarsign │ └── main.go ├── goverify │ └── main.go └── gowritevar │ └── main.go ├── efi ├── attr │ ├── attr.go │ ├── attr_linux.go │ └── attr_other.go ├── attributes │ └── attributes.go ├── device │ ├── acpi_device.go │ ├── device.go │ ├── device_test.go │ ├── hardware_device.go │ ├── media_device.go │ └── messaging_device.go ├── efi.go ├── efitest │ ├── efitest.go │ └── files.go ├── fs │ └── fs.go ├── signature │ ├── signature_database.go │ ├── signature_database_test.go │ ├── signature_list.go │ ├── signature_list_test.go │ ├── testdata │ │ ├── db │ │ ├── dbdefault │ │ ├── kek │ │ ├── kekdefault │ │ ├── pk │ │ └── pkdefault │ ├── varsign.go │ └── varsign_test.go └── util │ ├── certs.go │ ├── efitime.go │ ├── guid.go │ ├── guid_test.go │ ├── util.go │ └── util_test.go ├── efivar └── efivar.go ├── efivarfs ├── efifs.go ├── efivarfs.go ├── efivarfs_test.go ├── fswrapper │ └── fswrapper.go ├── testfs │ └── testfs.go └── types.go ├── go.mod ├── go.sum ├── pkcs7 ├── pkcs7.go ├── pkcs7_test.go ├── testdata │ ├── old_pkcs7_implementation.der │ └── test.signed └── utils_test.go └── tests ├── README.md ├── binaries └── HelloWorld.efi ├── bzImage ├── data ├── binary │ ├── HelloWorld.efi │ ├── HelloWorld.efi.signed │ ├── linuxx64.efi.stub │ └── test.pecoff ├── boot │ ├── Boot0000-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot0001-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot0010-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot0011-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot0012-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot0013-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot0014-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot0015-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot0016-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot0017-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot0018-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot0019-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot001A-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot001B-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot001C-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot001D-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot001E-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot001F-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot0020-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot0021-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot0022-8be4df61-93ca-11d2-aa0d-00e098032b8c │ └── Boot0023-8be4df61-93ca-11d2-aa0d-00e098032b8c ├── bootorder │ ├── BootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8c │ └── BootOrderDefault-0b7646a4-6b44-4332-8588-c8998117f2ef ├── misc │ ├── SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c │ └── SetupMode-8be4df61-93ca-11d2-aa0d-00e098032b8c └── signatures │ ├── secureboot │ ├── GUID │ └── keys │ │ ├── KEK │ │ ├── KEK.der │ │ ├── KEK.key │ │ └── KEK.pem │ │ ├── PK │ │ ├── PK.der │ │ ├── PK.key │ │ └── PK.pem │ │ └── db │ │ ├── db.der │ │ ├── db.key │ │ └── db.pem │ ├── siglist │ ├── KEK.der.esl │ ├── PK.der.esl │ └── db.der.esl │ ├── siglistchecksum │ └── sha256.bin.siglist │ ├── sigsupport │ ├── SignatureSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c │ └── SignatureSupport2-8be4df61-93ca-11d2-aa0d-00e098032b8c │ └── varsign │ ├── KEK.auth │ ├── PK.auth │ └── db.auth ├── integration_test.go ├── ovmf ├── OVMF_VARS.fd └── keys │ ├── KEK │ ├── KEK.auth │ ├── KEK.der │ ├── KEK.der.esl │ ├── KEK.key │ └── KEK.pem │ ├── PK │ ├── PK.auth │ ├── PK.der │ ├── PK.der.esl │ ├── PK.key │ └── PK.pem │ └── db │ ├── db.auth │ ├── db.der │ ├── db.der.esl │ ├── db.key │ └── db.pem ├── start-qemu.sh └── tests ├── enroll_keys └── enroll_keys_test.go ├── modify_dbx └── modify_dbx_test.go ├── remove_pk └── remove_pk_test.go └── secure_boot_enabled └── secure_boot_enabled_test.go /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2020 Morten Linderud 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation 6 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the 8 | Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included 11 | in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 15 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 18 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 19 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PREFIX ?= /usr/local 2 | BINDIR ?= $(PREFIX)/bin 3 | SHRDIR ?= $(PREFIX)/share 4 | DOCDIR ?= $(PREFIX)/share/doc 5 | MANDIR ?= $(PREFIX)/share/man 6 | MANS = $(basename $(wildcard docs/*.txt)) 7 | 8 | CGO_LDFLAGS := '$(LDFLAGS)' 9 | CGO_CPPFLAGS := '$(CPPFLAGS)' 10 | CGO_CFLAGS := '$(CFLAGS)' 11 | GOFLAGS ?= -buildmode=pie -trimpath 12 | 13 | SOURCES = $(shell go list -f '{{range .GoFiles}}{{$$.Dir}}/{{.}} {{end}}' ./...) 14 | 15 | all: build 16 | build: gosiglist 17 | 18 | gosiglist: $(SOURCES) 19 | go build -o $@ ./cmd/$@/... 20 | 21 | .PHONY: lint 22 | lint: 23 | go vet ./... 24 | staticcheck ./... 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | go-uefi 2 | ======= 3 | 4 | A UEFI library written to interact with Linux efivars. The goal is to provide a 5 | Go library to enable application authors to better utilize secure boot and UEFI. 6 | This also includes unit-testing to ensure the library is compatible with 7 | existing tools, and integration tests to ensure the library is able of deal with 8 | future UEFI revisions. 9 | 10 | 11 | # Features 12 | * Implements most Secure Boot relevant structs as defined in UEFI Spec Version 2.8 Errata A (February 14th 2020). 13 | * PE/COFF Checksumming. 14 | * Microsoft Authenticode signing. 15 | * A subset of PKCS7 16 | * Working with EFI_SIGNATURE_LIST and EFI_SIGNATURE_DATABASE. 17 | * Integration tests utilizing [vmtest](https://github.com/hugelgupf/vmtest) and tianocore. 18 | * Virtual filesystem support for easier testing. 19 | 20 | 21 | # Examples 22 | 23 | Some example can be found under `cmd/`. 24 | 25 | # Code Examples 26 | 27 | ## Append signatures to db 28 | 29 | ```go 30 | package main 31 | import ( 32 | "github.com/foxboron/go-uefi/efi/signature" 33 | "github.com/foxboron/go-uefi/efi/util" 34 | "github.com/foxboron/go-uefi/efivar" 35 | "github.com/foxboron/go-uefi/efivarfs" 36 | ) 37 | 38 | var ( 39 | cert, _ = util.ReadKeyFromFile("signing.key") 40 | key, _ = util.ReadCertFromFile("signing.cert") 41 | sigdata = signature.SignatureData{ 42 | Owner: util.EFIGUID{Data1: 0xc1095e1b, Data2: 0x8a3b, Data3: 0x4cf5, Data4: [8]uint8{0x9d, 0x4a, 0xaf, 0xc7, 0xd7, 0x5d, 0xca, 0x68}}, 43 | Data: []uint8{}} 44 | ) 45 | 46 | func main() { 47 | efifs := efivarfs.NewFS().Open() 48 | db, _ := efifs.Getdb() 49 | db.AppendSignature(signature.CERT_SHA256_GUID, &sigdata) 50 | efifs.WriteSignedUpdate(efivar.Db, db, key, cert) 51 | } 52 | ``` 53 | 54 | ## Use a in-memory efivarfs for tests 55 | 56 | ```go 57 | package main 58 | import ( 59 | "github.com/foxboron/go-uefi/efi" 60 | "github.com/foxboron/go-uefi/efi/efitest" 61 | "github.com/foxboron/go-uefi/efi/signature" 62 | "github.com/foxboron/go-uefi/efivarfs" 63 | ) 64 | 65 | func TestSecureBootOn(t *testing.T) { 66 | efifs := efivarfs.NewTestFS(). 67 | With(efitest.SecureBootOn()). 68 | Open() 69 | ok, err := efifs.GetSetupMode() 70 | if err != nil { 71 | t.Fatalf("%v", err) 72 | } 73 | if !ok { 74 | t.Fatalf("Secure Boot is not enabled") 75 | } 76 | } 77 | ``` 78 | 79 | ## Sign UEFI binary 80 | ```go 81 | package main 82 | import ( 83 | "github.com/foxboron/go-uefi/authenticode" 84 | "github.com/foxboron/go-uefi/efi/util" 85 | ) 86 | 87 | var ( 88 | key, _ := util.ReadKeyFromFile("signing.key") 89 | cert, _ := util.ReadCertFromFile("signing.cert") 90 | ) 91 | 92 | func main(){ 93 | peFile, _ := os.ReadFile("somefile") 94 | file, _ := authenticode.Parse(peFile) 95 | file.Sign(key, cert) 96 | os.WriteFile("somefile.signed", file.Bytes(), 0644) 97 | } 98 | ``` 99 | 100 | ## Checksum UEFI executable 101 | ```go 102 | package main 103 | import ( 104 | "github.com/foxboron/go-uefi/authenticode" 105 | ) 106 | 107 | func main(){ 108 | peFile, _ := os.ReadFile("somefile") 109 | file, _ := authenticode.Parse(peFile) 110 | checksum := file.Hash(crypto.SHA256) 111 | fmt.Printf("%x\n", checksum) 112 | } 113 | ``` 114 | -------------------------------------------------------------------------------- /asntest/asntest.go: -------------------------------------------------------------------------------- 1 | // Package for helper function in the test suite 2 | package asntest 3 | 4 | import ( 5 | "bytes" 6 | "crypto/rand" 7 | "crypto/rsa" 8 | "crypto/x509" 9 | "crypto/x509/pkix" 10 | "encoding/pem" 11 | "fmt" 12 | "log" 13 | "math/big" 14 | "os" 15 | "os/exec" 16 | "path" 17 | "testing" 18 | ) 19 | 20 | // Init a basic set of certs 21 | func InitCert() (*x509.Certificate, *rsa.PrivateKey) { 22 | serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128)) 23 | if err != nil { 24 | log.Fatalf("Failed to generate serial number: %v", err) 25 | } 26 | c := x509.Certificate{ 27 | SerialNumber: serialNumber, 28 | PublicKeyAlgorithm: x509.RSA, 29 | SignatureAlgorithm: x509.SHA256WithRSA, 30 | Subject: pkix.Name{ 31 | Country: []string{"TEST STRING"}, 32 | }, 33 | } 34 | 35 | priv, err := rsa.GenerateKey(rand.Reader, 4096) 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | 40 | derBytes, _ := x509.CreateCertificate(rand.Reader, &c, &c, &priv.PublicKey, priv) 41 | cert, _ := x509.ParseCertificate(derBytes) 42 | return cert, priv 43 | } 44 | 45 | func CertToBytes(cert *x509.Certificate) []byte { 46 | certOut := new(bytes.Buffer) 47 | if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}); err != nil { 48 | log.Fatalf("Failed to write data to cert.pem: %v", err) 49 | } 50 | return certOut.Bytes() 51 | } 52 | 53 | func RSAToBytes(r *rsa.PrivateKey) []byte { 54 | privateKeyBytes, err := x509.MarshalPKCS8PrivateKey(r) 55 | if err != nil { 56 | log.Fatalf("Unable to marshal private key: %v", err) 57 | } 58 | keyOut := new(bytes.Buffer) 59 | if err := pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: privateKeyBytes}); err != nil { 60 | log.Fatalf("Failed to write data to key.pem: %v", err) 61 | } 62 | return keyOut.Bytes() 63 | } 64 | 65 | func Asn1Parse(t *testing.T, buf []byte) []byte { 66 | f := path.Join(t.TempDir(), "asn1.der") 67 | os.WriteFile(f, buf, 0644) 68 | out, err := exec.Command("openssl", "asn1parse", "-in", f, "-inform", "der", "-dump", "-i").Output() 69 | if err != nil { 70 | fmt.Println(string(out)) 71 | log.Fatal(err) 72 | } 73 | return out 74 | } 75 | 76 | func DiffText(t *testing.T, a, b []byte) { 77 | aFile := path.Join(t.TempDir(), "a.der") 78 | bFile := path.Join(t.TempDir(), "b.der") 79 | os.WriteFile(aFile, a, 0644) 80 | os.WriteFile(bFile, b, 0644) 81 | out, _ := exec.Command("git", "diff", "--color=always", "--no-index", "--", aFile, bFile).Output() 82 | if len(out) > 0 { 83 | fmt.Println(string(a)) 84 | fmt.Println(string(b)) 85 | fmt.Println(string(out)) 86 | } 87 | } 88 | 89 | func Asn1Compare(t *testing.T, a, b []byte) { 90 | a = Asn1Parse(t, a) 91 | b = Asn1Parse(t, b) 92 | DiffText(t, a, b) 93 | } 94 | -------------------------------------------------------------------------------- /authenticode/authenticode.go: -------------------------------------------------------------------------------- 1 | // Package authenticode implements the Microsoft Authenticode standard. 2 | // 3 | // It allows parsing, verifying and signing of PE/COFF binaries. 4 | package authenticode 5 | 6 | import ( 7 | "bytes" 8 | "crypto" 9 | "crypto/x509" 10 | "crypto/x509/pkix" 11 | encasn1 "encoding/asn1" 12 | "errors" 13 | "fmt" 14 | "hash" 15 | "io" 16 | 17 | "golang.org/x/crypto/cryptobyte" 18 | "golang.org/x/crypto/cryptobyte/asn1" 19 | 20 | "github.com/foxboron/go-uefi/pkcs7" 21 | ) 22 | 23 | var ( 24 | OIDSpcIndirectDataContent = encasn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 1, 4} 25 | OIDSpcPEImageDataObjID = encasn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 1, 15} 26 | OIDMicrosoftIndividualCodeSigning = encasn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 1, 21} 27 | ) 28 | 29 | // Authenticode represents an authenticode signature. 30 | type Authenticode struct { 31 | Pkcs *pkcs7.PKCS7 32 | Algid *pkix.AlgorithmIdentifier 33 | Digest []byte 34 | } 35 | 36 | // Verify validates an authenticode signature. 37 | // Note it doesn't validate x509 certificate chains. 38 | func (a *Authenticode) Verify(cert *x509.Certificate, img io.Reader) (bool, error) { 39 | var h hash.Hash 40 | switch { 41 | case a.Algid.Algorithm.Equal(pkcs7.OIDDigestAlgorithmSHA256): 42 | h = crypto.SHA256.New() 43 | default: 44 | return false, errors.New("unsupported hashing function") 45 | } 46 | 47 | if h.Size() != len(a.Digest) { 48 | return false, errors.New("wrong block size") 49 | } 50 | 51 | if _, err := io.Copy(h, img); err != nil { 52 | return false, err 53 | } 54 | 55 | digest := h.Sum(nil) 56 | if !bytes.Equal(digest, a.Digest) { 57 | return false, errors.New("incorrect digest") 58 | } 59 | return a.Pkcs.Verify(cert) 60 | } 61 | 62 | // CreateSpcIndirectDataContent creates the SPCIndirectDataContent container as 63 | // specified int he Authenticode standard. 64 | func CreateSpcIndirectDataContent(digest []byte, alg crypto.Hash) ([]byte, error) { 65 | var b cryptobyte.Builder 66 | 67 | // data SpcAttributeTypeAndOptionalValue, 68 | b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) { 69 | 70 | // type ObjectID, 71 | b.AddASN1ObjectIdentifier(OIDSpcPEImageDataObjID) 72 | 73 | // SpcPeImageData ::= SEQUENCE { 74 | b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) { 75 | 76 | // flags SpcPeImageFlags DEFAULT { includeResources }, 77 | // SpcPeImageFlags ::= BIT STRING { 78 | // includeResources (0), 79 | // includeDebugInfo (1), 80 | // includeImportAddressTable (2) 81 | // } 82 | // We could also pass '0' but my reference implementation has one NULL byte, and not two NULL bytes. 83 | b.AddASN1BitString(nil) 84 | 85 | // file SpcLink 86 | // SpcLink ::= CHOICE { 87 | // url [0] IMPLICIT IA5STRING, 88 | // moniker [1] IMPLICIT SpcSerializedObject, 89 | // file [2] EXPLICIT SpcString 90 | // } --#public-- 91 | b.AddASN1(asn1.Tag(0).ContextSpecific().Constructed(), func(b *cryptobyte.Builder) { 92 | 93 | // file [2] EXPLICIT SpcString 94 | b.AddASN1(asn1.Tag(2).ContextSpecific().Constructed(), func(b *cryptobyte.Builder) { 95 | 96 | // unicode [0] IMPLICIT BMPSTRING, 97 | b.AddASN1(asn1.Tag(0).ContextSpecific(), func(b *cryptobyte.Builder) { 98 | 99 | // <<>>, but with null bytes after each bytes. idk why. 100 | s := []byte{0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x4f, 0x00, 0x62, 101 | 0x00, 0x73, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x74, 102 | 0x00, 0x65, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3E} 103 | b.AddBytes(s) 104 | 105 | }) 106 | }) 107 | }) 108 | }) 109 | }) 110 | 111 | // DigestInfo ::= SEQUENCE { 112 | b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) { 113 | 114 | // digestAlgorithm AlgorithmIdentifier, 115 | b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) { 116 | 117 | // digestAlgorithm AlgorithmIdentifier, 118 | b.AddASN1ObjectIdentifier(pkcs7.OIDDigestAlgorithmSHA256) 119 | 120 | // Add explicit null, I believe it's needed by picky UEFI things 121 | b.AddASN1NULL() 122 | }) 123 | 124 | // digest OCTETSTRING 125 | b.AddASN1OctetString(digest) 126 | }) 127 | 128 | return b.Bytes() 129 | } 130 | 131 | // SignAuthenticode signs a digest with the SPC Indirect Data Content as 132 | // specified by the authenticode standard. 133 | func SignAuthenticode(signer crypto.Signer, cert *x509.Certificate, digest io.Reader, alg crypto.Hash) ([]byte, error) { 134 | h := alg.New() 135 | 136 | if _, err := io.Copy(h, digest); err != nil { 137 | return nil, err 138 | } 139 | 140 | b, err := CreateSpcIndirectDataContent(h.Sum(nil), alg) 141 | if err != nil { 142 | return nil, fmt.Errorf("failed creating SpcIndirectDataContent: %v", err) 143 | } 144 | return pkcs7.SignPKCS7(signer, cert, OIDSpcIndirectDataContent, b) 145 | } 146 | 147 | // ParseAuthenticode parses an Authenticode signature. 148 | func ParseAuthenticode(b []byte) (*Authenticode, error) { 149 | var auth Authenticode 150 | pkcs, err := pkcs7.ParsePKCS7(b) 151 | if err != nil { 152 | return nil, fmt.Errorf("failed parsing authenticode: %v", err) 153 | } 154 | 155 | if !pkcs.OID.Equal(OIDSpcIndirectDataContent) { 156 | return nil, fmt.Errorf("not an authenticode siganture") 157 | } 158 | 159 | auth.Pkcs = pkcs 160 | 161 | der := cryptobyte.String(pkcs.ContentInfo) 162 | if !der.ReadASN1(&der, asn1.SEQUENCE) { 163 | return nil, errors.New("no spcindirectdatacontent") 164 | } 165 | 166 | var spcdata cryptobyte.String 167 | if !der.ReadASN1(&spcdata, asn1.SEQUENCE) { 168 | return nil, errors.New("no spcindirectdatacontent") 169 | } 170 | 171 | var dtype encasn1.ObjectIdentifier 172 | if !spcdata.ReadASN1ObjectIdentifier(&dtype) { 173 | return nil, errors.New("missing objectid type") 174 | } 175 | 176 | // TODO: Apparently microsoft sometimes uses a special OID for shim keys 177 | if !dtype.Equal(OIDSpcPEImageDataObjID) && !dtype.Equal(OIDMicrosoftIndividualCodeSigning) { 178 | return nil, fmt.Errorf("incorrect, expected %v, got %v", OIDSpcIndirectDataContent, dtype) 179 | } 180 | 181 | // TODO: We don't care about the next stuff in spcdata 182 | if !spcdata.SkipASN1(asn1.SEQUENCE) { 183 | return nil, errors.New("no spcpeimagedata") 184 | } 185 | 186 | if !der.ReadASN1(&der, asn1.SEQUENCE) { 187 | return nil, errors.New("no spcindirectdatacontent") 188 | } 189 | 190 | algid, err := pkcs7.ParseAlgorithmIdentifier(&der) 191 | if err != nil { 192 | return nil, fmt.Errorf("failed parsing DigestInfo: %v", err) 193 | } 194 | auth.Algid = algid 195 | 196 | var digest cryptobyte.String 197 | if !der.ReadASN1(&digest, asn1.OCTET_STRING) { 198 | return nil, errors.New("no spcindirectdatacontent") 199 | } 200 | 201 | auth.Digest = digest 202 | 203 | return &auth, err 204 | } 205 | -------------------------------------------------------------------------------- /authenticode/authenticode_test.go: -------------------------------------------------------------------------------- 1 | package authenticode 2 | 3 | import ( 4 | "bytes" 5 | "crypto" 6 | "os" 7 | "testing" 8 | 9 | "github.com/foxboron/go-uefi/asntest" 10 | ) 11 | 12 | func TestVerifyAuthenticode(t *testing.T) { 13 | cert, key := asntest.InitCert() 14 | 15 | img := []byte("test") 16 | b, err := SignAuthenticode(key, cert, bytes.NewReader(img), crypto.SHA256) 17 | if err != nil { 18 | t.Fatalf("message") 19 | } 20 | 21 | auth, err := ParseAuthenticode(b) 22 | if err != nil { 23 | t.Fatalf("%v", err) 24 | } 25 | ok, err := auth.Verify(cert, bytes.NewReader(img)) 26 | if err != nil { 27 | t.Fatalf("failed to verify authenticode checksum: %v", err) 28 | } 29 | 30 | if !ok { 31 | t.Fatalf("authenticode signature didn't validate, it should") 32 | } 33 | } 34 | 35 | func TestParseSbsign(t *testing.T) { 36 | b, err := os.ReadFile("testdata/test.authenticode.signed") 37 | if err != nil { 38 | t.Fatal(err) 39 | } 40 | 41 | _, err = ParseAuthenticode(b) 42 | if err != nil { 43 | t.Fatalf("failed to parse pkcs7: %v", err) 44 | } 45 | } 46 | 47 | // This test compares the library ASN.1 output to the old implementation 48 | // This is mostly for debugging the implementation. 49 | func TestCompareOldImplementation(t *testing.T) { 50 | if !testing.Verbose() { 51 | return 52 | } 53 | cert, key := asntest.InitCert() 54 | 55 | b, err := os.ReadFile("testdata/old_authenticode_implementation.der") 56 | if err != nil { 57 | t.Fatal(err) 58 | } 59 | 60 | img := []byte{0x00, 0x01} 61 | bb, err := SignAuthenticode(key, cert, bytes.NewReader(img), crypto.SHA256) 62 | if err != nil { 63 | t.Fatalf("failed signing digest") 64 | } 65 | 66 | // We should see a couple of differences, but largely the same structure should be present 67 | asntest.Asn1Compare(t, b, bb) 68 | } 69 | -------------------------------------------------------------------------------- /authenticode/checksum_test.go: -------------------------------------------------------------------------------- 1 | package authenticode 2 | 3 | import ( 4 | "bytes" 5 | "crypto" 6 | "crypto/x509" 7 | "encoding/hex" 8 | "encoding/pem" 9 | "errors" 10 | "fmt" 11 | "log" 12 | "os" 13 | "testing" 14 | 15 | "github.com/foxboron/go-uefi/asntest" 16 | "github.com/foxboron/go-uefi/efi/util" 17 | ) 18 | 19 | func mustHexdump(s string) []byte { 20 | b, err := hex.DecodeString(s) 21 | if err != nil { 22 | log.Fatalf("decodeHex: %s", err) 23 | } 24 | return b 25 | } 26 | 27 | func mustOpen(s string) []byte { 28 | b, err := os.ReadFile(s) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | return b 33 | } 34 | 35 | func mustCertificate(s string) *x509.Certificate { 36 | b := mustOpen(s) 37 | block, _ := pem.Decode(b) 38 | cert, err := x509.ParseCertificate(block.Bytes) 39 | if err != nil { 40 | log.Fatal(err) 41 | } 42 | return cert 43 | } 44 | 45 | func mustSigner(s string) crypto.Signer { 46 | b := mustOpen(s) 47 | k, err := util.ReadKey(b) 48 | if err != nil { 49 | log.Fatal(err) 50 | } 51 | return k 52 | } 53 | 54 | func TestSignVerify(t *testing.T) { 55 | cases := []struct { 56 | f string 57 | checksum []byte 58 | paddedchecksum []byte 59 | }{ 60 | { 61 | f: "../tests/data/binary/test.pecoff", 62 | checksum: mustHexdump("9f2b505ce20bc20c2ce7f7a33cb93ca97d1465735ce6821a6fc8e8c7b1e0e60a"), 63 | paddedchecksum: mustHexdump("e7d74d2bc1287c17bf056e259ad7d2ca557e848b252509ae9956df0b14f69702"), 64 | }, 65 | { 66 | f: "../tests/data/binary/HelloWorld.efi", 67 | checksum: mustHexdump("d2ae1f36ec9b40b55f60920a3f58ec902ebdc7c323e443412c751cdb3c42d3f3"), 68 | paddedchecksum: mustHexdump("765600a03f44d9f954376dd5f4e5b5e86b2ca1a3d6308a005f95922b0ebe7c94"), 69 | }, 70 | { 71 | f: "../tests/data/binary/HelloWorld.efi.signed", 72 | checksum: mustHexdump("765600a03f44d9f954376dd5f4e5b5e86b2ca1a3d6308a005f95922b0ebe7c94"), 73 | paddedchecksum: mustHexdump("765600a03f44d9f954376dd5f4e5b5e86b2ca1a3d6308a005f95922b0ebe7c94"), 74 | }, 75 | { 76 | f: "../tests/data/binary/linuxx64.efi.stub", 77 | checksum: mustHexdump("b9d3bcb414848ce11814ccb9fd98d95511e95fcc117c24e1517c9867761f251e"), 78 | paddedchecksum: mustHexdump("cc09c6b98fc5bf619ce09388399c35c21a510855f5fd308de653a8cf868e01cc"), 79 | }, 80 | } 81 | 82 | cert, key := asntest.InitCert() 83 | 84 | for n, c := range cases { 85 | unrecoverable := false 86 | 87 | t.Run(fmt.Sprintf("case %d", n), func(t *testing.T) { 88 | f, err := os.Open(c.f) 89 | if err != nil { 90 | unrecoverable = true 91 | t.Fatalf("failed reading file: %v", err) 92 | } 93 | defer f.Close() 94 | 95 | checksum, err := Parse(f) 96 | if err != nil { 97 | t.Fatalf("failed checksumming file: %v", err) 98 | } 99 | 100 | hashedbytes := checksum.Hash(crypto.SHA256) 101 | if !bytes.Equal(c.paddedchecksum, hashedbytes) { 102 | t.Fatalf("checksums didn't match. expected %x, got %x", c.checksum, hashedbytes) 103 | } 104 | 105 | _, err = checksum.Sign(key, cert) 106 | if err != nil { 107 | t.Fatalf("failed signing binary: %v", err) 108 | } 109 | 110 | ok, err := checksum.Verify(cert) 111 | if err != nil { 112 | t.Fatalf("failed verify binary: %v", err) 113 | } 114 | 115 | if !ok { 116 | t.Fatalf("failed verify binary, not ok") 117 | } 118 | }) 119 | 120 | if unrecoverable { 121 | t.FailNow() 122 | } 123 | } 124 | 125 | } 126 | 127 | func TestSbsignSignature(t *testing.T) { 128 | cases := []struct { 129 | f []byte 130 | checksum []byte 131 | cert *x509.Certificate 132 | err any 133 | ok bool 134 | size int 135 | fchecksum []byte 136 | }{ 137 | { 138 | f: mustOpen("testdata/test.pecoff"), 139 | checksum: mustHexdump("e7d74d2bc1287c17bf056e259ad7d2ca557e848b252509ae9956df0b14f69702"), 140 | cert: mustCertificate("testdata/db.pem"), 141 | err: ErrNoSignatures, 142 | ok: false, 143 | size: 3832, 144 | fchecksum: mustHexdump("ec693aefe32b2af94586fb43c78dbc3806658aca4dffd04019ac211cca3a6d5b"), 145 | }, 146 | { 147 | f: mustOpen("testdata/test.pecoff.signed"), 148 | checksum: mustHexdump("e7d74d2bc1287c17bf056e259ad7d2ca557e848b252509ae9956df0b14f69702"), 149 | err: nil, 150 | cert: mustCertificate("testdata/db.pem"), 151 | ok: true, 152 | size: 6064, 153 | fchecksum: mustHexdump("1f9eee26e7e041b286bc42e220c2398594a6a2b25863155a7d88fd4e98ef5192"), 154 | }, 155 | } 156 | 157 | for n, c := range cases { 158 | t.Run(fmt.Sprintf("case %d", n), func(t *testing.T) { 159 | checksum, err := Parse(bytes.NewReader(c.f)) 160 | if err != nil { 161 | t.Fatalf("failed checksumming file: %v", err) 162 | } 163 | 164 | hashedbytes := checksum.Hash(crypto.SHA256) 165 | if !bytes.Equal(c.checksum, hashedbytes) { 166 | t.Fatalf("checksums didn't match. expected %x, got %x", c.checksum, hashedbytes) 167 | } 168 | 169 | if len(checksum.Bytes()) != c.size { 170 | t.Fatalf("incorrect size. expected %v, got %v", c.size, len(checksum.Bytes())) 171 | } 172 | 173 | h := crypto.SHA256.New() 174 | h.Write(checksum.Bytes()) 175 | 176 | if !bytes.Equal(h.Sum(nil), c.fchecksum) { 177 | t.Fatalf("incorrect checksum.") 178 | } 179 | 180 | ok, err := checksum.Verify(c.cert) 181 | if !errors.As(err, &c.err) && (err != nil && c.err != nil) { 182 | t.Fatalf("failed verify function. expected %v, got %v", c.err, err) 183 | } 184 | 185 | if ok != c.ok { 186 | t.Fatalf("failed verify binary. expected %v, got %v", c.ok, ok) 187 | } 188 | }) 189 | } 190 | } 191 | 192 | func TestSignVerifyWrite(t *testing.T) { 193 | cases := []struct { 194 | f []byte 195 | checksum []byte 196 | cert *x509.Certificate 197 | key crypto.Signer 198 | err any 199 | ok bool 200 | size int 201 | fchecksum []byte 202 | }{ 203 | { 204 | f: mustOpen("testdata/test.pecoff"), 205 | checksum: mustHexdump("e7d74d2bc1287c17bf056e259ad7d2ca557e848b252509ae9956df0b14f69702"), 206 | cert: mustCertificate("testdata/db.pem"), 207 | key: mustSigner("testdata/db.key"), 208 | err: ErrNoSignatures, 209 | ok: true, 210 | size: 3825, 211 | fchecksum: mustHexdump("c821d06f18e84ecd33d9a53954d366d76b5c595f5819a5076009106601fc8c31"), 212 | }, 213 | } 214 | 215 | for n, c := range cases { 216 | t.Run(fmt.Sprintf("case %d", n), func(t *testing.T) { 217 | checksum, err := Parse(bytes.NewReader(c.f)) 218 | if err != nil { 219 | t.Fatalf("failed checksumming file: %v", err) 220 | } 221 | 222 | _, err = checksum.Sign(c.key, c.cert) 223 | if err != nil { 224 | t.Fatalf("failed signing binary: %v", err) 225 | } 226 | 227 | ok, err := checksum.Verify(c.cert) 228 | if !errors.As(err, &c.err) && (err != nil && c.err != nil) { 229 | t.Fatalf("failed verify function. expected %v, got %v", c.err, err) 230 | } 231 | 232 | if ok != c.ok { 233 | t.Fatalf("failed verify binary. expected %v, got %v", c.ok, ok) 234 | } 235 | 236 | // Debugging 237 | // os.WriteFile(path.Join(dir, "test.signed"), checksum.Bytes(), 0644) 238 | // bb := mustOpen(path.Join(dir, "test.signed")) 239 | 240 | binary, err := Parse(bytes.NewReader(checksum.Bytes())) 241 | if err != nil { 242 | t.Fatalf("failed parsing binary the second time: %v", err) 243 | } 244 | 245 | ok, err = binary.Verify(c.cert) 246 | 247 | if !errors.As(err, &c.err) && (err != nil && c.err != nil) { 248 | t.Fatalf("failed second verify function. expected %v, got %v", c.err, err) 249 | } 250 | 251 | if ok != c.ok { 252 | t.Fatalf("failed second verify binary. expected %v, got %v", c.ok, ok) 253 | } 254 | }) 255 | } 256 | } 257 | 258 | func TestWriteAndRead(t *testing.T) { 259 | f := mustOpen("testdata/test.pecoff.signed") 260 | 261 | cert := mustCertificate("testdata/db.pem") 262 | 263 | binary, _ := Parse(bytes.NewReader(f)) 264 | 265 | data := binary.Bytes() 266 | 267 | // os.WriteFile(path.Join(d, "reconstructed.pecoff"), data, 0o644) 268 | 269 | coff, err := Parse(bytes.NewReader(data)) 270 | if err != nil { 271 | t.Fatalf("failed parsing out written binary: %v", err) 272 | } 273 | 274 | h := crypto.SHA256.New() 275 | h.Write(coff.Bytes()) 276 | 277 | ok, err := coff.Verify(cert) 278 | if err != nil { 279 | t.Fatalf("failed verify: %v", err) 280 | } 281 | 282 | if !ok { 283 | t.Fatalf("should be true") 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /authenticode/multireader.go: -------------------------------------------------------------------------------- 1 | package authenticode 2 | 3 | import ( 4 | "io" 5 | "sort" 6 | ) 7 | 8 | // A SizeReaderAt is a ReaderAt with a Size method. 9 | // 10 | // An io.SectionReader implements SizeReaderAt. 11 | type SizeReaderAt interface { 12 | Size() int64 13 | io.ReaderAt 14 | } 15 | 16 | // newMultiReaderAt is like io.MultiReader but produces a ReaderAt 17 | // (and Size), instead of just a reader. 18 | func newMultiReaderAt(parts ...SizeReaderAt) SizeReaderAt { 19 | m := &multi{ 20 | parts: make([]offsetAndSource, 0, len(parts)), 21 | } 22 | var off int64 23 | for _, p := range parts { 24 | m.parts = append(m.parts, offsetAndSource{off, p}) 25 | off += p.Size() 26 | } 27 | m.size = off 28 | return m 29 | } 30 | 31 | type offsetAndSource struct { 32 | off int64 33 | SizeReaderAt 34 | } 35 | 36 | type multi struct { 37 | parts []offsetAndSource 38 | size int64 39 | } 40 | 41 | func (m *multi) Size() int64 { return m.size } 42 | 43 | func (m *multi) ReadAt(p []byte, off int64) (n int, err error) { 44 | wantN := len(p) 45 | 46 | // Skip past the requested offset. 47 | skipParts := sort.Search(len(m.parts), func(i int) bool { 48 | // This function returns whether parts[i] will 49 | // contribute any bytes to our output. 50 | part := m.parts[i] 51 | return part.off+part.Size() > off 52 | }) 53 | parts := m.parts[skipParts:] 54 | 55 | // How far to skip in the first part. 56 | needSkip := off 57 | if len(parts) > 0 { 58 | needSkip -= parts[0].off 59 | } 60 | 61 | for len(parts) > 0 && len(p) > 0 { 62 | readP := p 63 | partSize := parts[0].Size() 64 | if int64(len(readP)) > partSize-needSkip { 65 | readP = readP[:partSize-needSkip] 66 | } 67 | pn, err0 := parts[0].ReadAt(readP, needSkip) 68 | if err0 != nil { 69 | return n, err0 70 | } 71 | n += pn 72 | p = p[pn:] 73 | if int64(pn)+needSkip == partSize { 74 | parts = parts[1:] 75 | } 76 | needSkip = 0 77 | } 78 | 79 | if n != wantN { 80 | err = io.ErrUnexpectedEOF 81 | } 82 | return 83 | } 84 | -------------------------------------------------------------------------------- /authenticode/testdata/db.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | 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 51 | WrEB05arCxJhXH1qhZRaLWSckbwV1Q== 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /authenticode/testdata/db.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEvjCCAqagAwIBAgIQaVDP9r5CsuviM5FGEGcjkzANBgkqhkiG9w0BAQsFADAX 3 | MRUwEwYDVQQGEwxEYXRhYmFzZSBLZXkwIhgPMDAwMTAxMDEwMDAwMDBaGA8wMDAx 4 | MDEwMTAwMDAwMFowFzEVMBMGA1UEBhMMRGF0YWJhc2UgS2V5MIICIjANBgkqhkiG 5 | 9w0BAQEFAAOCAg8AMIICCgKCAgEAyv426XXrV2wXpbKqA4uX4Y0Howv41D1jpUOS 6 | x0Ypr/+CGDapFyVhS7KE5ZytHP/wSth5mHNiJ+mORX6MfQZ+stlRCvFmUbU/4hFt 7 | GvTPwCcBRaOnzPjdxB0j1DR+fUVW6Fv3NgesC2lwFPjBkqmqWdCBtWHFDm65rqEg 8 | vH+dCEL8PfkpEe8DSuRYLAieAfL4cTPfGQ5tyE8S1p1+VbCKyCyAfpaC6bf1fJV9 9 | 5arlUCz0iBYOHtRdZ4JKuGbHbgi0lP5zuPxGgb576rCckTW7RRP+5yIuAmDkBxuB 10 | Jb+hwIyT5jumpxlDlrVJ5pnY2TmIdRhlosQ5xVOfk4Ye2bmf8vv0BPVfFhQgKC3b 11 | 3Ws5OgKZu0pTYgyGL6W2VD4G3GzDWzfXG9zC9SlB4WwTD0pkwSXCMUWHlCy26mNE 12 | qgU3NR314n3ERfQ0kAx6yS2CqOgAsnPubc5RnEacYpqBvhMxmoWpaxeH/c29ukOp 13 | Y6eJ/ON3Kj1yNDyW2KOf1lqk/+6bpHczPPrjAR3rZNcoJQPcdkv1nPanquWCSWo3 14 | J00Th7h7jnXlKK977GXYCWLtqFJUikDwgYlLJPTGI5/6yI/+BYAlqyXteAil+U0h 15 | 1g1UXm9cfJb82ebQ0aH2YA/XDpx/UzvjHo30YJmLxciAgH3Lsmsmkwy1XAGb1JoE 16 | V/YqZTMCAwEAAaMCMAAwDQYJKoZIhvcNAQELBQADggIBABU15VvBSVYxcZkmxFpI 17 | OzTZ08uSME1VP9T1t8S2XR79KJkMpdOTKBnUuNyq5vYISl6krdgrKRAAbjrx5icP 18 | ChKkWlbDm4NJQWDnTOw3ubSx9kcXLCL+K5aRfAKtP/nE1HgDssob4RkjWKCJkFie 19 | nftPBSvVA8LU06Q/O8XIAliQSNgkNLieKL4iRq7VDaEyymOb29wZUqbnQ4zU/+ny 20 | fDETbE97nd6KUMrHW4HbF6OHqyWL05t6cYeQAasFOZahKlKgv4HQw7Tc5AyF7wh/ 21 | HNv0rGTdTm5yhuZ6sSQKJiZ3P1T+cQ5DX6oRU2ijEogTyVCtDncBIKDJSO3kgOoH 22 | 4xVWxWGKhU7+EelvX/uQ8LEMkL/gY7rqWTsFTckK/LRAifOErlO0kYnrqwdwnnOH 23 | pVlvItyjJDAWeo7E2S6Q24hXKPAeeGQjLLG7aESoYCKgrMCsVka481hdAxydpbPj 24 | 2GRU6KBW/x4zkw2i+kQeEiynCLNh7XgDx6Lni6X0JexPAQLvv6vBevgRELunVrCy 25 | XBaubTFPhK2k/nXfp68kLKFTI0/hp6lhTbnLWIvkx+RiT1Fg4JvokE9wgEXN+565 26 | hyC05ktGAwCjAhqkM3yVyF5XIiaYkc/OZ2CTM1fwDYSMKKoTKHPpcfgp1LoUDleX 27 | 74DyzFwfEvkJCoZa7Y647Jd2 28 | -----END CERTIFICATE----- 29 | -------------------------------------------------------------------------------- /authenticode/testdata/old_authenticode_implementation.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/authenticode/testdata/old_authenticode_implementation.der -------------------------------------------------------------------------------- /authenticode/testdata/test.authenticode.signed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/authenticode/testdata/test.authenticode.signed -------------------------------------------------------------------------------- /authenticode/testdata/test.pecoff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/authenticode/testdata/test.pecoff -------------------------------------------------------------------------------- /authenticode/testdata/test.pecoff.pk7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/authenticode/testdata/test.pecoff.pk7 -------------------------------------------------------------------------------- /authenticode/testdata/test.pecoff.signed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/authenticode/testdata/test.pecoff.signed -------------------------------------------------------------------------------- /cmd/appendmicrosoft/certs/MicCorUEFCA2011_2011-06-27.crt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/cmd/appendmicrosoft/certs/MicCorUEFCA2011_2011-06-27.crt -------------------------------------------------------------------------------- /cmd/appendmicrosoft/certs/MicWinProPCA2011_2011-10-19.crt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/cmd/appendmicrosoft/certs/MicWinProPCA2011_2011-10-19.crt -------------------------------------------------------------------------------- /cmd/appendmicrosoft/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "embed" 5 | "fmt" 6 | "log" 7 | ) 8 | 9 | //go:embed certs/* 10 | var content embed.FS 11 | 12 | func main() { 13 | files, err := content.ReadDir("certs") 14 | if err != nil { 15 | log.Fatal(err) 16 | } 17 | for _, file := range files { 18 | fmt.Println(file.Name()) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /cmd/efianalyze/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/x509" 6 | "fmt" 7 | "log" 8 | "os" 9 | 10 | "github.com/foxboron/go-uefi/authenticode" 11 | "github.com/foxboron/go-uefi/efi/attributes" 12 | "github.com/foxboron/go-uefi/efi/signature" 13 | "github.com/foxboron/go-uefi/pkcs7" 14 | ) 15 | 16 | func FormatSignatureList(siglist []*signature.SignatureList) { 17 | fmt.Printf("\nNumber of Signatures in the Signature List: %d\n", len(siglist)) 18 | for _, sig := range siglist { 19 | fmt.Printf("\nSignature Type: %s\n", signature.ValidEFISignatureSchemes[sig.SignatureType]) 20 | fmt.Printf("Signature List List Size : %d\n", sig.ListSize) 21 | fmt.Printf("Signature List Header Size : %d\n", sig.HeaderSize) 22 | fmt.Printf("Signature List Size : %d\n", sig.Size) 23 | fmt.Printf("Signature List Signature Header: %x (usually empty)\n", sig.SignatureHeader) 24 | fmt.Printf("Signature List Number of Signatures: %d\n", len(sig.Signatures)) 25 | fmt.Printf("Signature List Signatures:\n") 26 | for _, sigEntry := range sig.Signatures { 27 | fmt.Printf(" Signature Owner: %s\n", sigEntry.Owner.Format()) 28 | switch sig.SignatureType { 29 | case signature.CERT_X509_GUID: 30 | cert, _ := x509.ParseCertificate(sigEntry.Data) 31 | fmt.Printf(" Issuer: %s\n", cert.Issuer.String()) 32 | fmt.Printf(" Serial Number: %d\n", cert.SerialNumber) 33 | case signature.CERT_SHA256_GUID: 34 | fmt.Printf(" Type: %s\n", "SHA256") 35 | fmt.Printf(" Checksum: %x\n", sigEntry.Data) 36 | default: 37 | fmt.Println("Not implemented!") 38 | fmt.Println(sig.SignatureType.Format()) 39 | } 40 | } 41 | } 42 | } 43 | 44 | func ParseKeyDb(filename string) { 45 | _, f, _ := attributes.ReadEfivarsFile(filename) 46 | siglist, err := signature.ReadSignatureDatabase(f) 47 | if err != nil { 48 | log.Fatal(err) 49 | } 50 | FormatSignatureList(siglist) 51 | } 52 | 53 | func ParseSignatureList(filename string) { 54 | b, _ := os.ReadFile(filename) 55 | f := bytes.NewReader(b) 56 | siglist, err := signature.ReadSignatureDatabase(f) 57 | if err != nil { 58 | log.Fatal(err) 59 | } 60 | FormatSignatureList(siglist) 61 | } 62 | 63 | func FormatEFIVariableAuth2(sig *signature.EFIVariableAuthentication2) { 64 | fmt.Println("EFI Authentication Variable") 65 | fmt.Printf(" EFI Signing Time: %s\n", sig.Time.Format()) 66 | fmt.Println(" WINCertificate Info") 67 | fmt.Println(" Header:") 68 | fmt.Printf(" Length %d\n", sig.AuthInfo.Header.Length) 69 | fmt.Printf(" Revision: 0x%x (should be 0x200) \n", sig.AuthInfo.Header.Revision) 70 | fmt.Printf(" CertType: %s\n", signature.WINCertTypeString[sig.AuthInfo.Header.CertType]) 71 | fmt.Printf(" Certificate Type: ") 72 | switch sig.AuthInfo.CertType { 73 | case signature.EFI_CERT_TYPE_RSA2048_SHA256_GUID: 74 | fmt.Println("RSA2048 SHA256") 75 | case signature.EFI_CERT_TYPE_PKCS7_GUID: 76 | fmt.Println("PKCS7") 77 | } 78 | // pkcs7.ParseSignature(sig.AuthInfo.CertData) 79 | // l, err := moz.Parse(sig.AuthInfo.CertData) 80 | // if err != nil { 81 | // log.Fatal(err) 82 | // } 83 | 84 | // fmt.Printf("%+v", l) 85 | 86 | // err := ioutil.WriteFile("test.bin", sig.AuthInfo.CertData, 0644) 87 | // if err != nil { 88 | // log.Fatal(err) 89 | // } 90 | } 91 | 92 | func ParseEFIAuthVariable(filename string) { 93 | b, _ := os.ReadFile(filename) 94 | reader := bytes.NewReader(b) 95 | // Fetch the signature 96 | sig, err := signature.ReadEFIVariableAuthencation2(reader) 97 | if err != nil { 98 | log.Fatal(err) 99 | } 100 | FormatEFIVariableAuth2(sig) 101 | siglist, err := signature.ReadSignatureDatabase(reader) 102 | if err != nil { 103 | log.Fatal(err) 104 | } 105 | FormatSignatureList(siglist) 106 | } 107 | 108 | func ParseEFIImage(filename string) { 109 | b, err := os.ReadFile(filename) 110 | if err != nil { 111 | log.Fatal(err) 112 | os.Exit(1) 113 | } 114 | 115 | binary, err := authenticode.Parse(bytes.NewReader(b)) 116 | if err != nil { 117 | log.Fatal(err) 118 | } 119 | 120 | fmt.Println("Data Directory Header:") 121 | fmt.Printf(" Virtual Address: 0x%x\n", binary.Datadir.VirtualAddress) 122 | fmt.Printf(" Size in bytes: %d\n", binary.Datadir.Size) 123 | if binary.Datadir.Size == 0 { 124 | fmt.Println("No signatures") 125 | } 126 | 127 | signatures, err := binary.Signatures() 128 | if err != nil { 129 | log.Fatal(err) 130 | } 131 | for _, sig := range signatures { 132 | fmt.Printf("Certificate Type: %s\n", signature.WINCertTypeString[sig.CertType]) 133 | c, err := pkcs7.ParsePKCS7(sig.Certificate) 134 | if err != nil { 135 | log.Fatal(err) 136 | } 137 | for range c.SignerInfo { 138 | // TODO: Implement issuer and serial number parsing 139 | continue 140 | } 141 | } 142 | } 143 | 144 | func main() { 145 | if len(os.Args) == 1 { 146 | log.Fatalln("Need type") 147 | } 148 | if len(os.Args) == 2 { 149 | log.Fatalln("Need filename") 150 | } 151 | efiType := os.Args[1] 152 | file := os.Args[2] 153 | 154 | switch efiType { 155 | case "KEK", "PK", "db", "dbx": 156 | ParseKeyDb(file) 157 | case "siglist": 158 | ParseSignatureList(file) 159 | case "signed": 160 | ParseEFIAuthVariable(file) 161 | case "signed-image": 162 | ParseEFIImage(file) 163 | } 164 | 165 | } 166 | -------------------------------------------------------------------------------- /cmd/gosiglist/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "os" 9 | 10 | "github.com/foxboron/go-uefi/efi/signature" 11 | "github.com/foxboron/go-uefi/efi/util" 12 | ) 13 | 14 | func main() { 15 | owner := flag.String("o", "", "GUID of the owner") 16 | flag.Parse() 17 | args := flag.Args() 18 | if len(args) == 1 { 19 | fmt.Println("gosiglist: -o [input] [output]") 20 | } 21 | if len(args) != 2 { 22 | fmt.Println("Missing input and output file") 23 | os.Exit(1) 24 | } 25 | input := args[0] 26 | output := args[1] 27 | guid := util.StringToGUID(*owner) 28 | b, err := os.ReadFile(input) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | c := signature.NewSignatureList(signature.CERT_X509_GUID) 33 | c.AppendBytes(*guid, b) 34 | buf := new(bytes.Buffer) 35 | signature.WriteSignatureList(buf, *c) 36 | err = os.WriteFile(output, buf.Bytes(), 0644) 37 | if err != nil { 38 | log.Fatal(err) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /cmd/gosign/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "os" 8 | 9 | "github.com/foxboron/go-uefi/authenticode" 10 | "github.com/foxboron/go-uefi/efi/util" 11 | ) 12 | 13 | func main() { 14 | key := flag.String("key", "", "Key") 15 | cert := flag.String("cert", "", "Certificate") 16 | flag.Parse() 17 | args := flag.Args() 18 | if len(os.Args) == 1 { 19 | fmt.Println("gosign: -key -cert [input] [output]") 20 | } 21 | if len(os.Args) == 2 { 22 | fmt.Println("Missing input and output file") 23 | os.Exit(1) 24 | } 25 | 26 | peFile, err := os.Open(args[0]) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | Cert, err := util.ReadCertFromFile(*cert) 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | Key, err := util.ReadKeyFromFile(*key) 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | 40 | file, err := authenticode.Parse(peFile) 41 | if err != nil { 42 | log.Fatal(err) 43 | } 44 | file.Sign(Key, Cert) 45 | 46 | if err = os.WriteFile(args[1], file.Bytes(), 0644); err != nil { 47 | log.Fatal(err) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /cmd/govarsign/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "os" 9 | 10 | "github.com/foxboron/go-uefi/efi/signature" 11 | "github.com/foxboron/go-uefi/efi/util" 12 | "github.com/foxboron/go-uefi/efivar" 13 | ) 14 | 15 | func main() { 16 | key := flag.String("key", "", "Key") 17 | cert := flag.String("cert", "", "Certificate") 18 | variable := flag.String("var", "", "variable") 19 | flag.Parse() 20 | args := flag.Args() 21 | if len(os.Args) == 1 { 22 | fmt.Println("govarsign: -key -cert -var [input] [output]") 23 | } 24 | if len(os.Args) == 2 { 25 | fmt.Println("Missing input and output file") 26 | os.Exit(1) 27 | } 28 | b, err := os.ReadFile(args[0]) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | keyFile, err := util.ReadKeyFromFile(*key) 33 | if err != nil { 34 | log.Fatal(err) 35 | } 36 | certFile, err := util.ReadCertFromFile(*cert) 37 | if err != nil { 38 | log.Fatal(err) 39 | } 40 | 41 | var wvar efivar.Efivar 42 | switch *variable { 43 | case "db": 44 | wvar = efivar.Db 45 | case "KEK": 46 | wvar = efivar.KEK 47 | case "PK": 48 | wvar = efivar.PK 49 | } 50 | 51 | siglist, err := signature.ReadSignatureDatabase(bytes.NewReader(b)) 52 | if err != nil { 53 | log.Fatal(err) 54 | } 55 | 56 | _, sl, err := signature.SignEFIVariable(wvar, &siglist, keyFile, certFile) 57 | if err != nil { 58 | log.Fatal(err) 59 | } 60 | 61 | err = os.WriteFile(args[1], sl.Bytes(), 0644) 62 | if err != nil { 63 | log.Fatal(err) 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /cmd/goverify/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "os" 8 | 9 | "github.com/foxboron/go-uefi/authenticode" 10 | "github.com/foxboron/go-uefi/efi/util" 11 | ) 12 | 13 | func main() { 14 | cert := flag.String("cert", "", "Certificate") 15 | flag.Parse() 16 | args := flag.Args() 17 | if len(os.Args) == 1 { 18 | fmt.Println("goverify: -cert [input") 19 | } 20 | if len(os.Args) == 1 { 21 | fmt.Println("Missing input and output file") 22 | os.Exit(1) 23 | } 24 | 25 | peFile, err := os.Open(args[0]) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | x509Cert, err := util.ReadCertFromFile(*cert) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | 34 | binary, err := authenticode.Parse(peFile) 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | 39 | ok, err := binary.Verify(x509Cert) 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | 44 | if !ok { 45 | fmt.Println("Invalid signature") 46 | os.Exit(1) 47 | } 48 | fmt.Println("Valid signature") 49 | } 50 | -------------------------------------------------------------------------------- /cmd/gowritevar/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | 8 | "github.com/foxboron/go-uefi/efi" 9 | ) 10 | 11 | func main() { 12 | if len(os.Args) < 3 { 13 | fmt.Println("gowritevar [var] [efi variable]") 14 | os.Exit(1) 15 | } 16 | b, err := os.ReadFile(os.Args[2]) 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | if err := efi.WriteEFIVariable(os.Args[1], b); err != nil { 21 | log.Fatal(err) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /efi/attr/attr.go: -------------------------------------------------------------------------------- 1 | package attr 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | ) 7 | 8 | /* The code below won't work correctly if this tool is built 9 | * for a 64-bit big endian platform. 10 | * See https://github.com/golang/go/issues/45585 for context. */ 11 | 12 | const ( 13 | // from /usr/include/linux/fs.h 14 | FS_SECRM_FL = 0x00000001 /* Secure deletion */ 15 | FS_UNRM_FL = 0x00000002 /* Undelete */ 16 | FS_COMPR_FL = 0x00000004 /* Compress file */ 17 | FS_SYNC_FL = 0x00000008 /* Synchronous updates */ 18 | FS_IMMUTABLE_FL = 0x00000010 /* Immutable file */ 19 | FS_APPEND_FL = 0x00000020 /* writes to file may only append */ 20 | FS_NODUMP_FL = 0x00000040 /* do not dump file */ 21 | FS_NOATIME_FL = 0x00000080 /* do not update atime */ 22 | FS_DIRTY_FL = 0x00000100 23 | FS_COMPRBLK_FL = 0x00000200 /* One or more compressed clusters */ 24 | FS_NOCOMP_FL = 0x00000400 /* Don't compress */ 25 | FS_ECOMPR_FL = 0x00000800 /* Compression error */ 26 | FS_BTREE_FL = 0x00001000 /* btree format dir */ 27 | FS_INDEX_FL = 0x00001000 /* hash-indexed directory */ 28 | FS_IMAGIC_FL = 0x00002000 /* AFS directory */ 29 | FS_JOURNAL_DATA_FL = 0x00004000 /* Reserved for ext3 */ 30 | FS_NOTAIL_FL = 0x00008000 /* file tail should not be merged */ 31 | FS_DIRSYNC_FL = 0x00010000 /* dirsync behaviour (directories only) */ 32 | FS_TOPDIR_FL = 0x00020000 /* Top of directory hierarchies*/ 33 | FS_EXTENT_FL = 0x00080000 /* Extents */ 34 | FS_DIRECTIO_FL = 0x00100000 /* Use direct i/o */ 35 | FS_NOCOW_FL = 0x00800000 /* Do not cow file */ 36 | FS_PROJINHERIT_FL = 0x20000000 /* Create with parents projid */ 37 | FS_RESERVED_FL = 0x80000000 /* reserved for ext2 lib */ 38 | ) 39 | 40 | var ErrIsImmutable = errors.New("file is immutable") 41 | 42 | // checks if the file is immutable 43 | func IsImmutable(p string) error { 44 | a, err := GetAttr(p) 45 | if err != nil { 46 | return err 47 | } 48 | if (a & FS_IMMUTABLE_FL) != 0 { 49 | return ErrIsImmutable 50 | } 51 | return nil 52 | } 53 | 54 | func UnsetImmutable(p string) error { 55 | a, err := GetAttr(p) 56 | if err != nil { 57 | return err 58 | } 59 | a &= ^FS_IMMUTABLE_FL 60 | if err := SetAttr(p, a); err != nil { 61 | return err 62 | } 63 | return nil 64 | } 65 | 66 | // GetAttr retrieves the attributes of a file on a linux filesystem 67 | func GetAttr(path string) (int32, error) { 68 | f, err := os.OpenFile(path, os.O_RDONLY|os.O_CREATE, 0644) 69 | if err != nil { 70 | return 0, err 71 | } 72 | defer f.Close() 73 | return GetAttrFromFile(f) 74 | } 75 | 76 | // SetAttr sets the attributes of a file on a linux filesystem to the given value 77 | func SetAttr(path string, attr int32) error { 78 | f, err := os.OpenFile(path, os.O_RDONLY|os.O_CREATE, 0644) 79 | if err != nil { 80 | return err 81 | } 82 | defer f.Close() 83 | return SetAttrOnFile(f, attr) 84 | } 85 | -------------------------------------------------------------------------------- /efi/attr/attr_linux.go: -------------------------------------------------------------------------------- 1 | package attr 2 | 3 | import ( 4 | "os" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | // GetAttr retrieves the attributes of a file on a linux filesystem 10 | func GetAttrFromFile(f *os.File) (int32, error) { 11 | attr_int, err := unix.IoctlGetInt(int(f.Fd()), unix.FS_IOC_GETFLAGS) 12 | return int32(attr_int), err 13 | } 14 | 15 | // SetAttr sets the attributes of a file on a linux filesystem to the given value 16 | func SetAttrOnFile(f *os.File, attr int32) error { 17 | if err := unix.IoctlSetPointerInt(int(f.Fd()), unix.FS_IOC_SETFLAGS, int(attr)); err != nil { 18 | return err 19 | } 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /efi/attr/attr_other.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | package attr 3 | 4 | import ( 5 | "os" 6 | ) 7 | 8 | // Stub implementation for OSX 9 | func GetAttrFromFile(f *os.File) (int32, error) { 10 | return 0, nil 11 | } 12 | 13 | // Stub implementation for OSX 14 | func SetAttrOnFile(f *os.File, attr int32) error { 15 | return nil 16 | } 17 | -------------------------------------------------------------------------------- /efi/attributes/attributes.go: -------------------------------------------------------------------------------- 1 | package attributes 2 | 3 | // TODO: We should have an index of known vars and GUIDs 4 | 5 | import ( 6 | "bytes" 7 | "encoding/binary" 8 | "errors" 9 | "fmt" 10 | "io" 11 | "os" 12 | "path" 13 | 14 | "github.com/foxboron/go-uefi/efi/attr" 15 | "github.com/foxboron/go-uefi/efi/fs" 16 | "github.com/foxboron/go-uefi/efi/util" 17 | ) 18 | 19 | // Section 8.2 Variable Services 20 | type Attributes uint32 21 | 22 | var SizeofAttributes = 4 23 | 24 | func (a Attributes) Equal(b Attributes) bool { 25 | return (a & b) == a 26 | } 27 | 28 | func (a Attributes) Unmarshal(b *bytes.Buffer) { 29 | binary.Write(b, binary.LittleEndian, a) 30 | } 31 | 32 | func (a Attributes) Bytes() []byte { 33 | buf := new(bytes.Buffer) 34 | a.Unmarshal(buf) 35 | return buf.Bytes() 36 | } 37 | 38 | const ( 39 | EFI_VARIABLE_NON_VOLATILE Attributes = 0x00000001 40 | EFI_VARIABLE_BOOTSERVICE_ACCESS Attributes = 0x00000002 41 | EFI_VARIABLE_RUNTIME_ACCESS Attributes = 0x00000004 42 | EFI_VARIABLE_HARDWARE_ERROR_RECORD Attributes = 0x00000008 43 | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS Attributes = 0x00000010 // Deprecated, we only reserve it 44 | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS Attributes = 0x00000020 45 | EFI_VARIABLE_APPEND_WRITE Attributes = 0x00000040 46 | EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS Attributes = 0x00000080 // Uses the EFI_VARIABLE_AUTHENTICATION_3 struct 47 | ) 48 | 49 | // NV -> Non-Volatile 50 | // BS -> Boot Services 51 | // RT -> Runtime Services 52 | // AT -> Time Based Authenticated Write Access 53 | 54 | var EFI_GLOBAL_VARIABLE = util.EFIGUID{0x8BE4DF61, 0x93CA, 0x11d2, [8]uint8{0xAA, 0x0D, 0x00, 0xE0, 0x98, 0x03, 0x2B, 0x8C}} 55 | 56 | // Section 32.6 - Code Definition 57 | // Section 32.6.1 - UEFI Variable GUID & Variable Name 58 | // page 1728 59 | 60 | // Valid Databases 61 | // db - authorized signature database 62 | // dbx - forbidden signature database 63 | // dbt - authorized timestamp signature database 64 | // dbr - authorized recovery signature database 65 | var ( 66 | EFI_IMAGE_SECURITY_DATABASE_GUID = util.EFIGUID{0xd719b2cb, 0x3d3a, 0x4596, [8]uint8{0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f}} 67 | IMAGE_SECURITY_DATABASE = "db" 68 | IMAGE_SECURITY_DATABASE1 = "dbx" 69 | IMAGE_SECURITY_DATABASE2 = "dbt" 70 | IMAGE_SECURITY_DATABASE3 = "dbr" 71 | ImageSecurityDatabases = map[string]bool{ 72 | IMAGE_SECURITY_DATABASE: true, 73 | IMAGE_SECURITY_DATABASE1: true, 74 | IMAGE_SECURITY_DATABASE2: true, 75 | IMAGE_SECURITY_DATABASE3: true, 76 | } 77 | ) 78 | 79 | var ( 80 | Efivars = "/sys/firmware/efi/efivars" 81 | ) 82 | 83 | func ParseEfivars(f io.Reader, size int) (Attributes, *bytes.Buffer, error) { 84 | var attrs Attributes 85 | if err := binary.Read(f, binary.LittleEndian, &attrs); err != nil { 86 | return 0, nil, fmt.Errorf("could not read file: %w", err) 87 | } 88 | buf := make([]byte, size-SizeofAttributes) 89 | if err := binary.Read(f, binary.LittleEndian, &buf); err != nil { 90 | return 0, nil, err 91 | } 92 | return attrs, bytes.NewBuffer(buf), nil 93 | } 94 | 95 | // For a full path instead of the inferred efivars path 96 | func ReadEfivarsFile(filename string) (Attributes, *bytes.Buffer, error) { 97 | f, err := fs.Fs.Open(filename) 98 | if err != nil { 99 | return 0, nil, err 100 | } 101 | defer f.Close() 102 | stat, err := f.Stat() 103 | if err != nil { 104 | return 0, nil, fmt.Errorf("could not stat file descriptor: %w", err) 105 | } 106 | return ParseEfivars(f, int(stat.Size())) 107 | } 108 | 109 | func ReadEfivarsWithGuid(filename string, guid util.EFIGUID) (Attributes, *bytes.Buffer, error) { 110 | f := path.Join(Efivars, fmt.Sprintf("%s-%s", filename, guid.Format())) 111 | return ReadEfivarsFile(f) 112 | } 113 | 114 | // Reads a known EFI variable from efivarfs. 115 | func ReadEfivars(filename string) (Attributes, *bytes.Buffer, error) { 116 | guid := EFI_GLOBAL_VARIABLE 117 | if ok := ImageSecurityDatabases[filename]; ok { 118 | guid = EFI_IMAGE_SECURITY_DATABASE_GUID 119 | } 120 | return ReadEfivarsWithGuid(filename, guid) 121 | } 122 | 123 | func SerializeEfivars(f io.Writer, b []byte) error { 124 | return nil 125 | } 126 | 127 | func WriteEfivarsFile(filename, b []byte, fixImmutable bool) error { 128 | return nil 129 | } 130 | 131 | // Write an 132 | func WriteEfivars(name string, attrs Attributes, b []byte) error { 133 | guid := EFI_GLOBAL_VARIABLE 134 | if ok := ImageSecurityDatabases[name]; ok { 135 | guid = EFI_IMAGE_SECURITY_DATABASE_GUID 136 | } 137 | return WriteEfivarsWithGuid(name, attrs, b, guid) 138 | } 139 | 140 | // Write an EFI variable to sysfs 141 | // TODO: Fix retryable writes 142 | func WriteEfivarsWithGuid(name string, attrs Attributes, b []byte, guid util.EFIGUID) error { 143 | efivar := path.Join(Efivars, fmt.Sprintf("%s-%s", name, guid.Format())) 144 | err := attr.IsImmutable(efivar) 145 | switch { 146 | // Special case for test suites 147 | case fs.Fs.Name() == "MemMapFS": 148 | break 149 | case errors.Is(err, attr.ErrIsImmutable): 150 | if err := attr.UnsetImmutable(efivar); err != nil { 151 | return fmt.Errorf("couldn't unset immutable bit: %w", err) 152 | } 153 | case errors.Is(err, os.ErrNotExist): 154 | case err != nil: 155 | return err 156 | } 157 | 158 | flags := os.O_WRONLY | os.O_CREATE //| os.O_TRUNC 159 | if attrs&EFI_VARIABLE_APPEND_WRITE != 0 { 160 | flags |= os.O_APPEND 161 | } 162 | f, err := fs.Fs.OpenFile(efivar, flags, 0644) 163 | if err != nil { 164 | return fmt.Errorf("couldn't open file: %w", err) 165 | } 166 | defer f.Close() 167 | attrBuf := new(bytes.Buffer) 168 | binary.Write(attrBuf, binary.LittleEndian, attrs) 169 | buf := append(attrBuf.Bytes(), b...) 170 | if n, err := f.Write(buf); err != nil { 171 | return fmt.Errorf("couldn't write efi variable: %w", err) 172 | } else if n != len(buf) { 173 | return errors.New("could not write the entire buffer") 174 | } 175 | return nil 176 | } 177 | -------------------------------------------------------------------------------- /efi/device/acpi_device.go: -------------------------------------------------------------------------------- 1 | package device 2 | 3 | import ( 4 | "encoding/binary" 5 | "io" 6 | "log" 7 | ) 8 | 9 | // Subtypes of ACPI Device 10 | // Section 10.3.3 - ACPI Device Path 11 | const ( 12 | _ DevicePathSubType = iota 13 | ACPIDevice 14 | ExpandedACPIDevice 15 | ) 16 | 17 | type ACPIDevicePath struct { 18 | EFIDevicePath 19 | HID [4]byte 20 | UID [4]byte 21 | } 22 | 23 | func (a ACPIDevicePath) Format() string { 24 | return "No format" 25 | } 26 | 27 | func ParseACPIDevicePath(f io.Reader, efi *EFIDevicePath) EFIDevicePaths { 28 | switch efi.SubType { 29 | case ACPIDevice: 30 | a := ACPIDevicePath{EFIDevicePath: *efi} 31 | for _, i := range []interface{}{&a.HID, &a.UID} { 32 | if err := binary.Read(f, binary.LittleEndian, i); err != nil { 33 | log.Fatalf("Can't prase ACPI Device Path: %s", err) 34 | } 35 | } 36 | return a 37 | case ExpandedACPIDevice: 38 | log.Fatalf("Not implemented ACPIDevicePath type: %x\n", efi.SubType) 39 | default: 40 | log.Printf("Not implemented ACPIDevicePath type: %x\n", efi.SubType) 41 | } 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /efi/device/device.go: -------------------------------------------------------------------------------- 1 | package device 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | "io" 8 | "log" 9 | 10 | "github.com/foxboron/go-uefi/efi/attributes" 11 | "github.com/foxboron/go-uefi/efi/util" 12 | ) 13 | 14 | // Section 3.1.3 Load Options 15 | // 16 | // Page 71 17 | type EFILoadOption struct { 18 | Attributes attributes.Attributes 19 | FilePathListLength uint16 20 | Description string 21 | FilePath []EFIDevicePaths 22 | OptionalData []byte // TODO: Implement 23 | } 24 | 25 | func (e *EFILoadOption) Unmarshal(b *bytes.Buffer) error { 26 | elo, err := ParseEFILoadOption(b) 27 | if err != nil { 28 | return err 29 | } 30 | elo.FilePath, err = ParseDevicePath(b) 31 | if err != nil { 32 | return fmt.Errorf("could parse device path: %w", err) 33 | } 34 | *e = *elo 35 | return nil 36 | } 37 | 38 | // Section 10.3 Device Path Nodes 39 | // Page 286 40 | type DevicePathType uint8 41 | 42 | const ( 43 | _ DevicePathType = iota 44 | Hardware 45 | ACPI 46 | MessagingDevicePath 47 | MediaDevicePath 48 | BIOSBootSpecificationDevicePath 49 | EndOfHardwareDevicePath DevicePathType = 127 50 | ) 51 | 52 | // Section 10.3.1 Generic Device Path Structures 53 | // Page 287 54 | type DevicePathSubType uint8 55 | 56 | // Table 45. Device Path End Structure 57 | // Subtypes of EndofHardwareDevicePath 58 | const ( 59 | NewDevicePath DevicePathSubType = 1 60 | NoNewDevicePath DevicePathSubType = 255 61 | ) 62 | 63 | // Section 10.2 EFI Device Path Protocol 64 | // Page 285 65 | type EFIDevicePath struct { 66 | Type DevicePathType 67 | SubType DevicePathSubType 68 | Length [2]uint8 69 | } 70 | 71 | type EFIDevicePaths interface { 72 | Format() string 73 | } 74 | 75 | func (e EFIDevicePath) Format() string { 76 | return "No format" 77 | } 78 | 79 | func ParseDevicePath(f io.Reader) ([]EFIDevicePaths, error) { 80 | var ret []EFIDevicePaths 81 | for { 82 | var efidevice EFIDevicePath 83 | if err := binary.Read(f, binary.LittleEndian, &efidevice); err != nil { 84 | log.Fatalf("Failed to parse EFI Device Path: %s", err) 85 | } 86 | switch efidevice.Type { 87 | case Hardware: 88 | d := ParseHardwareDevicePath(f, &efidevice) 89 | ret = append(ret, d) 90 | case ACPI: 91 | d := ParseACPIDevicePath(f, &efidevice) 92 | ret = append(ret, d) 93 | case MediaDevicePath: 94 | d, err := ParseMediaDevicePath(f, &efidevice) 95 | if err != nil { 96 | return nil, err 97 | } 98 | ret = append(ret, d) 99 | case MessagingDevicePath: 100 | d := ParseMessagingDevicePath(f, &efidevice) 101 | ret = append(ret, d) 102 | case EndOfHardwareDevicePath: 103 | // log.Printf("Reached end of HardwareDevicePath: %+v\n", efidevice) 104 | goto end 105 | default: 106 | // log.Printf("Not implemented EFIDevicePath type: %+v\n", efidevice) 107 | goto end 108 | } 109 | } 110 | end: 111 | return ret, nil 112 | } 113 | 114 | func ParseEFILoadOption(f *bytes.Buffer) (*EFILoadOption, error) { 115 | var bootentry EFILoadOption 116 | for _, b := range []interface{}{&bootentry.Attributes, &bootentry.FilePathListLength} { 117 | if err := binary.Read(f, binary.LittleEndian, b); err != nil { 118 | return nil, fmt.Errorf("can't parse EFI Load Option: %w", err) 119 | } 120 | } 121 | 122 | b := util.ReadNullString(f) 123 | s, err := util.ParseUtf16Var(bytes.NewBuffer(b)) 124 | if err != nil { 125 | return nil, err 126 | } 127 | bootentry.Description = s 128 | return &bootentry, nil 129 | } 130 | -------------------------------------------------------------------------------- /efi/device/device_test.go: -------------------------------------------------------------------------------- 1 | package device 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | 9 | "github.com/foxboron/go-uefi/efi/attributes" 10 | ) 11 | 12 | func TestAbs(t *testing.T) { 13 | dir := "../../tests/data/boot" 14 | attributes.Efivars = dir 15 | files, err := os.ReadDir(dir) 16 | if err != nil { 17 | log.Fatal(err) 18 | } 19 | for _, file := range files { 20 | filepath := filepath.Join(dir, file.Name()) 21 | _, f, err := attributes.ReadEfivarsFile(filepath) 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | ParseEFILoadOption(f) 26 | ParseDevicePath(f) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /efi/device/hardware_device.go: -------------------------------------------------------------------------------- 1 | package device 2 | 3 | import ( 4 | "encoding/binary" 5 | "io" 6 | "log" 7 | ) 8 | 9 | // Subtypes of ACPI Device 10 | // Section 10.3.1 - Hardware Device Path 11 | const ( 12 | _ DevicePathSubType = iota 13 | HardwarePCI 14 | HardwarePCCARD 15 | HardwareMemoryMapped 16 | HardwareVendor 17 | HardwareController 18 | HardwareBMC 19 | ) 20 | 21 | type PCIDevicePath struct { 22 | EFIDevicePath 23 | Function [1]byte 24 | Device [1]byte 25 | } 26 | 27 | func (p PCIDevicePath) Format() string { 28 | return "No format" 29 | } 30 | 31 | func ParseHardwareDevicePath(f io.Reader, efi *EFIDevicePath) EFIDevicePaths { 32 | switch efi.SubType { 33 | case HardwarePCI: 34 | p := PCIDevicePath{EFIDevicePath: *efi} 35 | for _, d := range []interface{}{&p.Function, &p.Device} { 36 | if err := binary.Read(f, binary.LittleEndian, d); err != nil { 37 | log.Fatalf("Couldn't Parse PCI Device Path: %s", err) 38 | } 39 | } 40 | return p 41 | default: 42 | log.Printf("Not implemented HardwareDevicePath type: %x\n", efi.SubType) 43 | } 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /efi/device/media_device.go: -------------------------------------------------------------------------------- 1 | package device 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | "io" 8 | "log" 9 | 10 | "github.com/foxboron/go-uefi/efi/util" 11 | ) 12 | 13 | // Subtypes of Media Device 14 | // Section 10.3.5 - Media Device Path 15 | const ( 16 | _ DevicePathSubType = iota 17 | HardDriveDevicePath 18 | CDRomDevicePath 19 | VendorMediaDevicePath 20 | FilePathDevicePath 21 | MediaProtocolDevicePath 22 | PIWGFirmwareDevicePath 23 | ) 24 | 25 | type HardDriveMediaDevicePath struct { 26 | EFIDevicePath 27 | PartitionNumber uint32 28 | PartitionStart [8]byte 29 | PartitionSize [8]byte 30 | PartitionSignature [16]byte 31 | PartitionFormat uint8 32 | SignatureType uint8 33 | } 34 | 35 | func (h HardDriveMediaDevicePath) Format() string { 36 | format := []string{"MBR", "GPT"} 37 | if h.PartitionNumber == 0 { 38 | return fmt.Sprintf("HD(%d,%s,%x)", 39 | h.PartitionNumber, 40 | format[h.PartitionFormat-1], 41 | h.PartitionSignature) 42 | } 43 | return fmt.Sprintf("HD(%d,%s,%s,0x%x,0x%x)", 44 | h.PartitionNumber, 45 | format[h.PartitionFormat-1], 46 | util.BytesToGUID(h.PartitionSignature[:]).Format(), 47 | binary.LittleEndian.Uint64(h.PartitionStart[:]), 48 | binary.LittleEndian.Uint64(h.PartitionSize[:])) 49 | } 50 | 51 | type FileTypeMediaDevicePath struct { 52 | EFIDevicePath 53 | PathName string 54 | } 55 | 56 | func (f FileTypeMediaDevicePath) Format() string { 57 | return fmt.Sprintf("File(%s)", f.PathName) 58 | } 59 | 60 | type FirmwareFielMediaDevicePath struct { 61 | EFIDevicePath 62 | FirmwareFileName [16]byte 63 | } 64 | 65 | func (f FirmwareFielMediaDevicePath) Format() string { 66 | return "No format" 67 | } 68 | 69 | func ParseMediaDevicePath(f io.Reader, efi *EFIDevicePath) (EFIDevicePaths, error) { 70 | var err error 71 | switch efi.SubType { 72 | case HardDriveDevicePath: 73 | m := HardDriveMediaDevicePath{EFIDevicePath: *efi} 74 | for _, b := range []interface{}{ 75 | &m.PartitionNumber, 76 | &m.PartitionStart, 77 | &m.PartitionSize, 78 | &m.PartitionSignature, 79 | &m.PartitionFormat, 80 | &m.SignatureType, 81 | } { 82 | if err := binary.Read(f, binary.LittleEndian, b); err != nil { 83 | return nil, fmt.Errorf("Couldn't parse Harddrive Device Path: %w", err) 84 | } 85 | } 86 | return m, nil 87 | case FilePathDevicePath: 88 | file := FileTypeMediaDevicePath{EFIDevicePath: *efi} 89 | b := util.ReadNullString(f) 90 | file.PathName, err = util.ParseUtf16Var(bytes.NewBuffer(b)) 91 | if err != nil { 92 | return nil, err 93 | } 94 | return file, nil 95 | case PIWGFirmwareDevicePath: 96 | file := FirmwareFielMediaDevicePath{EFIDevicePath: *efi} 97 | if err := binary.Read(f, binary.LittleEndian, &file.FirmwareFileName); err != nil { 98 | return nil, fmt.Errorf("Couldn't parse PIWG Firmware Device Path: %w", err) 99 | } 100 | return file, nil 101 | default: 102 | log.Printf("Not implemented MediaDevicePath type: %x", efi.SubType) 103 | } 104 | return nil, nil 105 | } 106 | -------------------------------------------------------------------------------- /efi/device/messaging_device.go: -------------------------------------------------------------------------------- 1 | package device 2 | 3 | import ( 4 | "encoding/binary" 5 | "io" 6 | "log" 7 | 8 | "github.com/foxboron/go-uefi/efi/util" 9 | ) 10 | 11 | // Subtypes of Messaging Device Path 12 | // Section 10.3.4 13 | const ( 14 | _ DevicePathSubType = iota 15 | _ 16 | _ 17 | _ 18 | _ 19 | MessagingUSB 20 | _ 21 | _ 22 | _ 23 | _ 24 | MessagingVendor 25 | ) 26 | 27 | type USBMessagingDevicePath struct { 28 | EFIDevicePath 29 | USBParentPortNumber uint8 30 | Interface uint8 31 | } 32 | 33 | func (u USBMessagingDevicePath) Format() string { 34 | return "No format" 35 | } 36 | 37 | type VendorMessagingDevicePath struct { 38 | EFIDevicePath 39 | Guid util.EFIGUID 40 | } 41 | 42 | func (v VendorMessagingDevicePath) Format() string { 43 | return "No format" 44 | } 45 | 46 | func ParseMessagingDevicePath(f io.Reader, efi *EFIDevicePath) EFIDevicePaths { 47 | switch efi.SubType { 48 | case MessagingUSB: 49 | u := USBMessagingDevicePath{EFIDevicePath: *efi} 50 | for _, d := range []interface{}{&u.USBParentPortNumber, &u.Interface} { 51 | if err := binary.Read(f, binary.LittleEndian, d); err != nil { 52 | log.Fatalf("Couldn't parse USB Messaging Device Path: %s", err) 53 | } 54 | } 55 | return u 56 | case MessagingVendor: 57 | m := VendorMessagingDevicePath{EFIDevicePath: *efi} 58 | if err := binary.Read(f, binary.LittleEndian, &m.Guid); err != nil { 59 | log.Fatalf("Could not parse Vendor Messaging Device Path: %s", err) 60 | } 61 | default: 62 | log.Printf("Not implemented MessagingDevicePath type: %x\n", efi.SubType) 63 | } 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /efi/efi.go: -------------------------------------------------------------------------------- 1 | package efi 2 | 3 | // Top level API for goefi 4 | 5 | import ( 6 | "encoding/binary" 7 | "fmt" 8 | "io" 9 | "os" 10 | 11 | "github.com/foxboron/go-uefi/efi/attributes" 12 | "github.com/foxboron/go-uefi/efi/device" 13 | "github.com/foxboron/go-uefi/efi/signature" 14 | "github.com/foxboron/go-uefi/efi/util" 15 | "github.com/pkg/errors" 16 | ) 17 | 18 | // Keeps track of expected attributes for each variable 19 | var ValidAttributes = map[string]attributes.Attributes{ 20 | "SetupMode": attributes.EFI_VARIABLE_NON_VOLATILE | 21 | attributes.EFI_VARIABLE_BOOTSERVICE_ACCESS, 22 | "PK": attributes.EFI_VARIABLE_NON_VOLATILE | 23 | attributes.EFI_VARIABLE_BOOTSERVICE_ACCESS | 24 | attributes.EFI_VARIABLE_RUNTIME_ACCESS | 25 | attributes.EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, 26 | "KEK": attributes.EFI_VARIABLE_NON_VOLATILE | 27 | attributes.EFI_VARIABLE_BOOTSERVICE_ACCESS | 28 | attributes.EFI_VARIABLE_RUNTIME_ACCESS | 29 | attributes.EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, 30 | "db": attributes.EFI_VARIABLE_NON_VOLATILE | 31 | attributes.EFI_VARIABLE_BOOTSERVICE_ACCESS | 32 | attributes.EFI_VARIABLE_RUNTIME_ACCESS | 33 | attributes.EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, 34 | "dbx": attributes.EFI_VARIABLE_NON_VOLATILE | 35 | attributes.EFI_VARIABLE_BOOTSERVICE_ACCESS | 36 | attributes.EFI_VARIABLE_RUNTIME_ACCESS | 37 | attributes.EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, 38 | } 39 | 40 | func GetBootOrder() []string { 41 | ret := []string{} 42 | _, data, _ := attributes.ReadEfivars("BootOrder") 43 | for i := 0; i < data.Len(); i += 2 { 44 | b := make([]byte, 2) 45 | data.Read(b) 46 | val := binary.BigEndian.Uint16([]byte{b[1], b[0]}) 47 | ret = append(ret, fmt.Sprintf("Boot%04x\n", val)) 48 | } 49 | return ret 50 | } 51 | 52 | func GetBootEntry(entry string) (*device.EFILoadOption, error) { 53 | _, f, _ := attributes.ReadEfivars(entry) 54 | loadOption, err := device.ParseEFILoadOption(f) 55 | if err != nil { 56 | return nil, err 57 | } 58 | loadOption.FilePath, err = device.ParseDevicePath(f) 59 | if err != nil { 60 | return nil, err 61 | } 62 | return loadOption, nil 63 | } 64 | 65 | // GetSetupMode returns if setup mode has been enabled on the machine. 66 | func GetSetupMode() bool { 67 | if _, data, err := attributes.ReadEfivars("SetupMode"); err == nil { 68 | b, _ := data.ReadByte() 69 | if b == 1 { 70 | return true 71 | } 72 | } 73 | return false 74 | } 75 | 76 | // GetSecureBoot returns if secure boot has been enabled on the machine. 77 | func GetSecureBoot() bool { 78 | if _, data, err := attributes.ReadEfivars("SecureBoot"); err == nil { 79 | b, _ := data.ReadByte() 80 | if b == 1 { 81 | return true 82 | } 83 | } 84 | return false 85 | } 86 | 87 | func GetPK() (*signature.SignatureDatabase, error) { 88 | efivar := "PK" 89 | attributes, data, err := attributes.ReadEfivars(efivar) 90 | if err != nil { 91 | if errors.Is(err, io.EOF) || os.IsNotExist(err) { 92 | return &signature.SignatureDatabase{}, nil 93 | } 94 | return &signature.SignatureDatabase{}, err 95 | } 96 | if (ValidAttributes[efivar] & attributes) != ValidAttributes[efivar] { 97 | return nil, fmt.Errorf("invalid bitmask") 98 | } 99 | siglist, err := signature.ReadSignatureDatabase(data) 100 | if err != nil { 101 | return nil, errors.Wrapf(err, "can't parse Platform Key") 102 | } 103 | return &siglist, nil 104 | } 105 | 106 | func GetKEK() (*signature.SignatureDatabase, error) { 107 | efivar := "KEK" 108 | attr, data, err := attributes.ReadEfivars(efivar) 109 | if err != nil { 110 | if errors.Is(err, io.EOF) || os.IsNotExist(err) { 111 | return &signature.SignatureDatabase{}, nil 112 | } 113 | return &signature.SignatureDatabase{}, err 114 | } 115 | if (ValidAttributes[efivar] & attr) != ValidAttributes[efivar] { 116 | return nil, fmt.Errorf("invalid bitmask") 117 | } 118 | siglist, err := signature.ReadSignatureDatabase(data) 119 | if err != nil { 120 | return nil, errors.Wrapf(err, "can't parse Key Exchange key") 121 | } 122 | return &siglist, nil 123 | } 124 | 125 | func Getdb() (*signature.SignatureDatabase, error) { 126 | efivar := "db" 127 | attr, data, err := attributes.ReadEfivars(efivar) 128 | if err != nil { 129 | if errors.Is(err, io.EOF) || os.IsNotExist(err) { 130 | return &signature.SignatureDatabase{}, nil 131 | } 132 | return &signature.SignatureDatabase{}, err 133 | } 134 | if (ValidAttributes[efivar] & attr) != ValidAttributes[efivar] { 135 | return nil, fmt.Errorf("invalid bitmask") 136 | } 137 | siglist, err := signature.ReadSignatureDatabase(data) 138 | if err != nil { 139 | return nil, errors.Wrapf(err, "can't parse database key") 140 | } 141 | return &siglist, nil 142 | } 143 | 144 | func Getdbx() (*signature.SignatureDatabase, error) { 145 | efivar := "dbx" 146 | attr, data, err := attributes.ReadEfivars(efivar) 147 | if err != nil { 148 | if errors.Is(err, io.EOF) || os.IsNotExist(err) { 149 | return &signature.SignatureDatabase{}, nil 150 | } 151 | return &signature.SignatureDatabase{}, err 152 | } 153 | if (ValidAttributes[efivar] & attr) != ValidAttributes[efivar] { 154 | return nil, fmt.Errorf("invalid bitmask") 155 | } 156 | siglist, err := signature.ReadSignatureDatabase(data) 157 | if err != nil { 158 | return nil, errors.Wrapf(err, "can't parse forbidden database key") 159 | } 160 | return &siglist, nil 161 | } 162 | 163 | func WriteEFIVariable(variable string, buf []byte) error { 164 | attrs := ValidAttributes[variable] 165 | if err := attributes.WriteEfivars(variable, attrs, buf); err != nil { 166 | return err 167 | } 168 | return nil 169 | } 170 | 171 | // Return the boot entry which is currently booted. 172 | func GetCurrentlyBootedEntry() (string, error) { 173 | _, data, err := attributes.ReadEfivarsWithGuid( 174 | "LoaderEntrySelected", 175 | *util.StringToGUID("4a67b082-0a4c-41cf-b6c7-440b29bb8c4f"), 176 | ) 177 | if err != nil { 178 | return "", err 179 | } 180 | 181 | name, err := util.ParseUtf16Var(data) 182 | if err != nil { 183 | return "", err 184 | } 185 | 186 | return name, nil 187 | } 188 | -------------------------------------------------------------------------------- /efi/efitest/efitest.go: -------------------------------------------------------------------------------- 1 | package efitest 2 | 3 | import ( 4 | "path/filepath" 5 | "testing/fstest" 6 | 7 | "github.com/foxboron/go-uefi/efi/fs" 8 | "github.com/spf13/afero" 9 | ) 10 | 11 | // Convert fstest.MapFS to afero.Fs 12 | func FromMapFS(files fstest.MapFS) afero.Fs { 13 | memfs := afero.NewMemMapFs() 14 | for name, file := range files { 15 | memfs.MkdirAll(filepath.Dir(name), 0644) 16 | f, err := memfs.Create(name) 17 | if err != nil { 18 | continue 19 | } 20 | f.Write(file.Data) 21 | f.Close() 22 | } 23 | return memfs 24 | } 25 | 26 | type FSState struct { 27 | fs fstest.MapFS 28 | } 29 | 30 | func (f *FSState) ToAfero() afero.Fs { 31 | return FromMapFS(f.fs) 32 | } 33 | 34 | func (f *FSState) SetFS() *FSState { 35 | fs.SetFS(f.ToAfero()) 36 | return f 37 | } 38 | 39 | func (f *FSState) With(files ...fstest.MapFS) *FSState { 40 | for _, mapfs := range files { 41 | for path, file := range mapfs { 42 | f.fs[path] = file 43 | } 44 | } 45 | return f 46 | } 47 | 48 | func NewFS() *FSState { 49 | return &FSState{fs: fstest.MapFS{}} 50 | } 51 | -------------------------------------------------------------------------------- /efi/efitest/files.go: -------------------------------------------------------------------------------- 1 | package efitest 2 | 3 | import "testing/fstest" 4 | 5 | func SecureBootOff() fstest.MapFS { 6 | return fstest.MapFS{ 7 | "/sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c": { 8 | Data: []byte{0x6, 0x0, 0x0, 0x0, 0x0}}, 9 | } 10 | } 11 | 12 | func SecureBootOn() fstest.MapFS { 13 | return fstest.MapFS{ 14 | "/sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c": { 15 | Data: []byte{0x6, 0x0, 0x0, 0x0, 0x1}}, 16 | } 17 | } 18 | 19 | func SetUpModeOn() fstest.MapFS { 20 | return fstest.MapFS{ 21 | "/sys/firmware/efi/efivars/SetupMode-8be4df61-93ca-11d2-aa0d-00e098032b8c": { 22 | Data: []byte{0x6, 0x0, 0x0, 0x0, 0x1}}, 23 | } 24 | } 25 | 26 | func SetUpModeOff() fstest.MapFS { 27 | return fstest.MapFS{ 28 | "/sys/firmware/efi/efivars/SetupMode-8be4df61-93ca-11d2-aa0d-00e098032b8c": { 29 | Data: []byte{0x6, 0x0, 0x0, 0x0, 0x1}}, 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /efi/fs/fs.go: -------------------------------------------------------------------------------- 1 | package fs 2 | 3 | import "github.com/spf13/afero" 4 | 5 | // Storage backend 6 | var ( 7 | Fs = afero.NewOsFs() 8 | ) 9 | 10 | func SetFS(f afero.Fs) { 11 | Fs = f 12 | } 13 | -------------------------------------------------------------------------------- /efi/signature/signature_database.go: -------------------------------------------------------------------------------- 1 | package signature 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "reflect" 7 | 8 | "github.com/foxboron/go-uefi/efi/util" 9 | "github.com/pkg/errors" 10 | ) 11 | 12 | // SignatureDatabase is a list of EFI signature lists 13 | type SignatureDatabase []*SignatureList 14 | 15 | var ErrNotFoundSigList = errors.New("signature list not found") 16 | 17 | func NewSignatureDatabase() *SignatureDatabase { 18 | return &SignatureDatabase{} 19 | } 20 | 21 | // Checks if all signatures in a list is present in the signature database 22 | func (sd *SignatureDatabase) SigDataExists(certtype util.EFIGUID, sigdata *SignatureData) bool { 23 | for _, sdsiglist := range *sd { 24 | if ok, _ := sdsiglist.Exists(sigdata); ok { 25 | return true 26 | } 27 | } 28 | return false 29 | } 30 | 31 | // Checks if all signatures in a list is present in the signature database 32 | func (sd *SignatureDatabase) Exists(certtype util.EFIGUID, siglist *SignatureList) bool { 33 | for _, sdsiglist := range *sd { 34 | if !sdsiglist.CmpHeader(siglist) { 35 | continue 36 | } 37 | return sdsiglist.ExistsInList(siglist) 38 | } 39 | return false 40 | } 41 | 42 | // Checks if the bytes in a list is present in the signature database 43 | func (sd *SignatureDatabase) BytesExists(certtype util.EFIGUID, owner util.EFIGUID, data []byte) bool { 44 | return sd.SigDataExists(certtype, &SignatureData{Owner: owner, Data: data}) 45 | } 46 | 47 | // Appends the raw signature values to the database 48 | func (sd *SignatureDatabase) Append(certtype util.EFIGUID, owner util.EFIGUID, data []byte) error { 49 | if _, ok := ValidEFISignatureSchemes[certtype]; !ok { 50 | return ErrNoSuchSignatureScheme 51 | } 52 | for _, l := range *sd { 53 | if !util.CmpEFIGUID(l.SignatureType, certtype) { 54 | continue 55 | } 56 | size := uint32(len(data)) + util.SizeofEFIGUID 57 | if size != l.Size { 58 | continue 59 | } 60 | if err := l.AppendSignature(SignatureData{Owner: owner, Data: data}); err != nil { 61 | return err 62 | } 63 | return nil 64 | } 65 | sl := NewSignatureList(certtype) 66 | if err := sl.AppendBytes(owner, data); err != nil { 67 | return err 68 | } 69 | *sd = append(*sd, sl) 70 | return nil 71 | } 72 | 73 | // Appends a signaure to the database. It will scan the database for the appropriate list to append 74 | // itself to. 75 | func (sd *SignatureDatabase) AppendSignature(certtype util.EFIGUID, sl *SignatureData) error { 76 | return sd.Append(certtype, sl.Owner, sl.Data) 77 | } 78 | 79 | // Appends a signature list to the database 80 | // TODO: Should merge towards a fitting list? 81 | func (sd *SignatureDatabase) AppendList(sl *SignatureList) { 82 | *sd = append(*sd, sl) 83 | } 84 | 85 | // Appends a signature database 86 | func (sd *SignatureDatabase) AppendDatabase(s *SignatureDatabase) { 87 | for _, list := range *s { 88 | sd.AppendList(list) 89 | } 90 | } 91 | 92 | // Remove the raw signature values to the database 93 | func (sd *SignatureDatabase) Remove(certtype util.EFIGUID, owner util.EFIGUID, data []byte) error { 94 | // We might want to differentiate between not finding a list for the type and 95 | // not finding appropriate sigdata in the different lists. 96 | nosigdata := false 97 | for _, l := range *sd { 98 | if !util.CmpEFIGUID(l.SignatureType, certtype) { 99 | continue 100 | } 101 | size := uint32(len(data)) + util.SizeofEFIGUID 102 | if size != l.Size { 103 | continue 104 | } 105 | if err := l.RemoveBytes(owner, data); errors.Is(err, ErrNotFoundSigData) { 106 | nosigdata = true 107 | continue 108 | } 109 | if len(l.Signatures) == 0 { 110 | return sd.RemoveList(l) 111 | } 112 | return nil 113 | } 114 | if nosigdata { 115 | return ErrNotFoundSigData 116 | } 117 | return ErrNotFoundSigList 118 | } 119 | 120 | // Removes a signaure to the database. It will scan available lists for something to remove 121 | func (sd *SignatureDatabase) RemoveSignature(certtype util.EFIGUID, sl *SignatureData) error { 122 | return sd.Remove(certtype, sl.Owner, sl.Data) 123 | } 124 | 125 | func (sd *SignatureDatabase) removeslice(index int) { 126 | if len(*sd) == 1 { 127 | *sd = SignatureDatabase{} 128 | return 129 | } 130 | *sd = append((*sd)[:index], (*sd)[index+1:]...) 131 | } 132 | 133 | // Removes a signature list from the database 134 | func (sd *SignatureDatabase) RemoveList(sl *SignatureList) error { 135 | for index, siglist := range *sd { 136 | if reflect.DeepEqual(sl, siglist) { 137 | sd.removeslice(index) 138 | return nil 139 | } 140 | } 141 | return ErrNotFoundSigList 142 | } 143 | 144 | // Serialize the Signature Database into bytes 145 | func (sd *SignatureDatabase) Bytes() []byte { 146 | buf := new(bytes.Buffer) 147 | WriteSignatureDatabase(buf, *sd) 148 | return buf.Bytes() 149 | } 150 | 151 | func (sd *SignatureDatabase) Marshal(b *bytes.Buffer) { 152 | WriteSignatureDatabase(b, *sd) 153 | } 154 | 155 | func (sd *SignatureDatabase) Unmarshal(b *bytes.Buffer) error { 156 | s, err := ReadSignatureDatabase(b) 157 | if err != nil { 158 | return err 159 | } 160 | *sd = s 161 | return nil 162 | } 163 | 164 | // Write a signature database which contains a slice of SignautureLists 165 | func WriteSignatureDatabase(b io.Writer, sigdb SignatureDatabase) { 166 | for _, l := range sigdb { 167 | WriteSignatureList(b, *l) 168 | } 169 | } 170 | 171 | // Reads several signature lists from a io.Reader. It assumes io.EOF means there 172 | // are no more signatures to read as opposed to an actual issue 173 | func ReadSignatureDatabase(f io.Reader) (SignatureDatabase, error) { 174 | siglist := []*SignatureList{} 175 | for { 176 | sig, err := ReadSignatureList(f) 177 | if errors.Is(err, io.EOF) { 178 | break 179 | } else if err != nil { 180 | return siglist, errors.Wrapf(err, "failed to parse signature lists") 181 | } 182 | siglist = append(siglist, sig) 183 | } 184 | return siglist, nil 185 | } 186 | -------------------------------------------------------------------------------- /efi/signature/signature_database_test.go: -------------------------------------------------------------------------------- 1 | package signature 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "testing" 7 | 8 | "github.com/pkg/errors" 9 | ) 10 | 11 | func TestSigdatabaseAppend(t *testing.T) { 12 | sigdb := SignatureDatabase{} 13 | for _, sig := range sigdata { 14 | sigdb.AppendSignature(CERT_SHA256_GUID, &sig) 15 | } 16 | if sigdb[0].ListSize != 172 { 17 | fmt.Println(sigdb[0].ListSize) 18 | t.Fatal("append: list size incorrect") 19 | } 20 | if sigdb[0].Size != 48 { 21 | fmt.Println(sigdb[0].ListSize) 22 | t.Fatal("append: size incorrect") 23 | } 24 | if len(sigdb[0].Signatures) != 3 { 25 | t.Fatal("append: number of signatures wrong") 26 | } 27 | } 28 | 29 | func TestSigdatabaseAppendSame(t *testing.T) { 30 | sigdb := SignatureDatabase{} 31 | for _, sig := range sigdata { 32 | sigdb.AppendSignature(CERT_SHA256_GUID, &sig) 33 | } 34 | sigdb.AppendSignature(CERT_SHA256_GUID, &sigdata[0]) 35 | if len(sigdb[0].Signatures) != 3 { 36 | t.Fatal("append: number of signatures wrong") 37 | } 38 | } 39 | 40 | func TestSigdatabaseRemove(t *testing.T) { 41 | sigdb := SignatureDatabase{} 42 | for _, sig := range sigdata { 43 | sigdb.AppendSignature(CERT_SHA256_GUID, &sig) 44 | } 45 | err := sigdb.RemoveSignature(CERT_SHA256_GUID, &sigdata[0]) 46 | if err != nil { 47 | t.Error(err) 48 | } 49 | if sigdb[0].ListSize != 124 { 50 | fmt.Println(sigdb[0].ListSize) 51 | t.Fatal("remove: list size incorrect") 52 | } 53 | if sigdb[0].Size != 48 { 54 | fmt.Println(sigdb[0].ListSize) 55 | t.Fatal("remove: size incorrect") 56 | } 57 | if len(sigdb[0].Signatures) != 2 { 58 | t.Fatal("remove: number of signatures wrong") 59 | } 60 | } 61 | 62 | func TestSigdatabaseRemoveSame(t *testing.T) { 63 | sigdb := SignatureDatabase{} 64 | for _, sig := range sigdata { 65 | sigdb.AppendSignature(CERT_SHA256_GUID, &sig) 66 | } 67 | sigdb.RemoveSignature(CERT_SHA256_GUID, &sigdata[0]) 68 | err := sigdb.RemoveSignature(CERT_SHA256_GUID, &sigdata[0]) 69 | if !errors.Is(err, ErrNotFoundSigData) { 70 | t.Fatalf("remove: wrong error, got: %s", err) 71 | } 72 | } 73 | 74 | func TestSigdatabaseRemoveWrongSiglistType(t *testing.T) { 75 | sigdb := SignatureDatabase{} 76 | for _, sig := range sigdata { 77 | sigdb.AppendSignature(CERT_SHA256_GUID, &sig) 78 | } 79 | err := sigdb.RemoveSignature(CERT_X509_GUID, &sigdata[0]) 80 | if !errors.Is(err, ErrNotFoundSigList) { 81 | t.Fatalf("remove: wrong error, got: %s", err) 82 | } 83 | } 84 | 85 | func TestSigdatabaseRemoveAll(t *testing.T) { 86 | var err error 87 | sigdb := SignatureDatabase{} 88 | for _, sig := range sigdata { 89 | sigdb.AppendSignature(CERT_SHA256_GUID, &sig) 90 | } 91 | for index, sig := range sigdata { 92 | err = sigdb.RemoveSignature(CERT_SHA256_GUID, &sig) 93 | if len(sigdb) != 0 && 3-(index+1) != len(sigdb[0].Signatures) { 94 | t.Fatalf("remove all: sigdb wrong size. expected %d, got %d", 3-(index+1), len(sigdb[0].Signatures)) 95 | } 96 | } 97 | if err != nil { 98 | t.Fatalf("remove all: got error: %s", err) 99 | } 100 | if !reflect.DeepEqual(sigdb, SignatureDatabase{}) { 101 | t.Fatalf("remove all: not empty interface") 102 | } 103 | } 104 | 105 | func TestSigdatabaseExists(t *testing.T) { 106 | sigdb := SignatureDatabase{} 107 | for _, sig := range sigdata { 108 | sigdb.AppendSignature(CERT_SHA256_GUID, &sig) 109 | } 110 | sl := NewSignatureList(CERT_SHA256_GUID) 111 | for _, sig := range sigdata { 112 | sl.AppendBytes(sig.Owner, sig.Data) 113 | } 114 | if !sigdb.Exists(CERT_SHA256_GUID, sl) { 115 | t.Fatalf("exists: siglist is not in database") 116 | } 117 | } 118 | 119 | func TestSigdatabaseSigdataExists(t *testing.T) { 120 | sigdb := SignatureDatabase{} 121 | for _, sig := range sigdata { 122 | sigdb.AppendSignature(CERT_SHA256_GUID, &sig) 123 | } 124 | if !sigdb.SigDataExists(CERT_SHA256_GUID, &sigdata[0]) { 125 | t.Fatalf("exists: siglist is not in database") 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /efi/signature/signature_list.go: -------------------------------------------------------------------------------- 1 | package signature 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "encoding/pem" 7 | "fmt" 8 | "io" 9 | "log" 10 | "reflect" 11 | 12 | "github.com/foxboron/go-uefi/efi/util" 13 | "github.com/pkg/errors" 14 | ) 15 | 16 | // Section 32.4.1 Signature Database 17 | // Page 1714 -> Page 1717 18 | var ( 19 | CERT_SHA256_GUID = util.EFIGUID{0xc1c41626, 0x504c, 0x4092, [8]uint8{0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28}} 20 | CERT_RSA2048_GUID = util.EFIGUID{0x3c5766e8, 0x269c, 0x4e34, [8]uint8{0xaa, 0x14, 0xed, 0x77, 0x6e, 0x85, 0xb3, 0xb6}} 21 | CERT_RSA2048_SHA256_GUID = util.EFIGUID{0xe2b36190, 0x879b, 0x4a3d, [8]uint8{0xad, 0x8d, 0xf2, 0xe7, 0xbb, 0xa3, 0x27, 0x84}} 22 | 23 | CERT_SHA1_GUID = util.EFIGUID{0x826ca512, 0xcf10, 0x4ac9, [8]uint8{0xb1, 0x87, 0xbe, 0x01, 0x49, 0x66, 0x31, 0xbd}} 24 | CERT_RSA2048_SHA1_GUID = util.EFIGUID{0x67f8444f, 0x8743, 0x48f1, [8]uint8{0xa3, 0x28, 0x1e, 0xaa, 0xb8, 0x73, 0x60, 0x80}} 25 | 26 | CERT_X509_GUID = util.EFIGUID{0xa5c059a1, 0x94e4, 0x4aa7, [8]uint8{0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72}} 27 | 28 | CERT_SHA224_GUID = util.EFIGUID{0xb6e5233, 0xa65c, 0x44c9, [8]uint8{0x94, 0x07, 0xd9, 0xab, 0x83, 0xbf, 0xc8, 0xbd}} 29 | 30 | CERT_SHA384_GUID = util.EFIGUID{0xff3e5307, 0x9fd0, 0x48c9, [8]uint8{0x85, 0xf1, 0x8a, 0xd5, 0x6c, 0x70, 0x1e, 0x01}} 31 | 32 | CERT_SHA512_GUID = util.EFIGUID{0x93e0fae, 0xa6c4, 0x4f50, [8]uint8{0x9f, 0x1b, 0xd4, 0x1e, 0x2b, 0x89, 0xc1, 0x9a}} 33 | 34 | CERT_X509_SHA256_GUID = util.EFIGUID{0x3bd2a492, 0x96c0, 0x4079, [8]uint8{0xb4, 0x20, 0xfc, 0xf9, 0x8e, 0xf1, 0x03, 0xed}} 35 | 36 | CERT_EXTERNAL_MANAGEMENT_GUID = util.EFIGUID{0x452e8ced, 0xdfff, 0x4b8c, [8]uint8{0xae, 0x01, 0x51, 0x18, 0x86, 0x2e, 0x68, 0x2c}} 37 | ) 38 | 39 | type CertType string 40 | 41 | var ErrNoSuchSignatureScheme = errors.New("no such signature scheme") 42 | 43 | // Quick access list 44 | // Maybe a map[string]EFIGUID? 45 | var ValidEFISignatureSchemes = map[util.EFIGUID]CertType{ 46 | CERT_SHA256_GUID: "SHA256", 47 | CERT_RSA2048_GUID: "RSA2048", 48 | CERT_RSA2048_SHA256_GUID: "RSA2048 SHA256", 49 | CERT_SHA1_GUID: "SHA1", 50 | CERT_RSA2048_SHA1_GUID: "RSA2048 SHA1", 51 | CERT_X509_GUID: "X509", 52 | CERT_SHA224_GUID: "SHA224", 53 | CERT_SHA384_GUID: "SHA238", 54 | CERT_SHA512_GUID: "SHA512", 55 | CERT_X509_SHA256_GUID: "X509 SHA256", 56 | CERT_EXTERNAL_MANAGEMENT_GUID: "EXTERNAL MANAGEMENT", 57 | } 58 | 59 | const ( 60 | CERT_SHA256 CertType = "SHA256" 61 | CERT_RSA2048 = "RSA2048" 62 | CERT_RSA2048_SHA256 = "RSA2048 SHA256" 63 | CERT_SHA1 = "SHA1" 64 | CERT_RSA2048_SHA1 = "RSA2048 SHA1" 65 | CERT_X509 = "X509" 66 | CERT_SHA224 = "SHA224" 67 | CERT_SHA384 = "SHA238" 68 | CERT_SHA512 = "SHA512" 69 | CERT_X509_SHA256 = "X509 SHA256" 70 | ) 71 | 72 | // Section 3.3 - Globally Defined Variables 73 | // Array of GUIDs representing the type of signatures supported by 74 | // the platform firmware. Should be treated as read-only 75 | func GetSupportedSignatures(f io.Reader) ([]util.EFIGUID, error) { 76 | // This is a bit bad. But io.Reader is *probably nicer* but we need to know 77 | // the length in a better way. 78 | buf := new(bytes.Buffer) 79 | buf.ReadFrom(f) 80 | supportedSigs := make([]util.EFIGUID, buf.Len()/16) 81 | if err := binary.Read(buf, binary.LittleEndian, &supportedSigs); err != nil { 82 | return nil, errors.Wrapf(err, "could not parse EFIGUIDs from this reader") 83 | } 84 | return supportedSigs, nil 85 | } 86 | 87 | // Section 32.4.1 - Signature Database 88 | // Page 1712 89 | type SignatureData struct { 90 | Owner util.EFIGUID 91 | Data []uint8 92 | } 93 | 94 | func ReadSignatureData(f io.Reader, size uint32) (*SignatureData, error) { 95 | s := SignatureData{} 96 | if err := binary.Read(f, binary.LittleEndian, &s.Owner); err != nil { 97 | return &SignatureData{}, errors.Wrapf(err, "could not read Signature Data") 98 | } 99 | data := make([]uint8, size-util.SizeofEFIGUID) // Subtract the size of Owner 100 | if err := binary.Read(f, binary.LittleEndian, &data); err != nil { 101 | return &SignatureData{}, errors.Wrapf(err, "Couldn't read Signature Data") 102 | } 103 | s.Data = data[:] 104 | return &s, nil 105 | } 106 | 107 | func WriteSignatureData(b io.Writer, s SignatureData) { 108 | for _, v := range []interface{}{s.Owner, s.Data} { 109 | err := binary.Write(b, binary.LittleEndian, v) 110 | if err != nil { 111 | log.Fatalf("Couldn't write signature data: %s", err) 112 | } 113 | } 114 | } 115 | 116 | func (sd *SignatureData) Bytes() []byte { 117 | buf := new(bytes.Buffer) 118 | WriteSignatureData(buf, *sd) 119 | return buf.Bytes() 120 | } 121 | 122 | // Section 32.4.1 - Signature Database 123 | // Page 1713 124 | type SignatureList struct { 125 | SignatureType util.EFIGUID 126 | ListSize uint32 // Total size of the signature list, including this header 127 | HeaderSize uint32 // Size of SignatureHead 128 | Size uint32 // Size of each signature. At least the size of EFI_SIGNATURE_DATA 129 | SignatureHeader []uint8 // SignatureType defines the content of this header 130 | Signatures []SignatureData // SignatureData List 131 | } 132 | 133 | // SignatureSize + sizeof(SignatureType) + sizeof(uint32)*3 134 | const SizeofSignatureList uint32 = util.SizeofEFIGUID + 4 + 4 + 4 135 | 136 | var ErrNotFoundSigData = errors.New("signature data not found") 137 | var ErrSigDataExists = errors.New("signature data exists already") 138 | 139 | func NewSignatureList(certtype util.EFIGUID) *SignatureList { 140 | return &SignatureList{ 141 | SignatureType: certtype, 142 | ListSize: SizeofSignatureList, 143 | HeaderSize: 0, 144 | Size: 0, 145 | SignatureHeader: []uint8{}, 146 | Signatures: []SignatureData{}, 147 | } 148 | } 149 | 150 | // Compare the signature lists header to see if they are the same type of list 151 | // This is usefull if you wonder if you can merge the lists or not 152 | func (sl *SignatureList) CmpHeader(siglist *SignatureList) bool { 153 | if !util.CmpEFIGUID(sl.SignatureType, siglist.SignatureType) { 154 | return false 155 | } 156 | if sl.Size != siglist.Size { 157 | return false 158 | } 159 | if !reflect.DeepEqual(sl.SignatureHeader, siglist.SignatureHeader) { 160 | return false 161 | } 162 | return true 163 | } 164 | 165 | // Check if signature exists in the signature list 166 | // Return true if it does along with the index 167 | func (sl *SignatureList) Exists(sigdata *SignatureData) (bool, int) { 168 | for index, sigs := range sl.Signatures { 169 | if !util.CmpEFIGUID(sigs.Owner, sigdata.Owner) { 170 | continue 171 | } 172 | if !bytes.Equal(sigs.Data, sigdata.Data) { 173 | continue 174 | } 175 | return true, index 176 | } 177 | return false, 0 178 | } 179 | 180 | func (sl *SignatureList) ExistsInList(siglist *SignatureList) bool { 181 | for _, item := range siglist.Signatures { 182 | if ok, _ := sl.Exists(&item); !ok { 183 | return false 184 | } 185 | } 186 | return true 187 | } 188 | 189 | func (sl *SignatureList) AppendBytes(owner util.EFIGUID, data []byte) error { 190 | if ok, _ := sl.Exists(&SignatureData{owner, data}); ok { 191 | return ErrSigDataExists 192 | } 193 | switch sl.SignatureType { 194 | case CERT_X509_GUID: 195 | // Check if the cert is PEM encoded 196 | // We need the DER encoded cert, but this makes it nicer 197 | // for us in the API 198 | if block, _ := pem.Decode(data); block != nil { 199 | data = block.Bytes 200 | } 201 | case CERT_SHA256_GUID: 202 | if len(data) != 32 { 203 | return errors.New("not a sha256 hash") 204 | } 205 | } 206 | sl.Signatures = append(sl.Signatures, SignatureData{Owner: owner, Data: data}) 207 | sl.Size = uint32(len(data)) + util.SizeofEFIGUID 208 | sl.ListSize += sl.Size 209 | return nil 210 | } 211 | 212 | func (sl *SignatureList) AppendSignature(s SignatureData) error { 213 | return sl.AppendBytes(s.Owner, s.Data) 214 | } 215 | 216 | func (sl *SignatureList) RemoveBytes(owner util.EFIGUID, data []byte) error { 217 | ok, index := sl.Exists(&SignatureData{owner, data}) 218 | if !ok { 219 | return ErrNotFoundSigData 220 | } 221 | if len(sl.Signatures) == 1 { 222 | *sl = *NewSignatureList(sl.SignatureType) 223 | return nil 224 | } 225 | sl.Signatures = append(sl.Signatures[:index], sl.Signatures[index+1:]...) 226 | sl.ListSize -= sl.Size 227 | return nil 228 | } 229 | 230 | func (sl *SignatureList) RemoveSignature(s SignatureData) error { 231 | return sl.RemoveBytes(s.Owner, s.Data) 232 | } 233 | 234 | func (sl *SignatureList) Bytes() []byte { 235 | buf := new(bytes.Buffer) 236 | WriteSignatureList(buf, *sl) 237 | return buf.Bytes() 238 | } 239 | 240 | // Writes a signature list 241 | func WriteSignatureList(b io.Writer, s SignatureList) { 242 | for _, v := range []interface{}{s.SignatureType, s.ListSize, s.HeaderSize, s.Size, s.SignatureHeader} { 243 | err := binary.Write(b, binary.LittleEndian, v) 244 | if err != nil { 245 | log.Fatalf("Couldn't write signature list: %s", err) 246 | } 247 | } 248 | for _, l := range s.Signatures { 249 | WriteSignatureData(b, l) 250 | } 251 | } 252 | 253 | // Read an EFI_SIGNATURE_LIST from io.Reader. It will read until io.EOF. 254 | // io.EOF should be somewhat expected if we are trying to read multiple 255 | // lists as they should be either at the end of the file, or the entire file. 256 | func ReadSignatureList(f io.Reader) (*SignatureList, error) { 257 | s := SignatureList{} 258 | for _, i := range []interface{}{&s.SignatureType, &s.ListSize, &s.HeaderSize, &s.Size} { 259 | err := binary.Read(f, binary.LittleEndian, i) 260 | if errors.Is(err, io.EOF) { 261 | return &SignatureList{}, err 262 | } else if err != nil { 263 | return &SignatureList{}, errors.Wrapf(err, "couldn't read signature list") 264 | } 265 | } 266 | 267 | var sigData []SignatureData 268 | var err error 269 | 270 | // The list size minus the size of the SignatureList struct 271 | // lets us figure out how much signature data we should read. 272 | totalSize := s.ListSize - SizeofSignatureList 273 | 274 | sig := ValidEFISignatureSchemes[s.SignatureType] 275 | // Anonymous function because I really can't figure out a better name for it 276 | parseList := func(data []SignatureData, size uint32) ([]SignatureData, error) { 277 | for { 278 | if totalSize == 0 { 279 | return data, nil 280 | } 281 | sigdata, err := ReadSignatureData(f, size) 282 | if err != nil { 283 | return nil, err 284 | } 285 | data = append(data, *sigdata) 286 | totalSize -= s.Size 287 | } 288 | } 289 | switch sig { 290 | case "X509": 291 | if s.HeaderSize != 0 { 292 | return nil, fmt.Errorf("unexpected HeaderSize for x509 cert. Should be 0") 293 | } 294 | sigData, err = parseList(sigData, s.Size) 295 | case "SHA256": 296 | if s.HeaderSize != 0 { 297 | return nil, fmt.Errorf("unexpected HeaderSize for SHA256. Should be 0") 298 | } 299 | if s.Size != 48 { 300 | return nil, fmt.Errorf("unexpected signature size for SHA256. Should be 16+32") 301 | } 302 | sigData, err = parseList(sigData, s.Size) 303 | case "EXTERNAL MANAGEMENT": 304 | if s.HeaderSize != 0 { 305 | return nil, fmt.Errorf("unexpected HeaderSize for EXTERNAL MANAGEMENT. Should be 0") 306 | } 307 | 308 | if s.Size != 17 { 309 | return nil, fmt.Errorf("unexpected signature size for EXTERNAL MANAGEMENT. Should be 16+1") 310 | } 311 | // Certs under external management should be ignored. 312 | // we discard the data we read. 313 | _, err = parseList(sigData, s.Size) 314 | default: 315 | // if s.Size != 0 { 316 | // buf := make([]byte, s.Size) 317 | // if err := binary.Read(f, binary.LittleEndian, buf); err != nil { 318 | // return nil, errors.Wrap(err, "could not read default list") 319 | // } 320 | // } 321 | return nil, fmt.Errorf("not implemented signature list certificate: %s", sig) 322 | } 323 | if err != nil { 324 | return &SignatureList{}, err 325 | } 326 | s.Signatures = sigData 327 | return &s, nil 328 | } 329 | -------------------------------------------------------------------------------- /efi/signature/signature_list_test.go: -------------------------------------------------------------------------------- 1 | package signature 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | "testing" 9 | 10 | "github.com/foxboron/go-uefi/efi/attributes" 11 | "github.com/foxboron/go-uefi/efi/util" 12 | "go.mozilla.org/pkcs7" 13 | ) 14 | 15 | func ReadTestData(dir string) []string { 16 | var paths []string 17 | files, _ := os.ReadDir(dir) 18 | for _, file := range files { 19 | paths = append(paths, filepath.Join(dir, file.Name())) 20 | } 21 | return paths 22 | } 23 | 24 | var ( 25 | EfivarsTestFiles = ReadTestData("../../tests/data/signatures/efivars") 26 | SiglistTestFiles = ReadTestData("../../tests/data/signatures/siglist") 27 | SiglistchecksumTestFiles = ReadTestData("../../tests/data/signatures/siglistchecksum") 28 | SigsupportTestFiles = ReadTestData("../../tests/data/signatures/sigsupport") 29 | ) 30 | 31 | func TestParseSignatureListVars(t *testing.T) { 32 | for _, path := range EfivarsTestFiles { 33 | attrs, data, err := attributes.ReadEfivarsFile(path) 34 | var pkflags attributes.Attributes 35 | pkflags |= attributes.EFI_VARIABLE_NON_VOLATILE 36 | pkflags |= attributes.EFI_VARIABLE_BOOTSERVICE_ACCESS 37 | pkflags |= attributes.EFI_VARIABLE_RUNTIME_ACCESS 38 | pkflags |= attributes.EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS 39 | if (pkflags & attrs) != pkflags { 40 | t.Errorf("Incorrect bitmask") 41 | } 42 | 43 | c, err := ReadSignatureList(data) 44 | if err != nil { 45 | t.Fatal(err) 46 | } 47 | if util.CmpEFIGUID(c.SignatureType, CERT_X509_GUID) { 48 | // Run over and ensure we are getting the correct type 49 | for _, d := range c.Signatures { 50 | _, err := pkcs7.NewSignedData(d.Data) 51 | if err != nil { 52 | t.Fatal(err) 53 | } 54 | } 55 | } 56 | } 57 | } 58 | 59 | func TestParseSignatureListFile(t *testing.T) { 60 | for _, path := range SiglistTestFiles { 61 | b, _ := os.ReadFile(path) 62 | f := bytes.NewReader(b) 63 | c, err := ReadSignatureList(f) 64 | if err != nil { 65 | t.Fatal(err) 66 | } 67 | if util.CmpEFIGUID(c.SignatureType, CERT_X509_GUID) { 68 | for _, d := range c.Signatures { 69 | _, err := pkcs7.NewSignedData(d.Data) 70 | if err != nil { 71 | t.Fatal(err) 72 | } 73 | } 74 | } 75 | } 76 | } 77 | 78 | func TestParseSignatureListHashFile(t *testing.T) { 79 | for _, path := range SiglistchecksumTestFiles { 80 | b, _ := os.ReadFile(path) 81 | f := bytes.NewReader(b) 82 | c, err := ReadSignatureList(f) 83 | if err != nil { 84 | t.Fatal(err) 85 | } 86 | if util.CmpEFIGUID(c.SignatureType, CERT_SHA256_GUID) { 87 | for _, d := range c.Signatures { 88 | if fmt.Sprintf("%x", d.Data) != "4be2e8d5ef8113c3b9218f05f8aed1df8a6b0e24c706360d39f74a7423f00e32" { 89 | t.Fatal("Not correct checksum") 90 | } 91 | } 92 | } 93 | } 94 | } 95 | 96 | func TestParseSignatureSupport(t *testing.T) { 97 | for _, path := range SigsupportTestFiles { 98 | attrs, data, err := attributes.ReadEfivarsFile(path) 99 | if err != nil { 100 | t.Fatal(err) 101 | } 102 | var pkflags attributes.Attributes 103 | pkflags |= attributes.EFI_VARIABLE_BOOTSERVICE_ACCESS 104 | pkflags |= attributes.EFI_VARIABLE_RUNTIME_ACCESS 105 | if (pkflags & attrs) != pkflags { 106 | t.Errorf("Incorrect bitmask") 107 | } 108 | guids, err := GetSupportedSignatures(data) 109 | if err != nil { 110 | t.Fatal(err) 111 | } 112 | if guids[0] != CERT_SHA1_GUID { 113 | t.Errorf("Unexpected certificate, should be SHA1") 114 | } 115 | if guids[1] != CERT_SHA256_GUID { 116 | t.Errorf("Unexpected certificate, should be SHA256") 117 | } 118 | if guids[2] != CERT_RSA2048_GUID { 119 | t.Errorf("Unexpected certificate, should be RSA2048") 120 | } 121 | if guids[3] != CERT_X509_GUID { 122 | t.Errorf("Unexpected certificate, should be X509") 123 | } 124 | } 125 | } 126 | 127 | var sigdata = []SignatureData{ 128 | SignatureData{Owner: util.EFIGUID{Data1: 0xc1095e1b, Data2: 0x8a3b, Data3: 0x4cf5, Data4: [8]uint8{0x9d, 0x4a, 0xaf, 0xc7, 0xd7, 0x5d, 0xca, 0x68}}, Data: []uint8{0x81, 0xb4, 0xd9, 0x69, 0x31, 0xbf, 0xd, 0x2, 0xfd, 0x91, 0xa6, 0x1e, 0x19, 0xd1, 0x4f, 0x1d, 0xa4, 0x52, 0xe6, 0x6d, 0xb2, 0x40, 0x8c, 0xa8, 0x60, 0x4d, 0x41, 0x1f, 0x92, 0x65, 0x9f, 0xa}}, 129 | SignatureData{Owner: util.EFIGUID{Data1: 0xc1095e1b, Data2: 0x8a3b, Data3: 0x4cf5, Data4: [8]uint8{0x9d, 0x4a, 0xaf, 0xc7, 0xd7, 0x5d, 0xca, 0x68}}, Data: []uint8{0x82, 0xb4, 0xd9, 0x69, 0x31, 0xbf, 0xd, 0x2, 0xfd, 0x91, 0xa6, 0x1e, 0x19, 0xd1, 0x4f, 0x1d, 0xa4, 0x52, 0xe6, 0x6d, 0xb2, 0x40, 0x8c, 0xa8, 0x60, 0x4d, 0x41, 0x1f, 0x92, 0x65, 0x9f, 0xa}}, 130 | SignatureData{Owner: util.EFIGUID{Data1: 0xc1095e1b, Data2: 0x8a3b, Data3: 0x4cf5, Data4: [8]uint8{0x9d, 0x4a, 0xaf, 0xc7, 0xd7, 0x5d, 0xca, 0x68}}, Data: []uint8{0x83, 0xb4, 0xd9, 0x69, 0x31, 0xbf, 0xd, 0x2, 0xfd, 0x91, 0xa6, 0x1e, 0x19, 0xd1, 0x4f, 0x1d, 0xa4, 0x52, 0xe6, 0x6d, 0xb2, 0x40, 0x8c, 0xa8, 0x60, 0x4d, 0x41, 0x1f, 0x92, 0x65, 0x9f, 0xa}}, 131 | } 132 | 133 | func TestSiglist(t *testing.T) { 134 | sl := NewSignatureList(CERT_SHA256_GUID) 135 | for _, sig := range sigdata { 136 | sl.AppendBytes(sig.Owner, sig.Data) 137 | } 138 | if sl.ListSize != 172 { 139 | t.Fatal("list size incorrect") 140 | } 141 | if sl.Size != 48 { 142 | t.Fatal("size incorrect") 143 | } 144 | if len(sl.Signatures) != 3 { 145 | t.Fatal("number of signatures wrong") 146 | } 147 | } 148 | 149 | func TestSiglistRemove(t *testing.T) { 150 | sl := NewSignatureList(CERT_SHA256_GUID) 151 | for _, sig := range sigdata { 152 | sl.AppendBytes(sig.Owner, sig.Data) 153 | } 154 | err := sl.RemoveSignature(sigdata[0]) 155 | if err != nil { 156 | t.Error(err) 157 | } 158 | if len(sl.Signatures) != 2 { 159 | t.Fatal("remove: number of signatures wrong") 160 | } 161 | if sl.Size != 48 { 162 | t.Fatal("remove: size incorrect") 163 | } 164 | if sl.ListSize != 124 { 165 | t.Fatal("remove: list size incorrect") 166 | } 167 | } 168 | 169 | func TestSiglistRemoveSame(t *testing.T) { 170 | sl := NewSignatureList(CERT_SHA256_GUID) 171 | for _, sig := range sigdata { 172 | sl.AppendBytes(sig.Owner, sig.Data) 173 | } 174 | sl.RemoveSignature(sigdata[0]) 175 | // Try remove the same sigdata, should give error 176 | err := sl.RemoveSignature(sigdata[0]) 177 | if err == nil { 178 | t.Error(err) 179 | } 180 | } 181 | 182 | func TestSiglistRemoveAll(t *testing.T) { 183 | sl := NewSignatureList(CERT_SHA256_GUID) 184 | for _, sig := range sigdata { 185 | sl.AppendBytes(sig.Owner, sig.Data) 186 | } 187 | var err error 188 | for _, sig := range sigdata { 189 | err = sl.RemoveSignature(sig) 190 | } 191 | if err != nil { 192 | t.Error(err) 193 | } 194 | if len(sl.Signatures) != 0 { 195 | t.Fatal("remove: number of signatures wrong") 196 | } 197 | if sl.Size != 0 { 198 | t.Fatal("remove: size incorrect") 199 | } 200 | if sl.ListSize != 28 { 201 | t.Fatal("remove: list size incorrect") 202 | } 203 | } 204 | 205 | func TestSiglistExists(t *testing.T) { 206 | sl1 := NewSignatureList(CERT_SHA256_GUID) 207 | for _, sig := range sigdata { 208 | sl1.AppendBytes(sig.Owner, sig.Data) 209 | } 210 | sl2 := NewSignatureList(CERT_SHA256_GUID) 211 | for _, sig := range sigdata { 212 | sl2.AppendBytes(sig.Owner, sig.Data) 213 | } 214 | if !sl1.ExistsInList(sl2) { 215 | t.Fatal("exists: not the same list") 216 | } 217 | } 218 | 219 | func TestSiglistSigDataExists(t *testing.T) { 220 | sl := NewSignatureList(CERT_SHA256_GUID) 221 | for _, sig := range sigdata { 222 | sl.AppendBytes(sig.Owner, sig.Data) 223 | } 224 | if ok, _ := sl.Exists(&sigdata[0]); !ok { 225 | t.Fatal("exists: sigdata is not in the list") 226 | } 227 | } 228 | 229 | func TestSiglists(t *testing.T) { 230 | for _, c := range []struct { 231 | name string 232 | f string 233 | }{ 234 | { 235 | name: "db", 236 | f: "testdata/db", 237 | }, 238 | { 239 | name: "dbdefault", 240 | f: "testdata/dbdefault", 241 | }, 242 | { 243 | name: "kek", 244 | f: "testdata/kek", 245 | }, 246 | { 247 | name: "kekdefault", 248 | f: "testdata/kekdefault", 249 | }, 250 | { 251 | name: "pk", 252 | f: "testdata/pk", 253 | }, 254 | { 255 | name: "pkdefault", 256 | f: "testdata/pkdefault", 257 | }, 258 | } { 259 | t.Run(c.name, func(t *testing.T) { 260 | _, data, err := attributes.ReadEfivarsFile(c.f) 261 | if err != nil { 262 | t.Fatalf("%v", err) 263 | } 264 | _, err = ReadSignatureDatabase(data) 265 | if err != nil { 266 | t.Fatalf("%v", err) 267 | } 268 | }) 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /efi/signature/testdata/db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/efi/signature/testdata/db -------------------------------------------------------------------------------- /efi/signature/testdata/dbdefault: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/efi/signature/testdata/dbdefault -------------------------------------------------------------------------------- /efi/signature/testdata/kek: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/efi/signature/testdata/kek -------------------------------------------------------------------------------- /efi/signature/testdata/kekdefault: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/efi/signature/testdata/kekdefault -------------------------------------------------------------------------------- /efi/signature/testdata/pk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/efi/signature/testdata/pk -------------------------------------------------------------------------------- /efi/signature/testdata/pkdefault: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/efi/signature/testdata/pkdefault -------------------------------------------------------------------------------- /efi/signature/varsign.go: -------------------------------------------------------------------------------- 1 | package signature 2 | 3 | import ( 4 | "bytes" 5 | "crypto" 6 | "crypto/x509" 7 | "encoding/binary" 8 | "io" 9 | "log" 10 | 11 | "github.com/pkg/errors" 12 | "golang.org/x/crypto/cryptobyte" 13 | 14 | "github.com/foxboron/go-uefi/efi/util" 15 | "github.com/foxboron/go-uefi/efivar" 16 | "github.com/foxboron/go-uefi/pkcs7" 17 | ) 18 | 19 | // Section 32.2.4 Code Defintiions 20 | // Page. 1707 21 | // WIN_CERTIFICATE_UEFI_GUID 22 | 23 | // According to page 1705 24 | // UEFI Spec February 2020 25 | var WIN_CERTIFICATE_REVISION uint16 = 0x0200 26 | 27 | type WINCertType uint16 28 | 29 | // Page 1705 30 | // 0x0EF0 to 0x0EFF is the reserved range 31 | var ( 32 | WIN_CERT_TYPE_PKCS_SIGNED_DATA WINCertType = 0x0002 33 | WIN_CERT_TYPE_EFI_PKCS1_15 WINCertType = 0x0EF0 34 | WIN_CERT_TYPE_EFI_GUID WINCertType = 0x0EF1 35 | ) 36 | 37 | var WINCertTypeString = map[WINCertType]string{ 38 | 0x0002: "WIN_CERT_TYPE_PKCS_SIGNED_DATA", 39 | 0x0EF0: "WIN_CERT_TYPE_EFI_PKCS1_15", 40 | 0x0EF1: "WIN_CERT_TYPE_EFI_GUID", 41 | } 42 | 43 | // PE/COFF structure for signing 44 | // Page 1705 45 | type WINCertificate struct { 46 | Length uint32 47 | Revision uint16 48 | CertType WINCertType 49 | Certificate []uint8 50 | } 51 | 52 | const SizeofWINCertificate = 4 + 2 + 2 53 | 54 | var ErrParse = errors.New("could not parse struct") 55 | 56 | func ReadWinCertificate(f io.Reader) (WINCertificate, error) { 57 | var cert WINCertificate 58 | for _, v := range []interface{}{&cert.Length, &cert.Revision, &cert.CertType} { 59 | if err := binary.Read(f, binary.LittleEndian, v); err != nil { 60 | return WINCertificate{}, errors.Wrapf(err, "could not parse WINCertificate") 61 | } 62 | } 63 | if cert.Revision != WIN_CERTIFICATE_REVISION { 64 | return WINCertificate{}, errors.Wrapf(ErrParse, "WINCertificate revision should be %x, but is %x. Malformed or invalid", WIN_CERTIFICATE_REVISION, cert.Revision) 65 | } 66 | certLength := make([]byte, cert.Length-SizeofWINCertificate) 67 | if err := binary.Read(f, binary.LittleEndian, certLength); err != nil { 68 | return WINCertificate{}, errors.Wrapf(err, "could not get signature data") 69 | } 70 | cert.Certificate = certLength[:] 71 | return cert, nil 72 | } 73 | 74 | func WriteWinCertificate(b io.Writer, w *WINCertificate) { 75 | for _, d := range []interface{}{w.Length, w.Revision, w.CertType, w.Certificate} { 76 | if err := binary.Write(b, binary.LittleEndian, d); err != nil { 77 | log.Fatal(err) 78 | } 79 | } 80 | } 81 | 82 | var ( 83 | EFI_CERT_TYPE_RSA2048_SHA256_GUID = util.EFIGUID{0xa7717414, 0xc616, 0x4977, [8]uint8{0x94, 0x20, 0x84, 0x47, 0x12, 0xa7, 0x35, 0xbf}} 84 | EFI_CERT_TYPE_PKCS7_GUID = util.EFIGUID{0x4aafd29d, 0x68df, 0x49ee, [8]uint8{0x8a, 0xa9, 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7}} 85 | ) 86 | 87 | // Should implement an interface 88 | // Page 1707 89 | type WinCertificateUEFIGUID struct { 90 | Header WINCertificate 91 | CertType util.EFIGUID // One of the EFI_CERT types 92 | CertData []uint8 93 | } 94 | 95 | const SizeofWinCertificateUEFIGUID = SizeofWINCertificate + util.SizeofEFIGUID 96 | 97 | func ReadWinCertificateUEFIGUID(f io.Reader) (WinCertificateUEFIGUID, error) { 98 | var cert WinCertificateUEFIGUID 99 | hdr, err := ReadWinCertificate(f) 100 | if err != nil { 101 | return WinCertificateUEFIGUID{}, errors.Wrap(err, "could not parse WINCert UEFI_GUID") 102 | } 103 | cert.Header = hdr 104 | reader := bytes.NewBuffer(cert.Header.Certificate) 105 | if err := binary.Read(reader, binary.LittleEndian, &cert.CertType); err != nil { 106 | log.Fatal(err) 107 | } 108 | rbuf := make([]byte, reader.Len()) 109 | if err := binary.Read(reader, binary.LittleEndian, rbuf); err != nil { 110 | log.Fatal(err) 111 | } 112 | cert.CertData = rbuf[:] 113 | return cert, nil 114 | } 115 | 116 | func WriteWinCertificateUEFIGUID(b *bytes.Buffer, w *WinCertificateUEFIGUID) { 117 | WriteWinCertificate(b, &w.Header) 118 | if err := binary.Write(b, binary.LittleEndian, w.CertType); err != nil { 119 | log.Fatal(err) 120 | } 121 | if err := binary.Write(b, binary.LittleEndian, w.CertData); err != nil { 122 | log.Fatal(err) 123 | } 124 | } 125 | 126 | // Page. 238 127 | // Only used when EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS is set 128 | type EFIVariableAuthentication3 struct { 129 | Version uint8 130 | Type uint8 131 | MetadataSize uint32 132 | Flags uint32 133 | } 134 | 135 | // Page. 238 136 | // Only accepts the CertType EFI_CERT_TYPE_PKCS7_GUID 137 | type EFIVariableAuthentication2 struct { 138 | Time util.EFITime 139 | AuthInfo WinCertificateUEFIGUID 140 | } 141 | 142 | // Returns an EFIVariableAuthencation2 struct 143 | // no SignedData 144 | func NewEFIVariableAuthentication2() *EFIVariableAuthentication2 { 145 | return &EFIVariableAuthentication2{ 146 | Time: *util.NewEFITime(), 147 | AuthInfo: WinCertificateUEFIGUID{ 148 | Header: WINCertificate{ 149 | Length: SizeofWinCertificateUEFIGUID, 150 | Revision: WIN_CERTIFICATE_REVISION, 151 | CertType: WIN_CERT_TYPE_EFI_GUID, 152 | }, 153 | CertType: EFI_CERT_TYPE_PKCS7_GUID, 154 | }, 155 | } 156 | } 157 | 158 | func (e *EFIVariableAuthentication2) Marshal(b *bytes.Buffer) { 159 | WriteEFIVariableAuthencation2(b, *e) 160 | } 161 | 162 | func (e *EFIVariableAuthentication2) Unmarshal(b *bytes.Buffer) error { 163 | auth, err := ReadEFIVariableAuthencation2(b) 164 | if err != nil { 165 | return err 166 | } 167 | *e = *auth 168 | return nil 169 | } 170 | 171 | func (e *EFIVariableAuthentication2) Verify(cert *x509.Certificate) (bool, error) { 172 | signature, err := pkcs7.ParsePKCS7(e.AuthInfo.CertData) 173 | if err != nil { 174 | return false, err 175 | } 176 | return signature.Verify(cert) 177 | } 178 | 179 | // We should maybe not duplicate this 180 | // TODO: Remove this 181 | // See efivarfs/types.go 182 | type efibytes bytes.Buffer 183 | 184 | func (e efibytes) Marshal(b *bytes.Buffer) { 185 | if _, err := io.Copy(b, (*bytes.Buffer)(&e)); err != nil { 186 | return 187 | } 188 | } 189 | 190 | func (e efibytes) Bytes() []byte { 191 | var b bytes.Buffer 192 | e.Marshal(&b) 193 | return b.Bytes() 194 | } 195 | 196 | func SignEFIVariable(v efivar.Efivar, m efivar.Marshallable, key crypto.Signer, cert *x509.Certificate) (*EFIVariableAuthentication2, efivar.Marshallable, error) { 197 | authvar := NewEFIVariableAuthentication2() 198 | 199 | // Bytes of the signaturedatabase (probably) 200 | var sb bytes.Buffer 201 | 202 | // Buffer for the bytes we are signing 203 | var buf bytes.Buffer 204 | 205 | s := []byte{} 206 | for _, n := range []byte(v.Name) { 207 | s = append(s, n, 0x00) 208 | } 209 | 210 | // Marshal the bytes so we can include them in for our signed data 211 | m.Marshal(&sb) 212 | 213 | writeOrder := []interface{}{ 214 | s, 215 | *v.GUID, 216 | v.Attributes, 217 | authvar.Time, 218 | sb.Bytes(), 219 | } 220 | for _, d := range writeOrder { 221 | if err := binary.Write(&buf, binary.LittleEndian, d); err != nil { 222 | log.Fatal(err) 223 | } 224 | } 225 | 226 | der, err := pkcs7.SignPKCS7(key, cert, pkcs7.OIDData, buf.Bytes()) 227 | if err != nil { 228 | return nil, nil, err 229 | } 230 | 231 | // We need to unwrap the outer ContentInfo layer 232 | cs := cryptobyte.String(der) 233 | _, signature, err := pkcs7.ParseContentInfo(&cs) 234 | if err != nil { 235 | return nil, nil, err 236 | } 237 | 238 | authvar.AuthInfo.Header.Length += uint32(len(signature)) 239 | authvar.AuthInfo.CertData = signature 240 | 241 | // Create a marshallable variable we can give to WriteVar 242 | // Wrapper which contains the Auth header with the Marshallable bytes behind 243 | var auth efibytes 244 | authvar.Marshal((*bytes.Buffer)(&auth)) 245 | m.Marshal((*bytes.Buffer)(&auth)) 246 | 247 | return authvar, auth, nil 248 | } 249 | 250 | func ReadEFIVariableAuthencation2(f io.Reader) (*EFIVariableAuthentication2, error) { 251 | var efi EFIVariableAuthentication2 252 | if err := binary.Read(f, binary.LittleEndian, &efi.Time); err != nil { 253 | log.Fatal(err) 254 | } 255 | authinfo, err := ReadWinCertificateUEFIGUID(f) 256 | if err != nil { 257 | return &EFIVariableAuthentication2{}, errors.Wrap(err, "could not parse WINCertificate UEFI_GUID") 258 | } 259 | efi.AuthInfo = authinfo 260 | if efi.AuthInfo.Header.CertType != WIN_CERT_TYPE_EFI_GUID { 261 | log.Fatalf("EFI_VARIABLE_AUTHENTICATION2 accepts only CertType WIN_CERT_TYPE_EFI_GUID. Got: %x", efi.AuthInfo.CertType) 262 | } 263 | return &efi, nil 264 | } 265 | 266 | func WriteEFIVariableAuthencation2(b *bytes.Buffer, e EFIVariableAuthentication2) { 267 | if err := binary.Write(b, binary.LittleEndian, e.Time); err != nil { 268 | log.Fatal(err) 269 | } 270 | WriteWinCertificateUEFIGUID(b, &e.AuthInfo) 271 | } 272 | 273 | // Page. 237 274 | // Deprecated. But defined because #reasons 275 | type EFIVariableAuthentication struct { 276 | MonotonicCount uint64 277 | AuthInfo util.EFIGUID // WIN_CERTIFICATE_UEFI_GUID 278 | } 279 | -------------------------------------------------------------------------------- /efi/signature/varsign_test.go: -------------------------------------------------------------------------------- 1 | package signature 2 | 3 | import ( 4 | "bytes" 5 | "log" 6 | "os" 7 | "path/filepath" 8 | "testing" 9 | ) 10 | 11 | func TestReadEFIVariableAuthentication2File(t *testing.T) { 12 | dir := "../../tests/data/signatures/varsign" 13 | files, err := os.ReadDir(dir) 14 | if err != nil { 15 | log.Fatal(err) 16 | } 17 | for _, file := range files { 18 | path := filepath.Join(dir, file.Name()) 19 | b, _ := os.ReadFile(path) 20 | f := bytes.NewReader(b) 21 | ReadEFIVariableAuthencation2(f) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /efi/util/certs.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "crypto/rsa" 5 | "crypto/x509" 6 | "encoding/pem" 7 | "fmt" 8 | "os" 9 | ) 10 | 11 | func ReadKey(b []byte) (*rsa.PrivateKey, error) { 12 | block, _ := pem.Decode(b) 13 | if block == nil { 14 | return nil, fmt.Errorf("failed to parse pem block") 15 | } 16 | priv, err := x509.ParsePKCS8PrivateKey(block.Bytes) 17 | if err != nil { 18 | return nil, fmt.Errorf("failed to parse key: %w", err) 19 | } 20 | switch priv := priv.(type) { 21 | case *rsa.PrivateKey: 22 | return priv, nil 23 | default: 24 | return nil, fmt.Errorf("unknown type of public key") 25 | } 26 | } 27 | 28 | func ReadKeyFromFile(path string) (*rsa.PrivateKey, error) { 29 | b, err := os.ReadFile(path) 30 | if err != nil { 31 | return nil, err 32 | } 33 | return ReadKey(b) 34 | } 35 | 36 | func ReadCert(b []byte) (*x509.Certificate, error) { 37 | block, _ := pem.Decode(b) 38 | if block == nil { 39 | return nil, fmt.Errorf("no pem block") 40 | } 41 | 42 | cert, err := x509.ParseCertificate(block.Bytes) 43 | if err != nil { 44 | return nil, fmt.Errorf("failed to parse cert: %w", err) 45 | } 46 | return cert, nil 47 | } 48 | 49 | func ReadCertFromFile(path string) (*x509.Certificate, error) { 50 | b, err := os.ReadFile(path) 51 | if err != nil { 52 | return nil, err 53 | } 54 | return ReadCert(b) 55 | } 56 | -------------------------------------------------------------------------------- /efi/util/efitime.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | // Section 8.2 - Time Services 9 | 10 | var ( 11 | EFI_TIME_ADJUST_DAYLIGHT uint8 = 0x01 12 | EFI_TIME_IN_DAYLIGHT uint8 = 0x02 13 | EFI_UNSPECIFIED_TIMEZONE uint16 = 0x07FF 14 | ) 15 | 16 | const SizeofEFITime = 2 + 6 + 4 + 2 + 2 17 | 18 | type EFITime struct { 19 | Year uint16 // 1900 - 9999 AKA Y99K y'all 20 | Month uint8 // 1-12 21 | Day uint8 // 1 -31 22 | Hour uint8 // 0 - 23 23 | Minute uint8 // 0 - 59 24 | Second uint8 // 0 - 59 25 | Pad1 uint8 26 | Nanosecond uint32 // 0 - 999,999,999 27 | TimeZone int16 // -1440 to 1440 or 2047 28 | Daylight uint8 29 | Pad2 uint8 30 | } 31 | 32 | func (e *EFITime) Format() string { 33 | return fmt.Sprintf("%d-%d-%d %d:%d:%d:%d", e.Year, e.Month, e.Day, e.Hour, e.Minute, e.Second, e.Nanosecond) 34 | } 35 | 36 | type EFITImeCapabilitie struct { 37 | Resolution uint32 38 | Accuracy uint32 39 | SetsToZero bool 40 | } 41 | 42 | func NewEFITime() *EFITime { 43 | t := time.Now() 44 | return &EFITime{ 45 | Year: uint16(t.Year()), 46 | Month: uint8(t.Month()), 47 | Day: uint8(t.Day()), 48 | Hour: uint8(t.Hour()), 49 | Minute: uint8(t.Minute()), 50 | Second: uint8(t.Second()), 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /efi/util/guid.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "encoding/hex" 7 | "fmt" 8 | "strings" 9 | ) 10 | 11 | // Defined two places 12 | 13 | // Section 7.3 - Protocol Handler Services 14 | // Related Definitions 15 | // Page 176 16 | 17 | // Appendix A - GUID and Time Formats 18 | // Page 2272 19 | 20 | type EFIGUID struct { 21 | Data1 uint32 22 | Data2 uint16 23 | Data3 uint16 24 | Data4 [8]uint8 25 | } 26 | 27 | const SizeofEFIGUID uint32 = 16 28 | 29 | // Pretty print an EFIGUID struct 30 | func (e *EFIGUID) Format() string { 31 | return fmt.Sprintf("%08x-%04x-%04x-%04x-%12x", e.Data1, e.Data2, e.Data3, e.Data4[:2], e.Data4[2:]) 32 | } 33 | 34 | func (e *EFIGUID) Bytes() []byte { 35 | return GUIDToBytes(e) 36 | } 37 | 38 | // Compare two EFIGUID structs 39 | func CmpEFIGUID(cmp1 EFIGUID, cmp2 EFIGUID) bool { 40 | return cmp1.Data1 == cmp2.Data1 && 41 | cmp1.Data2 == cmp2.Data2 && 42 | cmp1.Data3 == cmp2.Data3 && 43 | cmp1.Data4 == cmp2.Data4 44 | } 45 | 46 | // Convert a string to an EFIGUID 47 | func StringToGUID(s string) *EFIGUID { 48 | decoded, _ := hex.DecodeString(strings.ReplaceAll(s, "-", "")) 49 | return BytesToGUID(decoded) 50 | } 51 | 52 | // Convert a byte slice to an EFIGUID 53 | func BytesToGUID(s []byte) *EFIGUID { 54 | var efi EFIGUID 55 | f := bytes.NewReader(s[:]) 56 | binary.Read(f, binary.BigEndian, &efi) 57 | return &efi 58 | } 59 | 60 | // Convert an EFIGUID to a byte slice 61 | func GUIDToBytes(g *EFIGUID) []byte { 62 | b := new(bytes.Buffer) 63 | for _, v := range []interface{}{g.Data1, g.Data2, g.Data3, g.Data4} { 64 | binary.Write(b, binary.BigEndian, v) 65 | } 66 | return b.Bytes() 67 | } 68 | 69 | // Write an EFIGUID to a bytes.Buffer 70 | func WriteGUID(b *bytes.Buffer, g *EFIGUID) { 71 | for _, v := range []interface{}{g.Data1, g.Data2, g.Data3, g.Data4} { 72 | binary.Write(b, binary.BigEndian, v) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /efi/util/guid_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | // We might need them later :p 9 | var ( 10 | EFI_GLOBAL_GUID = EFIGUID{0x8be4df61, 0x93ca, 0x11d2, [8]uint8{0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c}} 11 | PARTITION_SYSTEM_GUID = EFIGUID{0xC12A7328, 0xF81F, 0x11d2, [8]uint8{0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B}} 12 | LEGACY_MBR_PARTITION_GUID = EFIGUID{0x024DEE41, 0x33E7, 0x11d3, [8]uint8{0x9D, 0x69, 0x00, 0x08, 0xC7, 0x81, 0xF3, 0x9F}} 13 | PARTITION_MSFT_RESERVED_GUID = EFIGUID{0xE3C9E316, 0x0B5C, 0x4DB8, [8]uint8{0x81, 0x7D, 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE}} 14 | PARTITION_BASIC_DATA_GUID = EFIGUID{0xEBD0A0A2, 0xB9E5, 0x4433, [8]uint8{0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7}} 15 | PARTITION_LINUX_RAID_GUID = EFIGUID{0xa19d880f, 0x05fc, 0x4d3b, [8]uint8{0xA0, 0x06, 0x74, 0x3f, 0x0f, 0x84, 0x91, 0x1e}} 16 | PARTITION_LINUX_SWAP_GUID = EFIGUID{0x0657fd6d, 0xa4ab, 0x43c4, [8]uint8{0x84, 0xE5, 0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f}} 17 | PARTITION_LINUX_LVM_GUID = EFIGUID{0xe6d6d379, 0xf507, 0x44c2, [8]uint8{0xa2, 0x3c, 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28}} 18 | ) 19 | 20 | var TestData = []struct { 21 | GUID EFIGUID 22 | Bytes []byte 23 | }{ 24 | { 25 | GUID: EFI_GLOBAL_GUID, 26 | Bytes: []byte{0x8b, 0xe4, 0xdf, 0x61, 0x93, 0xca, 0x11, 0xd2, 0xaa, 0xd, 0x0, 0xe0, 0x98, 0x3, 0x2b, 0x8c}, 27 | }, 28 | { 29 | GUID: PARTITION_SYSTEM_GUID, 30 | Bytes: []byte{0xc1, 0x2a, 0x73, 0x28, 0xf8, 0x1f, 0x11, 0xd2, 0xba, 0x4b, 0x0, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b}, 31 | }, 32 | { 33 | GUID: LEGACY_MBR_PARTITION_GUID, 34 | Bytes: []byte{0x2, 0x4d, 0xee, 0x41, 0x33, 0xe7, 0x11, 0xd3, 0x9d, 0x69, 0x0, 0x8, 0xc7, 0x81, 0xf3, 0x9f}, 35 | }, 36 | { 37 | GUID: PARTITION_MSFT_RESERVED_GUID, 38 | Bytes: []byte{0xe3, 0xc9, 0xe3, 0x16, 0xb, 0x5c, 0x4d, 0xb8, 0x81, 0x7d, 0xf9, 0x2d, 0xf0, 0x2, 0x15, 0xae}, 39 | }, 40 | 41 | { 42 | GUID: PARTITION_BASIC_DATA_GUID, 43 | Bytes: []byte{0xeb, 0xd0, 0xa0, 0xa2, 0xb9, 0xe5, 0x44, 0x33, 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7}, 44 | }, 45 | { 46 | GUID: PARTITION_LINUX_RAID_GUID, 47 | Bytes: []byte{0xa1, 0x9d, 0x88, 0xf, 0x5, 0xfc, 0x4d, 0x3b, 0xa0, 0x6, 0x74, 0x3f, 0xf, 0x84, 0x91, 0x1e}, 48 | }, 49 | { 50 | GUID: PARTITION_LINUX_SWAP_GUID, 51 | Bytes: []byte{0x6, 0x57, 0xfd, 0x6d, 0xa4, 0xab, 0x43, 0xc4, 0x84, 0xe5, 0x9, 0x33, 0xc8, 0x4b, 0x4f, 0x4f}, 52 | }, 53 | { 54 | GUID: PARTITION_LINUX_LVM_GUID, 55 | Bytes: []byte{0xe6, 0xd6, 0xd3, 0x79, 0xf5, 0x7, 0x44, 0xc2, 0xa2, 0x3c, 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28}, 56 | }, 57 | } 58 | 59 | func TestParseGUIDToBytes(t *testing.T) { 60 | for _, data := range TestData { 61 | b := GUIDToBytes(&data.GUID) 62 | if r := bytes.Equal(b, data.Bytes); !r { 63 | t.Errorf("Expected true, got false") 64 | } 65 | } 66 | } 67 | 68 | func TestParseBytesToGuid(t *testing.T) { 69 | for _, data := range TestData { 70 | g := BytesToGUID(data.Bytes) 71 | if g.Format() != data.GUID.Format() { 72 | t.Errorf("GUID doesn't match: Got %s, wanted %s", g.Format(), data.GUID.Format()) 73 | } 74 | } 75 | } 76 | 77 | func TestParseFormatRoundTrip(t *testing.T) { 78 | input := "8be4df61-93ca-11d2-aa0d-00e098032b8c" 79 | guid := StringToGUID(input) 80 | 81 | if guid.Format() != input { 82 | t.Errorf("GUID failed round trip: Got %s, wanted %s", guid.Format(), input) 83 | } 84 | } 85 | 86 | func TestParseFormatRoundTripWithLeadingZero(t *testing.T) { 87 | input := "4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" 88 | guid := StringToGUID(input) 89 | 90 | if guid.Format() != input { 91 | t.Errorf("GUID failed round trip: Got %s, wanted %s", guid.Format(), input) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /efi/util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "io" 7 | 8 | "golang.org/x/text/encoding/unicode" 9 | "golang.org/x/text/transform" 10 | ) 11 | 12 | // Read a null terminated string 13 | func ReadNullString(f io.Reader) []byte { 14 | var ret []byte 15 | for { 16 | block := make([]byte, 2) 17 | r, _ := f.Read(block) 18 | if r == 0 { 19 | break 20 | } 21 | ret = append(ret, block...) 22 | if bytes.Equal(block, []byte{0x00, 0x00}) { 23 | break 24 | } 25 | } 26 | return ret 27 | } 28 | 29 | func MarshalUtf16Var(s string) []byte { 30 | var b bytes.Buffer 31 | utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) 32 | utf16Writer := transform.NewWriter(&b, utf16.NewEncoder()) 33 | utf16Writer.Write([]byte(s)) 34 | utf16Writer.Write([]byte("\x00")) 35 | return b.Bytes() 36 | } 37 | 38 | // Parse an efivar as a UTF-16 string. 39 | func ParseUtf16Var(data *bytes.Buffer) (string, error) { 40 | utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) 41 | utf16Reader := transform.NewReader(data, utf16.NewDecoder()) 42 | b, err := io.ReadAll(utf16Reader) 43 | 44 | if err != nil { 45 | return "", err 46 | } 47 | 48 | if b[len(b)-1] != 0 { 49 | return "", errors.New("value is not a null-terminated string") 50 | } 51 | 52 | return string(bytes.Trim(b, "\x00")), nil 53 | } 54 | -------------------------------------------------------------------------------- /efi/util/util_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestParseValidUtf16String(t *testing.T) { 9 | // This is "arch.efi", as encoded by a Dell laptop's firmware. 10 | value := []byte{ 11 | 97, 12 | 0, 13 | 114, 14 | 0, 15 | 99, 16 | 0, 17 | 104, 18 | 0, 19 | 46, 20 | 0, 21 | 101, 22 | 0, 23 | 102, 24 | 0, 25 | 105, 26 | 0, 27 | 0, 28 | 0, 29 | } 30 | buffer := bytes.NewBuffer(value) 31 | 32 | expected := "arch.efi" 33 | actual, err := ParseUtf16Var(buffer) 34 | 35 | if err != nil { 36 | t.Fatal(err) 37 | } 38 | 39 | if actual != expected { 40 | t.Fatalf( 41 | "ParseUtf16Var(%s) returned %v (%v), expected %v (%v).", 42 | value, 43 | actual, 44 | []byte(actual), 45 | expected, 46 | []byte(expected), 47 | ) 48 | } 49 | 50 | } 51 | 52 | func TestParseInvalidUtf16String(t *testing.T) { 53 | // This is "arch.efi", missing the final null strings. 54 | value := []byte{ 55 | 97, 56 | 0, 57 | 114, 58 | 0, 59 | 99, 60 | 0, 61 | 104, 62 | 0, 63 | 46, 64 | 0, 65 | 101, 66 | 0, 67 | 102, 68 | 0, 69 | 105, 70 | 0, 71 | } 72 | buffer := bytes.NewBuffer(value) 73 | 74 | _, err := ParseUtf16Var(buffer) 75 | if err == nil { 76 | t.Fatalf("ParseUtf16Var did not err with a non-null-terminated string.") 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /efivarfs/efifs.go: -------------------------------------------------------------------------------- 1 | package efivarfs 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | 7 | "github.com/foxboron/go-uefi/efi/attr" 8 | "github.com/foxboron/go-uefi/efi/attributes" 9 | "github.com/foxboron/go-uefi/efivar" 10 | "github.com/foxboron/go-uefi/efivarfs/fswrapper" 11 | ) 12 | 13 | // This package deals with the interface actually writing the variables properly 14 | // to the efivarfs backend. 15 | 16 | var ( 17 | ErrImmutable = attr.ErrIsImmutable 18 | ErrIncorrectAttributes = errors.New("efivar has the wrong attributes") 19 | ) 20 | 21 | // EFIVars is the interface for interacting with writing and getting EFI variables. 22 | type EFIVars interface { 23 | GetVar(efivar.Efivar, efivar.Unmarshallable) error 24 | GetVarWithAttributes(efivar.Efivar, efivar.Unmarshallable) (attributes.Attributes, error) 25 | WriteVar(efivar.Efivar, efivar.Marshallable) error 26 | } 27 | 28 | // EFIFS is a struct that combines reading variables from the file system while also ensuring we are 29 | type EFIFS struct { 30 | *fswrapper.FSWrapper 31 | } 32 | 33 | var _ EFIVars = &EFIFS{} 34 | 35 | // NewFS creates a new instance of *EFIFS 36 | func NewFS() *EFIFS { 37 | return &EFIFS{ 38 | fswrapper.NewFSWrapper(), 39 | } 40 | } 41 | 42 | // Open returns a initialization Efivarfs for high-level abstractions. 43 | func (f *EFIFS) Open() *Efivarfs { 44 | return &Efivarfs{f} 45 | } 46 | 47 | // Check if file is immutable before writing to the file. 48 | // Returns ErrImmutable if the file is immutable. 49 | func (f *EFIFS) CheckImmutable() *EFIFS { 50 | f.FSWrapper.CheckImmutable() 51 | return f 52 | } 53 | 54 | // UnsetImmutable implicitly when writing towards a file. 55 | func (f *EFIFS) UnsetImmutable() *EFIFS { 56 | f.FSWrapper.UnsetImmutable() 57 | return f 58 | } 59 | 60 | // GetVar parses and unmarshalls a EFI variable. 61 | func (t *EFIFS) GetVar(v efivar.Efivar, e efivar.Unmarshallable) error { 62 | if _, err := t.GetVarWithAttributes(v, e); err != nil { 63 | return err 64 | } 65 | return nil 66 | } 67 | 68 | // GetVarWithAttributes parses and unmarshalls a EFI variable, while also 69 | // returning the parsed attributes. 70 | func (t *EFIFS) GetVarWithAttributes(v efivar.Efivar, e efivar.Unmarshallable) (attributes.Attributes, error) { 71 | attrs, buf, err := t.ReadEfivarsWithGuid(v.Name, *v.GUID) 72 | if err != nil { 73 | return 0, err 74 | } 75 | 76 | if !v.Attributes.Equal(attrs) { 77 | return attrs, ErrIncorrectAttributes 78 | } 79 | 80 | if err := e.Unmarshal(buf); err != nil { 81 | return 0, err 82 | } 83 | 84 | return attrs, nil 85 | } 86 | 87 | // WriteVar writes an EFI variables to the EFIFS. 88 | func (t *EFIFS) WriteVar(v efivar.Efivar, e efivar.Marshallable) error { 89 | var b bytes.Buffer 90 | e.Marshal(&b) 91 | return t.WriteEfivarsWithGuid(v.Name, v.Attributes, b.Bytes(), *v.GUID) 92 | } 93 | -------------------------------------------------------------------------------- /efivarfs/efivarfs.go: -------------------------------------------------------------------------------- 1 | package efivarfs 2 | 3 | import ( 4 | "bytes" 5 | "crypto" 6 | "crypto/x509" 7 | "encoding/binary" 8 | "fmt" 9 | 10 | "github.com/foxboron/go-uefi/efi/device" 11 | "github.com/foxboron/go-uefi/efi/signature" 12 | "github.com/foxboron/go-uefi/efivar" 13 | ) 14 | 15 | // This is the high-level abstraction of efivarfs. It gives you the easy 16 | // variable access and auxillary functions you should expect from a library like 17 | // this. 18 | 19 | type Efivarfs struct { 20 | EFIVars 21 | } 22 | 23 | func Open(e EFIVars) *Efivarfs { 24 | return &Efivarfs{e} 25 | } 26 | 27 | func (e *Efivarfs) GetPK() (*signature.SignatureDatabase, error) { 28 | var rsb signature.SignatureDatabase 29 | if err := e.GetVar(efivar.PK, &rsb); err != nil { 30 | return nil, err 31 | } 32 | return &rsb, nil 33 | } 34 | 35 | func (e *Efivarfs) GetSetupMode() (bool, error) { 36 | var rsb efibool 37 | if err := e.GetVar(efivar.SetupMode, &rsb); err != nil { 38 | return false, err 39 | } 40 | return (bool)(rsb), nil 41 | } 42 | 43 | func (e *Efivarfs) GetSecureBoot() (bool, error) { 44 | var rsb efibool 45 | if err := e.GetVar(efivar.SecureBoot, &rsb); err != nil { 46 | return false, err 47 | } 48 | return (bool)(rsb), nil 49 | } 50 | 51 | func (e *Efivarfs) GetKEK() (*signature.SignatureDatabase, error) { 52 | var rsb signature.SignatureDatabase 53 | if err := e.GetVar(efivar.KEK, &rsb); err != nil { 54 | return nil, err 55 | } 56 | return &rsb, nil 57 | } 58 | 59 | func (e *Efivarfs) Getdb() (*signature.SignatureDatabase, error) { 60 | var rsb signature.SignatureDatabase 61 | if err := e.GetVar(efivar.Db, &rsb); err != nil { 62 | return nil, err 63 | } 64 | return &rsb, nil 65 | } 66 | 67 | func (e *Efivarfs) Getdbx() (*signature.SignatureDatabase, error) { 68 | var rsb signature.SignatureDatabase 69 | if err := e.GetVar(efivar.Dbx, &rsb); err != nil { 70 | return nil, err 71 | } 72 | return &rsb, nil 73 | } 74 | 75 | // Writes a signed variable update 76 | func (e *Efivarfs) WriteSignedUpdate(v efivar.Efivar, m efivar.Marshallable, key crypto.Signer, cert *x509.Certificate) error { 77 | // The reason why we do this is because we are wrapping a bytes.Butter in a 78 | // marshaller interface to pass through the layers 79 | // I haven't decided if this is.. elegant or not. 80 | _, marshal, err := signature.SignEFIVariable(v, m, key, cert) 81 | if err != nil { 82 | return err 83 | } 84 | 85 | return e.WriteVar(v, marshal) 86 | } 87 | 88 | func (e *Efivarfs) GetBootEntry(option string) (*device.EFILoadOption, error) { 89 | var rsb device.EFILoadOption 90 | entry := efivar.BootEntry 91 | entry.Name = option 92 | if err := e.GetVar(entry, &rsb); err != nil { 93 | return nil, err 94 | } 95 | return &rsb, nil 96 | } 97 | 98 | type bootorder []string 99 | 100 | func (bo *bootorder) Unmarshal(b *bytes.Buffer) error { 101 | for i := 0; b.Len() != 0; i += 2 { 102 | sec := make([]byte, 2) 103 | b.Read(sec) 104 | val := binary.BigEndian.Uint16([]byte{sec[1], sec[0]}) 105 | *bo = append(*bo, fmt.Sprintf("Boot%04x", val)) 106 | } 107 | return nil 108 | } 109 | 110 | func (e *Efivarfs) GetBootOrder() []string { 111 | var rsb bootorder 112 | if err := e.GetVar(efivar.BootOrder, &rsb); err != nil { 113 | return nil 114 | } 115 | return rsb 116 | } 117 | 118 | func (e *Efivarfs) GetLoaderEntrySelected() (string, error) { 119 | var rsb efivar.Efistring 120 | if err := e.GetVar(efivar.LoaderEntrySelected, &rsb); err != nil { 121 | return "", err 122 | } 123 | return string(rsb), nil 124 | } 125 | -------------------------------------------------------------------------------- /efivarfs/efivarfs_test.go: -------------------------------------------------------------------------------- 1 | package efivarfs_test 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | "testing/fstest" 7 | 8 | "github.com/foxboron/go-uefi/asntest" 9 | "github.com/foxboron/go-uefi/efi/efitest" 10 | "github.com/foxboron/go-uefi/efi/signature" 11 | "github.com/foxboron/go-uefi/efi/util" 12 | "github.com/foxboron/go-uefi/efivar" 13 | 14 | . "github.com/foxboron/go-uefi/efivarfs/testfs" 15 | ) 16 | 17 | var ( 18 | sigdata = []signature.SignatureData{ 19 | signature.SignatureData{Owner: util.EFIGUID{Data1: 0xc1095e1b, Data2: 0x8a3b, Data3: 0x4cf5, Data4: [8]uint8{0x9d, 0x4a, 0xaf, 0xc7, 0xd7, 0x5d, 0xca, 0x68}}, Data: []uint8{0x81, 0xb4, 0xd9, 0x69, 0x31, 0xbf, 0xd, 0x2, 0xfd, 0x91, 0xa6, 0x1e, 0x19, 0xd1, 0x4f, 0x1d, 0xa4, 0x52, 0xe6, 0x6d, 0xb2, 0x40, 0x8c, 0xa8, 0x60, 0x4d, 0x41, 0x1f, 0x92, 0x65, 0x9f, 0xa}}, 20 | signature.SignatureData{Owner: util.EFIGUID{Data1: 0xc1095e1b, Data2: 0x8a3b, Data3: 0x4cf5, Data4: [8]uint8{0x9d, 0x4a, 0xaf, 0xc7, 0xd7, 0x5d, 0xca, 0x68}}, Data: []uint8{0x82, 0xb4, 0xd9, 0x69, 0x31, 0xbf, 0xd, 0x2, 0xfd, 0x91, 0xa6, 0x1e, 0x19, 0xd1, 0x4f, 0x1d, 0xa4, 0x52, 0xe6, 0x6d, 0xb2, 0x40, 0x8c, 0xa8, 0x60, 0x4d, 0x41, 0x1f, 0x92, 0x65, 0x9f, 0xa}}, 21 | signature.SignatureData{Owner: util.EFIGUID{Data1: 0xc1095e1b, Data2: 0x8a3b, Data3: 0x4cf5, Data4: [8]uint8{0x9d, 0x4a, 0xaf, 0xc7, 0xd7, 0x5d, 0xca, 0x68}}, Data: []uint8{0x83, 0xb4, 0xd9, 0x69, 0x31, 0xbf, 0xd, 0x2, 0xfd, 0x91, 0xa6, 0x1e, 0x19, 0xd1, 0x4f, 0x1d, 0xa4, 0x52, 0xe6, 0x6d, 0xb2, 0x40, 0x8c, 0xa8, 0x60, 0x4d, 0x41, 0x1f, 0x92, 0x65, 0x9f, 0xa}}, 22 | } 23 | ) 24 | 25 | func TestGetSecureBootEfivar(t *testing.T) { 26 | efivarfs := NewTestFS(). 27 | With(efitest.SetUpModeOff()). 28 | Open() 29 | ok, err := efivarfs.GetSetupMode() 30 | if err != nil { 31 | t.Fatalf("%v", err) 32 | } 33 | if ok != true { 34 | t.Fatalf("wrong") 35 | } 36 | } 37 | 38 | func TestWriteSignatureDatabaseEfivar(t *testing.T) { 39 | efivarfs := NewTestFS().Open() 40 | 41 | // Write some test data 42 | var wsb signature.SignatureDatabase 43 | for _, sig := range sigdata { 44 | wsb.AppendSignature(signature.CERT_SHA256_GUID, &sig) 45 | } 46 | 47 | if err := efivarfs.WriteVar(efivar.Db, &wsb); err != nil { 48 | t.Fatalf("encountered error: %v", err) 49 | } 50 | 51 | var rsb signature.SignatureDatabase 52 | if err := efivarfs.GetVar(efivar.Db, &rsb); err != nil { 53 | t.Fatalf("encountered error: %v", err) 54 | } 55 | 56 | if !reflect.DeepEqual(wsb[0].Signatures, rsb[0].Signatures) { 57 | t.Fatalf("Not equal") 58 | } 59 | } 60 | 61 | func TestWriteSignatureDatabaseEfivarAuthedUpdate(t *testing.T) { 62 | efivarfs := NewTestFS().Open() 63 | 64 | cert, priv := asntest.InitCert() 65 | 66 | // Write some test data 67 | var wsb signature.SignatureDatabase 68 | for _, sig := range sigdata { 69 | wsb.AppendSignature(signature.CERT_SHA256_GUID, &sig) 70 | } 71 | 72 | err := efivarfs.WriteSignedUpdate(efivar.Db, &wsb, priv, cert) 73 | if err != nil { 74 | t.Fatal(err) 75 | } 76 | 77 | rsb, err := efivarfs.Getdb() 78 | if err != nil { 79 | t.Fatalf("encountered error: %v", err) 80 | } 81 | 82 | if !reflect.DeepEqual(wsb[0].Signatures, (*rsb)[0].Signatures) { 83 | t.Fatalf("Not equal") 84 | } 85 | } 86 | 87 | func TestGetBootOrder(t *testing.T) { 88 | efivarfs := NewTestFS(). 89 | With( 90 | fstest.MapFS{ 91 | "/sys/firmware/efi/efivars/BootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8c": {Data: []byte{ 92 | // 0001,2001,2002,2003,0000 93 | 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x20, 0x02, 0x20, 0x03, 0x20, 0x00, 0x00, 94 | }}, 95 | }, 96 | ). 97 | Open() 98 | entries := efivarfs.GetBootOrder() 99 | if len(entries) != 5 { 100 | t.Fatalf("incorrect number of boot entries") 101 | } 102 | } 103 | 104 | func TestGetBootEntry(t *testing.T) { 105 | efivarfs := NewTestFS(). 106 | With( 107 | fstest.MapFS{ 108 | "/sys/firmware/efi/efivars/BootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8c": {Data: []byte{ 109 | 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 110 | }}, 111 | "/sys/firmware/efi/efivars/Boot0001-8be4df61-93ca-11d2-aa0d-00e098032b8c": {Data: []byte{0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x74, 0x00, 0x4c, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x78, 0x00, 0x20, 0x00, 0x42, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x20, 0x00, 0x4d, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x04, 0x01, 0x2a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xf8, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xda, 0x34, 0xa8, 0x08, 0x99, 0xaa, 0x44, 0xb6, 0x7c, 0xd8, 0x06, 0x2c, 0x5a, 0x5c, 0x10, 0x02, 0x02, 0x04, 0x04, 0x46, 0x00, 0x5c, 0x00, 0x45, 0x00, 0x46, 0x00, 0x49, 0x00, 0x5c, 0x00, 0x73, 0x00, 0x79, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x64, 0x00, 0x5c, 0x00, 0x73, 0x00, 0x79, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x64, 0x00, 0x2d, 0x00, 0x62, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x78, 0x00, 0x36, 0x00, 0x34, 0x00, 0x2e, 0x00, 0x65, 0x00, 0x66, 0x00, 0x69, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x04, 0x00}}, 112 | }, 113 | ). 114 | Open() 115 | entry, err := efivarfs.GetBootEntry("Boot0001") 116 | if err != nil { 117 | t.Fatalf("%v", err) 118 | } 119 | if entry.Description != "Linux Boot Manager" { 120 | t.Fatalf("expected 'Linux Boot Manager' got '%s' instead", entry.Description) 121 | } 122 | 123 | if entry.FilePath[1].Format() != "File(\\EFI\\systemd\\systemd-bootx64.efi)" { 124 | t.Fatalf("expected filepath got '%s' instead", entry.FilePath[1].Format()) 125 | } 126 | } 127 | 128 | func TestGetLoaderEntrySelected(t *testing.T) { 129 | efivarfs := NewTestFS(). 130 | With( 131 | fstest.MapFS{ 132 | "/sys/firmware/efi/efivars/LoaderEntrySelected-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f": {Data: []byte{0x06, 0x00, 0x00, 0x00, 0x61, 0x00, 0x72, 0x00, 0x63, 0x00, 0x68, 0x00, 0x2d, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x78, 0x00, 0x2e, 0x00, 0x65, 0x00, 0x66, 0x00, 0x69, 0x00, 0x00, 0x00}}, 133 | }, 134 | ). 135 | Open() 136 | entry, err := efivarfs.GetLoaderEntrySelected() 137 | if err != nil { 138 | t.Fatalf("%v", err) 139 | } 140 | 141 | if entry != "arch-linux.efi" { 142 | t.Fatalf("entry does not match the loader entry") 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /efivarfs/fswrapper/fswrapper.go: -------------------------------------------------------------------------------- 1 | package fswrapper 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "io/fs" 10 | "os" 11 | "path" 12 | 13 | "github.com/foxboron/go-uefi/efi/attr" 14 | "github.com/foxboron/go-uefi/efi/attributes" 15 | "github.com/foxboron/go-uefi/efi/util" 16 | "github.com/spf13/afero" 17 | ) 18 | 19 | // This package is the lowest layer of the filesystem abstraction. 20 | // It ensures we have a filesystem api with WriteFile/ReadFile/OpenFile as this 21 | // is not suporting supported by io/fs nor 22 | // afero.Fs at the moment. 23 | 24 | // This should largely be considered a patch layer on top of our virtual filesystems! 25 | 26 | var ( 27 | errImmutable = attr.ErrIsImmutable 28 | ) 29 | 30 | // Unsure if we need this 31 | type efifswrappers interface { 32 | WriteFile(name string, data []byte, perm os.FileMode) error 33 | ReadFile(name string) ([]byte, error) 34 | OpenFile(name string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) 35 | Open(name string) (fs.File, error) 36 | SetFS(fs afero.Fs) 37 | // MkdirAll(path string, perm os.FileMode) error 38 | } 39 | 40 | type FSWrapper struct { 41 | unsetimmutable bool 42 | immutable bool 43 | fs afero.Fs 44 | } 45 | 46 | func (e *FSWrapper) SetFS(fs afero.Fs) { 47 | e.fs = fs 48 | } 49 | 50 | func (e *FSWrapper) CheckImmutable() { 51 | e.immutable = true 52 | } 53 | 54 | func (e *FSWrapper) UnsetImmutable() { 55 | e.unsetimmutable = true 56 | } 57 | 58 | func NewMemoryWrapper() *FSWrapper { 59 | return &FSWrapper{ 60 | unsetimmutable: false, 61 | immutable: false, 62 | fs: afero.NewMemMapFs(), 63 | } 64 | } 65 | 66 | func NewFSWrapper() *FSWrapper { 67 | return &FSWrapper{ 68 | unsetimmutable: false, 69 | immutable: false, 70 | fs: afero.NewOsFs(), 71 | } 72 | } 73 | 74 | func (t *FSWrapper) isimmutable(efivar string) error { 75 | if !t.immutable { 76 | return nil 77 | } 78 | err := attr.IsImmutable(efivar) 79 | switch { 80 | case errors.Is(err, attr.ErrIsImmutable): 81 | if !t.unsetimmutable { 82 | return fmt.Errorf("%s: %w", efivar, errImmutable) 83 | } 84 | if err := attr.UnsetImmutable(efivar); err != nil { 85 | return fmt.Errorf("couldn't unset immutable bit: %w", err) 86 | } 87 | case errors.Is(err, os.ErrNotExist): 88 | case err != nil: 89 | return err 90 | } 91 | return nil 92 | } 93 | 94 | func (t *FSWrapper) WriteFile(name string, data []byte, perm os.FileMode) error { 95 | if err := t.isimmutable(name); err != nil { 96 | return err 97 | } 98 | f, err := t.fs.Create(name) 99 | if err != nil { 100 | return err 101 | } 102 | _, err = f.Write(data) 103 | if err1 := f.Close(); err1 != nil && err == nil { 104 | err = err1 105 | } 106 | return err 107 | } 108 | 109 | func (t *FSWrapper) ReadFile(name string) ([]byte, error) { 110 | f, err := t.fs.Open(name) 111 | if err != nil { 112 | return nil, err 113 | } 114 | defer f.Close() 115 | 116 | var size int 117 | if info, err := f.Stat(); err == nil { 118 | size64 := info.Size() 119 | if int64(int(size64)) == size64 { 120 | size = int(size64) 121 | } 122 | } 123 | size++ // one byte for final read at EOF 124 | if size < 512 { 125 | size = 512 126 | } 127 | 128 | data := make([]byte, 0, size) 129 | for { 130 | if len(data) >= cap(data) { 131 | d := append(data[:cap(data)], 0) 132 | data = d[:len(data)] 133 | } 134 | n, err := f.Read(data[len(data):cap(data)]) 135 | data = data[:len(data)+n] 136 | if err != nil { 137 | if err == io.EOF { 138 | err = nil 139 | } 140 | return data, err 141 | } 142 | } 143 | } 144 | 145 | func (t *FSWrapper) OpenFile(name string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) { 146 | return t.fs.OpenFile(name, flag, perm) 147 | } 148 | 149 | func (t *FSWrapper) Open(name string) (fs.File, error) { 150 | return t.fs.Open(name) 151 | } 152 | 153 | func (t *FSWrapper) ParseEfivars(f io.Reader, size int) (attributes.Attributes, *bytes.Buffer, error) { 154 | var attrs attributes.Attributes 155 | if err := binary.Read(f, binary.LittleEndian, &attrs); err != nil { 156 | return 0, nil, fmt.Errorf("could not read file: %w", err) 157 | } 158 | buf := make([]byte, size-attributes.SizeofAttributes) 159 | if err := binary.Read(f, binary.LittleEndian, &buf); err != nil { 160 | return 0, nil, err 161 | } 162 | return attrs, bytes.NewBuffer(buf), nil 163 | } 164 | 165 | // For a full path instead of the inferred efivars path 166 | func (t *FSWrapper) ReadEfivarsFile(filename string) (attributes.Attributes, *bytes.Buffer, error) { 167 | f, err := t.fs.Open(filename) 168 | if err != nil { 169 | return 0, nil, err 170 | } 171 | defer f.Close() 172 | stat, err := f.Stat() 173 | if err != nil { 174 | return 0, nil, fmt.Errorf("could not stat file descriptor: %w", err) 175 | } 176 | return t.ParseEfivars(f, int(stat.Size())) 177 | } 178 | 179 | func (t *FSWrapper) ReadEfivarsWithGuid(filename string, guid util.EFIGUID) (attributes.Attributes, *bytes.Buffer, error) { 180 | f := path.Join(attributes.Efivars, fmt.Sprintf("%s-%s", filename, guid.Format())) 181 | return t.ReadEfivarsFile(f) 182 | } 183 | 184 | // Write an EFI variable to sysfs 185 | // TODO: Fix retryable writes 186 | func (t *FSWrapper) WriteEfivarsWithGuid(name string, attrs attributes.Attributes, b []byte, guid util.EFIGUID) error { 187 | efivar := path.Join(attributes.Efivars, fmt.Sprintf("%s-%s", name, guid.Format())) 188 | 189 | if err := t.isimmutable(efivar); err != nil { 190 | return err 191 | } 192 | 193 | flags := os.O_WRONLY | os.O_CREATE //| os.O_TRUNC 194 | if attrs&attributes.EFI_VARIABLE_APPEND_WRITE != 0 { 195 | flags |= os.O_APPEND 196 | } 197 | 198 | f, err := t.fs.OpenFile(efivar, flags, 0644) 199 | if err != nil { 200 | return fmt.Errorf("couldn't open file: %w", err) 201 | } 202 | defer f.Close() 203 | buf := append(attrs.Bytes(), b...) 204 | if n, err := f.Write(buf); err != nil { 205 | return fmt.Errorf("couldn't write efi variable: %w", err) 206 | } else if n != len(buf) { 207 | return errors.New("could not write the entire buffer") 208 | } 209 | return nil 210 | } 211 | -------------------------------------------------------------------------------- /efivarfs/testfs/testfs.go: -------------------------------------------------------------------------------- 1 | package testfs 2 | 3 | import ( 4 | "bytes" 5 | "os" 6 | "path/filepath" 7 | "testing/fstest" 8 | 9 | "github.com/foxboron/go-uefi/efi/signature" 10 | "github.com/foxboron/go-uefi/efivar" 11 | "github.com/foxboron/go-uefi/efivarfs" 12 | "github.com/foxboron/go-uefi/efivarfs/fswrapper" 13 | "github.com/spf13/afero" 14 | ) 15 | 16 | // TestFS deals with providing a layer controllable layer we can use for 17 | // integration tests. 18 | 19 | // This is the a wrapper around MapFS to easily inject data and convert it to afero.fs 20 | type TestFS struct { 21 | *efivarfs.EFIFS 22 | mapfs fstest.MapFS 23 | } 24 | 25 | func NewTestFS() *TestFS { 26 | return &TestFS{ 27 | EFIFS: &efivarfs.EFIFS{fswrapper.NewMemoryWrapper()}, 28 | mapfs: fstest.MapFS{}, 29 | } 30 | } 31 | 32 | // Convert fstest.MapFS to afero.Fs 33 | func fromMapFS(files fstest.MapFS) afero.Fs { 34 | memfs := afero.NewMemMapFs() 35 | for name, file := range files { 36 | if file.Mode.IsDir() { 37 | memfs.MkdirAll(name, file.Mode.Perm()) 38 | continue 39 | } 40 | // We ignore the error here as if the directory exists it should be fine. 41 | memfs.MkdirAll(filepath.Dir(name), 0644) 42 | f, err := memfs.OpenFile(name, os.O_CREATE, file.Mode.Perm()) 43 | if err != nil { 44 | continue 45 | } 46 | f.Write(file.Data) 47 | f.Close() 48 | } 49 | return memfs 50 | } 51 | 52 | // func (f *Testfs) ToFS() EFIFs { 53 | // return &FS{ 54 | // unsetimmutable: false, 55 | // immutable: false, 56 | // fs: f.ToAfero(), 57 | // } 58 | // } 59 | 60 | // With allows you to compose several overlay files into the in-memory filesystem. 61 | func (f *TestFS) With(files ...fstest.MapFS) *TestFS { 62 | for _, mapfs := range files { 63 | for path, file := range mapfs { 64 | f.mapfs[path] = file 65 | } 66 | } 67 | return f 68 | } 69 | 70 | // Open opens TestFS as Efivarfs 71 | func (f *TestFS) Open() *efivarfs.Efivarfs { 72 | overlayfs := fromMapFS(f.mapfs) 73 | // I don't think this was a good idea 74 | f.SetFS(overlayfs) 75 | return &efivarfs.Efivarfs{f} 76 | } 77 | 78 | // WriteVar is a shim around EFIFS.WriteVar and ensures variables written to the 79 | // in-memory filesystem is "unwrapped" of things like authentication headers if 80 | // they exist. 81 | func (f *TestFS) WriteVar(v efivar.Efivar, t efivar.Marshallable) error { 82 | switch v.Name { 83 | case "PK", "KEK", "db", "dbx": 84 | // Unwraps the auth header 85 | var b bytes.Buffer 86 | var sea signature.EFIVariableAuthentication2 87 | t.Marshal(&b) 88 | // Only unwrap the Auth header if it exists 89 | if err := sea.Unmarshal(&b); err == nil { 90 | var sb signature.SignatureDatabase 91 | sb.Unmarshal(&b) 92 | t = &sb 93 | } 94 | } 95 | return f.EFIFS.WriteVar(v, t) 96 | } 97 | -------------------------------------------------------------------------------- /efivarfs/types.go: -------------------------------------------------------------------------------- 1 | package efivarfs 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | ) 7 | 8 | // This package contains misc types we might promote to something more sane at a later point. 9 | // Currently only used internally to serialize/deserialize values we need to native Go types. 10 | 11 | // TODO: Move this to something better? 12 | // 13 | // I don't think we'll be using this to actually write stuff 14 | type efibool bool 15 | 16 | func (s *efibool) Unmarshal(b *bytes.Buffer) error { 17 | n, err := b.ReadByte() 18 | if err != nil { 19 | return err 20 | } 21 | *s = n == 1 22 | return nil 23 | } 24 | 25 | type efibytes bytes.Buffer 26 | 27 | func (e efibytes) Marshal(b *bytes.Buffer) { 28 | if _, err := io.Copy(b, (*bytes.Buffer)(&e)); err != nil { 29 | return 30 | } 31 | } 32 | 33 | func (e efibytes) Bytes() []byte { 34 | var b bytes.Buffer 35 | e.Marshal(&b) 36 | return b.Bytes() 37 | } 38 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/foxboron/go-uefi 2 | 3 | go 1.21.0 4 | 5 | require ( 6 | github.com/pkg/errors v0.9.1 7 | go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 8 | golang.org/x/crypto v0.31.0 9 | golang.org/x/sys v0.28.0 10 | golang.org/x/text v0.21.0 11 | ) 12 | 13 | require ( 14 | github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 15 | github.com/hugelgupf/vmtest v0.0.0-20240110072021-f6f07acb7aa1 16 | github.com/spf13/afero v1.9.3 17 | ) 18 | 19 | require ( 20 | github.com/creack/pty v1.1.21 // indirect 21 | github.com/dustin/go-humanize v1.0.1 // indirect 22 | github.com/google/goterm v0.0.0-20200907032337-555d40f16ae2 // indirect 23 | github.com/hashicorp/errwrap v1.1.0 // indirect 24 | github.com/hashicorp/go-multierror v1.1.1 // indirect 25 | github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 // indirect 26 | github.com/josharian/native v1.1.0 // indirect 27 | github.com/klauspost/compress v1.17.4 // indirect 28 | github.com/klauspost/pgzip v1.2.6 // indirect 29 | github.com/mattn/go-isatty v0.0.20 // indirect 30 | github.com/mdlayher/packet v1.1.2 // indirect 31 | github.com/mdlayher/socket v0.5.0 // indirect 32 | github.com/pierrec/lz4/v4 v4.1.14 // indirect 33 | github.com/spf13/pflag v1.0.5 // indirect 34 | github.com/u-root/gobusybox/src v0.0.0-20231224233253-2944a440b6b6 // indirect 35 | github.com/u-root/u-root v0.11.1-0.20230807200058-f87ad7ccb594 // indirect 36 | github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 // indirect 37 | github.com/ulikunitz/xz v0.5.11 // indirect 38 | github.com/vishvananda/netlink v1.2.1-beta.2 // indirect 39 | github.com/vishvananda/netns v0.0.4 // indirect 40 | golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 // indirect 41 | golang.org/x/mod v0.20.0 // indirect 42 | golang.org/x/net v0.33.0 // indirect 43 | golang.org/x/sync v0.10.0 // indirect 44 | golang.org/x/tools v0.24.0 // indirect 45 | src.elv.sh v0.16.0-rc1.0.20220116211855-fda62502ad7f // indirect 46 | ) 47 | -------------------------------------------------------------------------------- /pkcs7/pkcs7_test.go: -------------------------------------------------------------------------------- 1 | package pkcs7 2 | 3 | import ( 4 | "crypto" 5 | "log" 6 | "os" 7 | "testing" 8 | 9 | "github.com/foxboron/go-uefi/asntest" 10 | "golang.org/x/crypto/cryptobyte" 11 | ) 12 | 13 | func TestVerifySignature(t *testing.T) { 14 | cert, key = InitCert() 15 | b, err := SignPKCS7(key, cert, OIDData, []byte{0x00, 0x01}) 16 | if err != nil { 17 | t.Fatalf("message") 18 | } 19 | pkcs, err := ParsePKCS7(b) 20 | if err != nil { 21 | t.Fatalf("failed parsing PKCS7 signature: %v", err) 22 | } 23 | 24 | ok, err := pkcs.Verify(cert) 25 | if err != nil { 26 | t.Fatalf("failed verifying signature: %v", err) 27 | } 28 | 29 | if !ok { 30 | t.Fatalf("Signature should validate") 31 | } 32 | 33 | } 34 | 35 | // Try to parse a signature created by sbvarsign 36 | func TestParseSbvarignSignature(t *testing.T) { 37 | b, err := os.ReadFile("testdata/test.signed") 38 | if err != nil { 39 | t.Fatal(err) 40 | } 41 | 42 | _, err = ParsePKCS7(b) 43 | if err != nil { 44 | t.Fatalf("failed to parse pkcs7: %v", err) 45 | } 46 | } 47 | 48 | // This test compares the library ASN.1 output to the old implementation 49 | // This is mostly for debugging the implementation. 50 | func TestCompareOldImplementation(t *testing.T) { 51 | if !testing.Verbose() { 52 | return 53 | } 54 | cert, key := asntest.InitCert() 55 | 56 | b, err := os.ReadFile("testdata/old_pkcs7_implementation.der") 57 | if err != nil { 58 | t.Fatal(err) 59 | } 60 | 61 | img := []byte{0x00, 0x01} 62 | h := crypto.SHA256.New() 63 | h.Write(img) 64 | bb, err := SignPKCS7(key, cert, OIDData, h.Sum(nil)) 65 | if err != nil { 66 | t.Fatalf("failed signing digest") 67 | } 68 | 69 | cs := cryptobyte.String(bb) 70 | _, bytes, err := ParseContentInfo(&cs) 71 | if err != nil { 72 | log.Fatal(err) 73 | } 74 | 75 | // We should see a couple of differences, but largely the same structure should be present 76 | asntest.Asn1Compare(t, b, bytes) 77 | } 78 | -------------------------------------------------------------------------------- /pkcs7/testdata/old_pkcs7_implementation.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/pkcs7/testdata/old_pkcs7_implementation.der -------------------------------------------------------------------------------- /pkcs7/testdata/test.signed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/pkcs7/testdata/test.signed -------------------------------------------------------------------------------- /pkcs7/utils_test.go: -------------------------------------------------------------------------------- 1 | package pkcs7 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/rsa" 6 | "crypto/x509" 7 | "crypto/x509/pkix" 8 | "log" 9 | "math/big" 10 | ) 11 | 12 | var ( 13 | cert *x509.Certificate 14 | key *rsa.PrivateKey 15 | ) 16 | 17 | func InitCert() (*x509.Certificate, *rsa.PrivateKey) { 18 | serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128)) 19 | if err != nil { 20 | log.Fatalf("Failed to generate serial number: %v", err) 21 | } 22 | c := x509.Certificate{ 23 | SerialNumber: serialNumber, 24 | PublicKeyAlgorithm: x509.RSA, 25 | SignatureAlgorithm: x509.SHA256WithRSA, 26 | Subject: pkix.Name{ 27 | Country: []string{"TEST STRING"}, 28 | }, 29 | } 30 | 31 | priv, err := rsa.GenerateKey(rand.Reader, 4096) 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | 36 | derBytes, _ := x509.CreateCertificate(rand.Reader, &c, &c, &priv.PublicKey, priv) 37 | cert, _ := x509.ParseCertificate(derBytes) 38 | return cert, priv 39 | } 40 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | Integration tests 2 | ================= 3 | Follow https://github.com/anatol/vmtest/blob/master/docs/prepare_image.md 4 | Expects `/usr/share/edk2-ovmf/x64/OVMF_CODE.secboot.fd` 5 | -------------------------------------------------------------------------------- /tests/binaries/HelloWorld.efi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/binaries/HelloWorld.efi -------------------------------------------------------------------------------- /tests/bzImage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/bzImage -------------------------------------------------------------------------------- /tests/data/binary/HelloWorld.efi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/binary/HelloWorld.efi -------------------------------------------------------------------------------- /tests/data/binary/HelloWorld.efi.signed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/binary/HelloWorld.efi.signed -------------------------------------------------------------------------------- /tests/data/binary/linuxx64.efi.stub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/binary/linuxx64.efi.stub -------------------------------------------------------------------------------- /tests/data/binary/test.pecoff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/binary/test.pecoff -------------------------------------------------------------------------------- /tests/data/boot/Boot0000-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot0000-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/boot/Boot0001-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot0001-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/boot/Boot0010-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot0010-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/boot/Boot0011-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot0011-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/boot/Boot0012-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot0012-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/boot/Boot0013-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot0013-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/boot/Boot0014-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot0014-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/boot/Boot0015-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot0015-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/boot/Boot0016-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot0016-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/boot/Boot0017-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot0017-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/boot/Boot0018-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot0018-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/boot/Boot0019-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot0019-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/boot/Boot001A-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot001A-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/boot/Boot001B-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot001B-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/boot/Boot001C-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot001C-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/boot/Boot001D-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot001D-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/boot/Boot001E-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot001E-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/boot/Boot001F-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot001F-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/boot/Boot0020-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot0020-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/boot/Boot0021-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot0021-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/boot/Boot0022-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot0022-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/boot/Boot0023-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/boot/Boot0023-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/bootorder/BootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- 1 | # -------------------------------------------------------------------------------- /tests/data/bootorder/BootOrderDefault-0b7646a4-6b44-4332-8588-c8998117f2ef: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/data/misc/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/data/misc/SetupMode-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- 1 | 00000000: 0600 0000 00 ..... 2 | -------------------------------------------------------------------------------- /tests/data/signatures/secureboot/GUID: -------------------------------------------------------------------------------- 1 | 4bf4f3ee-2975-4930-839a-86a6b34ba576 -------------------------------------------------------------------------------- /tests/data/signatures/secureboot/keys/KEK/KEK.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/signatures/secureboot/keys/KEK/KEK.der -------------------------------------------------------------------------------- /tests/data/signatures/secureboot/keys/KEK/KEK.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | 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 51 | bU8/gBXV/EBxRa2j4Y1kMXrwstGLY4g= 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /tests/data/signatures/secureboot/keys/KEK/KEK.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIExjCCAq6gAwIBAgIQNShjMtQ0gnHqfJs9n7QhoTANBgkqhkiG9w0BAQsFADAb 3 | MRkwFwYDVQQGExBLZXkgRXhjaGFuZ2UgS2V5MCIYDzAwMDEwMTAxMDAwMDAwWhgP 4 | MDAwMTAxMDEwMDAwMDBaMBsxGTAXBgNVBAYTEEtleSBFeGNoYW5nZSBLZXkwggIi 5 | MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDP5kT2Dy2+rn0BxuGgJhlHOQwm 6 | dqXI+2KR6eI/dGsKzQ+4BpppHOTKhGa2E+IXSXB3WuiHipasa1GYN21HKBQEm8A5 7 | N+gU2W0yfHYPkjStW7ATpDyG6fD9VvPjbNRS/zbXYITs5rg0XW2yPi/uct7ZTHbt 8 | K4NMUNYbgkRC50d0gVlOOqocyYasn9p4ih0IJ0sWo3sHuEe1GgI+27YKK272K/7W 9 | QoEFtfn0YijDJEkOB0wa28ycc+eX09g9xgp+RDUhSxAylV2RbiqsphW8qMW/2WF8 10 | DHD036WcD/qni+3g5oSZuSxSLG/eIRP6grMuz4rKKRPkeJKhI5Nd/qSdhI2jCRLs 11 | SyWeYRIgoUdS7xeofWjLBQe6hEnv2NPU4bBcPek0dLMEs9zLn+1vDKYuNI+MYvMi 12 | zmpxZ2Z1gNEuZR0cY8FimOvNfXpN66frw3VtJY85++AICe1OYbk5lRO5Tk4m4y7J 13 | yl5886oSY029M3o78HZchkv0wHdL4XKIw6jMwQZ0/Be0HqPCcoUJKY2BbOoQU5Jh 14 | R61+PI4n0aLSQTcruQkmbFAjotDAAGXsf7aohQd8hq9mxGyubVfcZ4DbRAoCKL5V 15 | 8DhR0H/BRlqLBSEn/Aasxr10R/Es80ODRlrnsHsfDAu5TWJ7MVKKHPvoZFKsJy8S 16 | Y01YPb3zLvldeTHE8QIDAQABowIwADANBgkqhkiG9w0BAQsFAAOCAgEAATQTm13f 17 | 1SwRB03qYtG8EEQI09yMNhnYVusPhRkG55bVbQFuK9i14V1dMyiBENMbOvlXY/Gr 18 | JDdCnfNfOTlslV5E4efoj+YRHd1ahVOxA3/hRMu6vF8ACA3NlElC87E55CQqljwp 19 | uVk1/DZ8tu3QQ0H3cBS58IJuDwpEBc4F6BmykQFHC4rpKwBQHTco/NIKFTC/M3W2 20 | JI6TCRwc/Vxi2ORzOVbCdEET3lC42KE4DErowi6HJJR0kMdtCDLCWQWKMvfQyIiS 21 | bxdeVZxVzuPtpNs3MncexPQo5HtM3vK7SdjLOUAkt2JWcD0qa9JlwgbVrqr5llXa 22 | xlLu/Xv1cZEwQfUhORzRpHj7+9vdehFULBtYCWzvf2tWY/bN8j214PkKeDBn6cj2 23 | AWPW/tPsAhGHWupl319EayN8ofSsJmd3f9RMkAABkFEG7wUeSE4woL7JyK9s+x8a 24 | PE5Tnh/hGpwkmctCiw+yS3NrHKe4zBs5Gujp1TRbIEpS+CYnHXTceANbsArQZTio 25 | NV2MV2ucvz7sFmso9mFQI+2mrYirt53Ha10LdVFcL0HxqqeYByLDQigyR8isu71/ 26 | lvYHSJVZrUrMyLiFvYk7/eRzY/Pc2mNUky3WjYY0DZZUvJGT73VXj68l3Ks4EnH2 27 | fv86BqIFniq9SILQveG8SBTAHBoOWE+WkjE= 28 | -----END CERTIFICATE----- 29 | -------------------------------------------------------------------------------- /tests/data/signatures/secureboot/keys/PK/PK.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/signatures/secureboot/keys/PK/PK.der -------------------------------------------------------------------------------- /tests/data/signatures/secureboot/keys/PK/PK.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | 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 51 | H1rNw0S4J0ntTYLz/uzACnLQ4bO1PQ== 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /tests/data/signatures/secureboot/keys/PK/PK.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEvzCCAqegAwIBAgIRAMDouYZOOfc9Z8SXK9X57M4wDQYJKoZIhvcNAQELBQAw 3 | FzEVMBMGA1UEBhMMUGxhdGZvcm0gS2V5MCIYDzAwMDEwMTAxMDAwMDAwWhgPMDAw 4 | MTAxMDEwMDAwMDBaMBcxFTATBgNVBAYTDFBsYXRmb3JtIEtleTCCAiIwDQYJKoZI 5 | hvcNAQEBBQADggIPADCCAgoCggIBAN0Zu8kcOJZG3T9mNtwWq9TVvbe0bvjq93v1 6 | 173L0ylMa+Uv7EnU+Jc548BXPCedAW+nv6AIEmbpmjVP3x7o6qlEGpeRxqGvXSha 7 | RCCHNXyXXOh7X4+QbR4KffvjXfgaNIn+VmhIHVvlh/h4+ZmKdm9NJFJo6DBT/OYs 8 | MukVMhkgElEaFM5+ZWC4P4PV9u1Kf9GTlLO7wMKjW89mJz3O47g89TolOsUNjaeh 9 | XttvDbLH/3MlDmtm0vPn/VoJ1WadvKHcOI5sR49mErWIL95xES6bWsn2PRVX7uRs 10 | iKUYgdz2SRs8df+K6kMBlYEnu9hk2EZ8IdrVyPhcaVLgHvdW27g7+2d8xiZ0tmAW 11 | Ox2lYhlKs+ndR1ZAxHzF0IMgg2zuhfUZJsTC3HhORnevJh32tC5Up8dMI5f+QSI5 12 | NQ8rEir8V0K5lrMtHBe1l7kSZ4J940sLmgHCSh7SGFzTrW/tPoJs3e5W3pvvLM3T 13 | G4EoXoN7eYT6vYA29mRdioCc3HpRIBJv93XpMy1V/APsDePsQUzfgOGG4XNKZxYx 14 | i0MlDgZ/u9Ky3wphy1ShXMao/YjrjvF0cSvtQTvYW/eC0s3ABTj58JV7Cf/CwvER 15 | wMbjidQ2BdTEOzXEfHK6tve/qotcgYRgQLt8bScBkEKmeeYJrmEOJm389AhjbaIw 16 | iuny0MSRAgMBAAGjAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQAl9pocdvBbyG1k2Dp1 17 | ynopb/dQDgb8jjpm1qnpHwVcTWepJfd+UbKbQKmAFZxu95uNPZvc8+j3hRWWJzXM 18 | AGOd9oDNlIMGotRvF1PxGzocUKDFKX5fOUhis+Va9O9IEceZ1NW2tGTN6n4A1F1N 19 | 7XibygZhR6AqByAFfxvNLWoEkyfYK2KJ8ACxUF5Rp7iKAHqZ5sxbJ2ZHG9+yAYBy 20 | RPwo171Tg2ZxDwJeeH5ukVD71zQjya1LQmbh2USImKwJNmW6LXM33PZYza7kIfHA 21 | BQ92HLw/IOTH1ZJ68KFX3s1FDBZVBrscjrRBbWiBuM4yNBlJr/l3/s90uwxmLico 22 | dBtENJYDBDboJz5I+29M8A+xzsgstqPGh2SRBuN9/CFDYwTJ/Igr++KmxqACQw/m 23 | 4KoclaZ1nC8Q4jHwQGzuDgyGFBPij0PmHLJsRCAxdD+djtl9Jo5h2kDj0cUUgdMc 24 | W6WbSDffD69Fn9MWzFyVhRRDmTskMI40WtsfE3A8HdWHx7b3QyqIPRTmL7dcc/Mv 25 | mlBtV3+/jCUUXfuirMxqKzkuq3xW/pefo/nmR231zHLxnqOB1tz2UUX97a3mWRib 26 | aKsNF8fTozrUiPkSXVvS7EXShXc5hS/ZmA1+Us8e8Q+piJxa6H2WHeEmv/0km58J 27 | ARKQFE45mVKURXpS0eKGfwibsg== 28 | -----END CERTIFICATE----- 29 | -------------------------------------------------------------------------------- /tests/data/signatures/secureboot/keys/db/db.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/signatures/secureboot/keys/db/db.der -------------------------------------------------------------------------------- /tests/data/signatures/secureboot/keys/db/db.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | 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 51 | WrEB05arCxJhXH1qhZRaLWSckbwV1Q== 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /tests/data/signatures/secureboot/keys/db/db.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEvjCCAqagAwIBAgIQaVDP9r5CsuviM5FGEGcjkzANBgkqhkiG9w0BAQsFADAX 3 | MRUwEwYDVQQGEwxEYXRhYmFzZSBLZXkwIhgPMDAwMTAxMDEwMDAwMDBaGA8wMDAx 4 | MDEwMTAwMDAwMFowFzEVMBMGA1UEBhMMRGF0YWJhc2UgS2V5MIICIjANBgkqhkiG 5 | 9w0BAQEFAAOCAg8AMIICCgKCAgEAyv426XXrV2wXpbKqA4uX4Y0Howv41D1jpUOS 6 | x0Ypr/+CGDapFyVhS7KE5ZytHP/wSth5mHNiJ+mORX6MfQZ+stlRCvFmUbU/4hFt 7 | GvTPwCcBRaOnzPjdxB0j1DR+fUVW6Fv3NgesC2lwFPjBkqmqWdCBtWHFDm65rqEg 8 | vH+dCEL8PfkpEe8DSuRYLAieAfL4cTPfGQ5tyE8S1p1+VbCKyCyAfpaC6bf1fJV9 9 | 5arlUCz0iBYOHtRdZ4JKuGbHbgi0lP5zuPxGgb576rCckTW7RRP+5yIuAmDkBxuB 10 | Jb+hwIyT5jumpxlDlrVJ5pnY2TmIdRhlosQ5xVOfk4Ye2bmf8vv0BPVfFhQgKC3b 11 | 3Ws5OgKZu0pTYgyGL6W2VD4G3GzDWzfXG9zC9SlB4WwTD0pkwSXCMUWHlCy26mNE 12 | qgU3NR314n3ERfQ0kAx6yS2CqOgAsnPubc5RnEacYpqBvhMxmoWpaxeH/c29ukOp 13 | Y6eJ/ON3Kj1yNDyW2KOf1lqk/+6bpHczPPrjAR3rZNcoJQPcdkv1nPanquWCSWo3 14 | J00Th7h7jnXlKK977GXYCWLtqFJUikDwgYlLJPTGI5/6yI/+BYAlqyXteAil+U0h 15 | 1g1UXm9cfJb82ebQ0aH2YA/XDpx/UzvjHo30YJmLxciAgH3Lsmsmkwy1XAGb1JoE 16 | V/YqZTMCAwEAAaMCMAAwDQYJKoZIhvcNAQELBQADggIBABU15VvBSVYxcZkmxFpI 17 | OzTZ08uSME1VP9T1t8S2XR79KJkMpdOTKBnUuNyq5vYISl6krdgrKRAAbjrx5icP 18 | ChKkWlbDm4NJQWDnTOw3ubSx9kcXLCL+K5aRfAKtP/nE1HgDssob4RkjWKCJkFie 19 | nftPBSvVA8LU06Q/O8XIAliQSNgkNLieKL4iRq7VDaEyymOb29wZUqbnQ4zU/+ny 20 | fDETbE97nd6KUMrHW4HbF6OHqyWL05t6cYeQAasFOZahKlKgv4HQw7Tc5AyF7wh/ 21 | HNv0rGTdTm5yhuZ6sSQKJiZ3P1T+cQ5DX6oRU2ijEogTyVCtDncBIKDJSO3kgOoH 22 | 4xVWxWGKhU7+EelvX/uQ8LEMkL/gY7rqWTsFTckK/LRAifOErlO0kYnrqwdwnnOH 23 | pVlvItyjJDAWeo7E2S6Q24hXKPAeeGQjLLG7aESoYCKgrMCsVka481hdAxydpbPj 24 | 2GRU6KBW/x4zkw2i+kQeEiynCLNh7XgDx6Lni6X0JexPAQLvv6vBevgRELunVrCy 25 | XBaubTFPhK2k/nXfp68kLKFTI0/hp6lhTbnLWIvkx+RiT1Fg4JvokE9wgEXN+565 26 | hyC05ktGAwCjAhqkM3yVyF5XIiaYkc/OZ2CTM1fwDYSMKKoTKHPpcfgp1LoUDleX 27 | 74DyzFwfEvkJCoZa7Y647Jd2 28 | -----END CERTIFICATE----- 29 | -------------------------------------------------------------------------------- /tests/data/signatures/siglist/KEK.der.esl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/signatures/siglist/KEK.der.esl -------------------------------------------------------------------------------- /tests/data/signatures/siglist/PK.der.esl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/signatures/siglist/PK.der.esl -------------------------------------------------------------------------------- /tests/data/signatures/siglist/db.der.esl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/signatures/siglist/db.der.esl -------------------------------------------------------------------------------- /tests/data/signatures/siglistchecksum/sha256.bin.siglist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/signatures/siglistchecksum/sha256.bin.siglist -------------------------------------------------------------------------------- /tests/data/signatures/sigsupport/SignatureSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/signatures/sigsupport/SignatureSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/signatures/sigsupport/SignatureSupport2-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/signatures/sigsupport/SignatureSupport2-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/data/signatures/varsign/KEK.auth: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/signatures/varsign/KEK.auth -------------------------------------------------------------------------------- /tests/data/signatures/varsign/PK.auth: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/signatures/varsign/PK.auth -------------------------------------------------------------------------------- /tests/data/signatures/varsign/db.auth: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/data/signatures/varsign/db.auth -------------------------------------------------------------------------------- /tests/integration_test.go: -------------------------------------------------------------------------------- 1 | package itest 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "log" 9 | "os" 10 | "path" 11 | "path/filepath" 12 | "testing" 13 | "time" 14 | 15 | "github.com/Netflix/go-expect" 16 | "github.com/foxboron/go-uefi/authenticode" 17 | "github.com/foxboron/go-uefi/efi/util" 18 | "github.com/hugelgupf/vmtest" 19 | "github.com/hugelgupf/vmtest/qemu" 20 | ) 21 | 22 | func CopyFile(src, dst string) bool { 23 | source, err := os.Open(src) 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | defer source.Close() 28 | 29 | f, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | defer f.Close() 34 | io.Copy(f, source) 35 | si, err := os.Stat(src) 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | err = os.Chmod(dst, si.Mode()) 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | return true 44 | } 45 | 46 | type VMTest struct { 47 | ovmf string 48 | secboot string 49 | } 50 | 51 | func (vm *VMTest) RunTests(packages ...string) func(t *testing.T) { 52 | return func(t *testing.T) { 53 | vmtest.RunGoTestsInVM(t, packages, 54 | vmtest.WithVMOpt( 55 | vmtest.WithSharedDir("ovmf/keys"), 56 | vmtest.WithQEMUFn( 57 | qemu.WithVMTimeout(time.Minute), 58 | qemu.WithQEMUCommand("qemu-system-x86_64 -enable-kvm"), 59 | qemu.WithKernel("bzImage"), 60 | qemu.ArbitraryArgs( 61 | "-m", "1G", "-machine", "type=q35,smm=on", 62 | "-drive", fmt.Sprintf("if=pflash,format=raw,unit=0,file=%s,readonly=on", vm.secboot), 63 | "-drive", fmt.Sprintf("if=pflash,format=raw,unit=1,file=%s", vm.ovmf), 64 | ), 65 | )), 66 | ) 67 | } 68 | } 69 | 70 | var ( 71 | errOK = errors.New("OK") 72 | errFAIL = errors.New("FAIL") 73 | startupnsh = ` 74 | @echo -off 75 | echo Starting UEFI application... 76 | fs0: 77 | HelloWorld.efi.signed 78 | ` 79 | ) 80 | 81 | // Signs HelloWorld.efi and attempts to run it within tianocore 82 | func (v *VMTest) RunKernelTests(packages ...string) func(t *testing.T) { 83 | return func(t *testing.T) { 84 | dir := t.TempDir() 85 | 86 | // Get some signing keys 87 | dbPem, _ := os.ReadFile("ovmf/keys/db/db.pem") 88 | dbKey, _ := os.ReadFile("ovmf/keys/db/db.key") 89 | key, _ := util.ReadKey(dbKey) 90 | cert, _ := util.ReadCert(dbPem) 91 | 92 | // Sign HelloWorld.efi binary 93 | peFile, _ := os.Open("binaries/HelloWorld.efi") 94 | file, err := authenticode.Parse(peFile) 95 | if err != nil { 96 | t.Fatalf("failed authenticode.Parse: %v", err) 97 | } 98 | _, err = file.Sign(key, cert) 99 | if err != nil { 100 | t.Fatalf("failed PECOFFBinary.Sign: %v", err) 101 | } 102 | 103 | os.WriteFile(filepath.Join(dir, "HelloWorld.efi.signed"), file.Bytes(), 0o755) 104 | os.WriteFile(filepath.Join(dir, "startup.nsh"), []byte(startupnsh), 0o755) 105 | 106 | vm := vmtest.StartVM(t, 107 | vmtest.WithArch(qemu.ArchAMD64), 108 | vmtest.WithQEMUFn( 109 | qemu.WithQEMUCommand("qemu-system-x86_64"), 110 | qemu.WithVMTimeout(time.Minute), 111 | qemu.WithKernel(""), 112 | qemu.ReadOnlyDirectory(dir), 113 | qemu.ArbitraryArgs( 114 | // Disabled iPXE boot 115 | "-net", "none", 116 | "-machine", "type=q35,smm=on", 117 | "-drive", fmt.Sprintf("if=pflash,format=raw,unit=0,file=%s,readonly", v.secboot), 118 | "-drive", fmt.Sprintf("if=pflash,format=raw,unit=1,file=%s", v.ovmf)), 119 | func() qemu.Fn { 120 | return func(alloc *qemu.IDAllocator, opts *qemu.Options) error { 121 | opts.KernelArgs = "" 122 | return nil 123 | } 124 | }(), 125 | ), 126 | ) 127 | 128 | go vm.Wait() 129 | 130 | _, err = vm.Console.Expect( 131 | expect.String("Access Denied").Then(func(buf *bytes.Buffer) error { 132 | return errFAIL 133 | }), 134 | expect.String("HelloWorld").Then(func(buf *bytes.Buffer) error { 135 | return errOK 136 | }), 137 | ) 138 | 139 | if errors.Is(err, errFAIL) { 140 | t.Fatalf("failed signature validation") 141 | } 142 | } 143 | } 144 | 145 | // Sets up the test by making a copy of the OVMF files from the system 146 | func WithVM(t *testing.T, fn func(*VMTest)) { 147 | t.Helper() 148 | dir := t.TempDir() 149 | vm := VMTest{ 150 | ovmf: path.Join(dir, "OVMF_VARS.fd"), 151 | secboot: path.Join(dir, "OVMF_CODE.secboot.fd"), 152 | } 153 | CopyFile("/usr/share/edk2-ovmf/x64/OVMF_VARS.fd", vm.ovmf) 154 | CopyFile("/usr/share/edk2-ovmf/x64/OVMF_CODE.secboot.fd", vm.secboot) 155 | fn(&vm) 156 | } 157 | 158 | func TestSecureBoot(t *testing.T) { 159 | os.Setenv("VMTEST_QEMU", "qemu-system-x86_64") 160 | 161 | WithVM(t, func(vm *VMTest) { 162 | t.Run("Enroll keys", vm.RunTests("github.com/foxboron/go-uefi/tests/tests/enroll_keys")) 163 | t.Run("Secure Boot Enabled", vm.RunTests("github.com/foxboron/go-uefi/tests/tests/secure_boot_enabled")) 164 | t.Run("Boot signed kernel", vm.RunKernelTests()) 165 | t.Run("Modify dbx variable", vm.RunTests("github.com/foxboron/go-uefi/tests/tests/modify_dbx")) 166 | t.Run("Replace PK and rotate keys", vm.RunTests("github.com/foxboron/go-uefi/tests/tests/remove_pk")) 167 | }) 168 | } 169 | -------------------------------------------------------------------------------- /tests/ovmf/OVMF_VARS.fd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/ovmf/OVMF_VARS.fd -------------------------------------------------------------------------------- /tests/ovmf/keys/KEK/KEK.auth: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/ovmf/keys/KEK/KEK.auth -------------------------------------------------------------------------------- /tests/ovmf/keys/KEK/KEK.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/ovmf/keys/KEK/KEK.der -------------------------------------------------------------------------------- /tests/ovmf/keys/KEK/KEK.der.esl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/ovmf/keys/KEK/KEK.der.esl -------------------------------------------------------------------------------- /tests/ovmf/keys/KEK/KEK.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | 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 51 | toIeDSm6JDMFzj0kbzrGsSgFWDW/ 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /tests/ovmf/keys/KEK/KEK.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIExzCCAq+gAwIBAgIRAN0+r2apwvdmm97wInecsaQwDQYJKoZIhvcNAQELBQAw 3 | GzEZMBcGA1UEBhMQS2V5IEV4Y2hhbmdlIEtleTAiGA8wMDAxMDEwMTAwMDAwMFoY 4 | DzAwMDEwMTAxMDAwMDAwWjAbMRkwFwYDVQQGExBLZXkgRXhjaGFuZ2UgS2V5MIIC 5 | IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlekYj5cj0tvJEqBY62oGSoVo 6 | yrY1wShrsRCOM2tm5Ksrjszo47Yr4k8AprtYy6ri0MBgMBhXFDkwLDZjQtGw6AP7 7 | tK0JimRTOXreL/Pma1d5zBLJkQoXxDZWn0E+oyl2+1BJcxkUW48Kdz5Ds5k3bdP1 8 | XtyaAzwqcd3lT2BnLPy5wYA+lzhBJg71d1W9VsxJiMWl/Melq0dfdHt4wxO0im0T 9 | 2SfGaS4wohXnRtdlzOR7fjjpW8fFG2usb2dSNTtgvz16KSdRZDrECU7Yu8RQWI6W 10 | Gs4kJBhEZm8WBINxw7/9QWYAwycZQt/b3ClUT54s9oDX96Q64hKlTu10FIAkqP5I 11 | 5Q8AuDMv0gdElTBIvAs6GGX2cXJXnH7XTL+wYMAKuNG0Y+nZ8PWU6fGt4Gl9DAXr 12 | cQkHpJFQYpWXhNpv7Cx5MKe0QZrMONg1seOkFw/uMQGuotGmHyvtJoyx44Uixcjy 13 | akm9oMfcqco2HQR9lRldGrWrwjSwm0W7bGJDqHOW/EmGjRfD2kuEwAR5kXKlOOiy 14 | O8I7MJyRc78LNdyJE3Tpi+SNodU5IQ00x/5CTRrajnfeI5/2cZTgAjF4SOsXcZBn 15 | AAdkMaY6gwxtOoOCK5C3JZwfU6f/URY4nSAE3IZZMSd07SedtSxLxyNCrI4GU3Ss 16 | l5lmRNZcnUngbUnmYc8CAwEAAaMCMAAwDQYJKoZIhvcNAQELBQADggIBABX2H/n/ 17 | S8AdPC+S4uo7CUNvYdussOD7Hmf7xgjo3WGok3IU7bsvLcFB6eCMXmsrswukosRS 18 | SxGPUN0V8MIwG097VWt0JwEsMOv8uY65JAkxpL+Wr+3nhZyKYRP1lF77xVZ2f1uS 19 | 9H9Av39PkySQ6+OKi4NHsXJs4ei87X+WvGW9FspgdPjq9HajrKLyp4MXhjgU+xGN 20 | jUOipTJH/A+bnDp9Idssz2N1rV4mh0QT17lEwKFN42uXsUffd6/WR2CtjFNN6aj/ 21 | T95NxK0Lp611o5IfaD7uOoFVTzucYeniIwsXfu89YWJp9dPJtGH4i9gSL7kS4lTw 22 | SvA4PF5smrN5uadufPbr2hIWxkkibue0C+3/0lFoa/q987Pb6OrVAFnI5mTaRSUZ 23 | S0j00+NiqeSYjrJdqriGhSdYZl1/rtKyWAFq4IVjMICEGNydxZg30JgsyHlxLSRi 24 | WD2dO4PQG0DPmEO6Sjd3Ub2wXn0CyuZZC4fUDGRxgf6tAYiS1rpeXjJ+PGXHNrHb 25 | clULPBwu1oE1fypFCrHPfVHyHKCqMTNcAOHH3SC/xzajPQdEcpQS0ipi52saW/ec 26 | HCj19yeo4Clv9jnv3jk9k32WS6y2fx7p9MOb5ZeHPAXDM0UhlHKVAkzDsgRf9ir6 27 | H8pu90xR+iJJxJYf7KfimvNzuv0EdWfqAQ8F 28 | -----END CERTIFICATE----- 29 | -------------------------------------------------------------------------------- /tests/ovmf/keys/PK/PK.auth: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/ovmf/keys/PK/PK.auth -------------------------------------------------------------------------------- /tests/ovmf/keys/PK/PK.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/ovmf/keys/PK/PK.der -------------------------------------------------------------------------------- /tests/ovmf/keys/PK/PK.der.esl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/ovmf/keys/PK/PK.der.esl -------------------------------------------------------------------------------- /tests/ovmf/keys/PK/PK.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | 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 51 | JPbQnfB/5rpgyxUXUhlBWpQb/R+CuUM= 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /tests/ovmf/keys/PK/PK.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEvzCCAqegAwIBAgIRAINISunhbfmr4cz52wQ/0YAwDQYJKoZIhvcNAQELBQAw 3 | FzEVMBMGA1UEBhMMUGxhdGZvcm0gS2V5MCIYDzAwMDEwMTAxMDAwMDAwWhgPMDAw 4 | MTAxMDEwMDAwMDBaMBcxFTATBgNVBAYTDFBsYXRmb3JtIEtleTCCAiIwDQYJKoZI 5 | hvcNAQEBBQADggIPADCCAgoCggIBAL1IcxU3TvnafusV+xFwPVrSzrkvOGIcJ//B 6 | XFjqi66lOk+M7SMOxfK+//DGu7GlmyeKb031x2TdHdYUVvU6h+BhF3Pa4JZrJYVf 7 | IvHXeQveQv7fIFlB4GI25TXOZ40f/u4VddsAaF+ww/7UkviDpE/iS61H1ntqePlr 8 | 8wofn70nh4mU2L0cmmg4TDvFcxtjCNZXDa0FTuKjNsLa7sf/pjNihV0M+4lMmmhx 9 | v/b/Bk52Kc9fWdQIu10vsgIL0lhXv2QgEI2BaDZeSaQfNM/ozUGvO1bK1zn8jKpt 10 | F+cNA6um/9oAz0Dek/7DVqzhYwXk+Ou9J9sAOwRiTGa5WJW2inc3onBEpnycHTuB 11 | VkLUJXznzfqwJaPdWI2K6P16nzntdmVPRMBbDPpe+RoyP4tfOUIYBcfBPifb+LGV 12 | SEGAA4UqGWZB/Daix0yRMtSV8jizL7Hvh3dAMD7cEIX7iu0sU7x2HJwoaziW/UKF 13 | 5acBHnomRq/pD820PmBiqlZCqP71edeZv5M4q5QIGvVhUhy2vWNiD0XJed5Z4dWO 14 | cQC5L0PBJDcwDeU7yTkmTxyDlBGcMm5sE/4jUmUzRNGcPcxlkurQ6dIyy6v+3UhT 15 | ivIJatrazh8rn7usqsk1iUiFCZjb1wh2SiTFT8YY68BXZOkTnxI6sU9Y8JW7J7IN 16 | qtrOHD73AgMBAAGjAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQARhdgVM8ZRb8rBudZG 17 | UkEELKFtcfel5/O3Ayt5udNOOqvvidh8lAzvXiLbDevK73XY+piLyYTzNUEqT5Kx 18 | 6S4fSRgKjvf/diTq3pSsMcWouy2Nktvx03/4j1FO0nbOCPp6QwQ0HxrbqFaZtGz3 19 | fSupOKYKD992GSEvkEAd7rav4Gq/0NMoyVgp+qToQpLRNHU1GyV7rdNbJp9xHvG9 20 | znskQ3KWTjK43zseWKF+DCPo5/hmLttClAOqCKlAr0FA39N9awxNjlMyKhadQnMW 21 | fNM759zxvi29ZjgYb3fMFubND8hFIPlGLn+EmmzBm/jMrEjvRp+DFkHj5kPzy1/S 22 | UjRmfRwWpjkZf4k6nqkbLAX0JoQpiH/HecJzirkuF8akVVvDQF8DdCfg3gEArEqA 23 | EWkf+5iHpuMRWwFgarRbj5az7vZQaRn6+pzY62FEdIaqdAtovE0WoeoykBbZDuFD 24 | t6y4pE6MhZ3VaGPy9Xlk7RKptzdAVbZuFVFVfsetd2rN32WtjRDmxjk0L21sMl/N 25 | 3zsG4cT1V2nGh4+yzIh7MOWHHNCeBSEJWg9YBJibG2qJJ3R9k5nUj/eYlKhj1MGX 26 | oEJ2L2gHTNMxTUTfLziBltMoRu2UtjcKFsfxygUrXAUNNmDDNhZ6vUYM8tmvlIG6 27 | n4BzfjhCZ2nUKQyVQTf4AuUuhQ== 28 | -----END CERTIFICATE----- 29 | -------------------------------------------------------------------------------- /tests/ovmf/keys/db/db.auth: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/ovmf/keys/db/db.auth -------------------------------------------------------------------------------- /tests/ovmf/keys/db/db.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/ovmf/keys/db/db.der -------------------------------------------------------------------------------- /tests/ovmf/keys/db/db.der.esl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foxboron/go-uefi/69fb7dba244f45ba8d4bb040281995bfdddbf622/tests/ovmf/keys/db/db.der.esl -------------------------------------------------------------------------------- /tests/ovmf/keys/db/db.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | 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 51 | c85pERZGQdikEFI/CJ5pb2nfTh865w== 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /tests/ovmf/keys/db/db.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEvzCCAqegAwIBAgIRAI7B/h5WtdYgp1FIPdMe8qUwDQYJKoZIhvcNAQELBQAw 3 | FzEVMBMGA1UEBhMMRGF0YWJhc2UgS2V5MCIYDzAwMDEwMTAxMDAwMDAwWhgPMDAw 4 | MTAxMDEwMDAwMDBaMBcxFTATBgNVBAYTDERhdGFiYXNlIEtleTCCAiIwDQYJKoZI 5 | hvcNAQEBBQADggIPADCCAgoCggIBALTcUnGboQLpg/3oaiideU27MZ1HfptShtzI 6 | QVxP/Mp+xcbmWQ1+sEv3v5NNT0Qs9bukdO2n14F22Rn0PO9D154A9O7jS8cShJBV 7 | qzNHbRbqm9oreKzDiHRzTHdIhKk9BzkGq2W2yUwDYGzCpV0MM8C7GCRS8CejwW2s 8 | JmUZn9PANYHuxvfs+gpUy9AsWHKYD3QAnn8IlsFXptrdaP2TErVIyZavC8GPIbJh 9 | kw6CIZtCTSNhXwWKWhBOhkOpg+Vd0p00RtGB1D/0Kr4IFEkzVLuzlsTzc9SpsGuz 10 | NZ3J4H2U/sR11F4xJuRvhODixhLKLFHFbD79CIdAKooX4lGiXy/vCKXz8gr6O1vO 11 | m4H6iOgs7AGRiAioa/bO+jAE5OZa/fQ1SGNqUiQJZ+ZWKjG6wQPE1F997mmi43vl 12 | wharWq6JBYXr4jPJKsorqS1K8oElAiOSCF27nhjJy81mfF+AF6bazU3fvMO0iS4Q 13 | 2kLF+PVPwx+NqkJaYEumA6dUYa8YYqHSqysghIv1KhdHWSjahCIVWOuCDgn91fd0 14 | m9zItWtfblCFQLYTkP4JOWqHNjuqIk6mAWuDRtKX0nYUnBZsPK07iIVZucTvSOnv 15 | 39GA6l62X8G1DIPY0uViYZvbMUB51H0Oi//qNonUu7xR7EheTvPxtuxtOBskHw9I 16 | y4egdwXdAgMBAAGjAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQCgsBi/ToKlVxu4cliv 17 | KHsQrzOpVCUWINw8GAnUrGspnm9x8AXqhtX3ZqE19DSESA/rDIizTt6ZqvT6+E8t 18 | 7+YOPCbM3RdrHJ/cKoVssxCONaz5OLhFHwdxikAnLiCey6KqO2yjMDwbP32+Vyzu 19 | rrdJhRx3bADwmt+dWZS5OfhksN3GB1JePvccIikkBrkKjuyy5OWcyFhRSBhZJelc 20 | pMNa6FauaQ/X6QM/xWumnBS9D1s0ynqz37qCMpAryGgdbbqs/8tvJVGZ5ReUFCy9 21 | 9US2PS9E/eWGprl6xbZqVVVWpPdnzw4l+Lp12Ih26OxT6rG1cScl8H//qc5wkHMU 22 | rpg2z/5xxdx8UpS2PljpeyhJZMOo0iuY1l1shsB5f1w4MtBxhPfFugYs9HQFObFJ 23 | gmVXpR1qkz8fEy5gU75RPGan9rukvlp41EnW7U8EqCEwF/g9PgDOKypBtS2OmSfO 24 | 9lDWYH5WXezfWO+mUzpxiaTvLUFXJZ8y3T2PisSEM3cKTwYYRuC/Vm4jEs/qFUBt 25 | 5OPyaA6TmB0k1yHcg1GeIp9QhWt/WbHAzJkPLAl06x2ypNfyjcExFsvrwX0YvR74 26 | HnXX8X20YxyLxrq02/ZeYT8m1godiTCG6/PDr8DB1TXkQee5KGhUekS1k0T/bsS9 27 | jdTUvLFzwHTNOEJ/etlCJAPogg== 28 | -----END CERTIFICATE----- 29 | -------------------------------------------------------------------------------- /tests/start-qemu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm OVMF_VARS.fd || true 4 | mkdir -p ./shared || true 5 | 6 | cp /usr/share/edk2-ovmf/x64/OVMF_VARS.fd . 7 | 8 | qemu-system-x86_64 -enable-kvm -boot order=c,menu=on -m 3G -cpu host \ 9 | -machine type=q35,smm=on,accel=kvm \ 10 | -global driver=cfi.pflash01,property=secure,value=on \ 11 | -global ICH9-LPC.disable_s3=1 \ 12 | -drive if=pflash,format=raw,unit=0,file=/usr/share/edk2-ovmf/x64/OVMF_CODE.secboot.fd,readonly \ 13 | -drive if=pflash,format=raw,unit=1,file=OVMF_VARS.fd \ 14 | -drive file="$HOME/Downloads/archlinux-2020.01.01-x86_64.iso",media=cdrom,readonly=on \ 15 | -device virtio-net-pci,netdev=net0 \ 16 | -netdev user,id=net0 \ 17 | -fsdev local,id=test_dev,path=./shared,security_model=none \ 18 | -device virtio-9p-pci,fsdev=test_dev,mount_tag=shared 19 | -------------------------------------------------------------------------------- /tests/tests/enroll_keys/enroll_keys_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/foxboron/go-uefi/efi" 8 | "github.com/foxboron/go-uefi/efi/signature" 9 | "github.com/foxboron/go-uefi/efi/util" 10 | "github.com/foxboron/go-uefi/efivar" 11 | "github.com/foxboron/go-uefi/efivarfs" 12 | "github.com/hugelgupf/vmtest/guest" 13 | ) 14 | 15 | func TestSecureBootDisabled(t *testing.T) { 16 | guest.SkipIfNotInVM(t) 17 | 18 | if efi.GetSecureBoot() { 19 | t.Fatal("in secure boot mode") 20 | } 21 | 22 | if !efi.GetSetupMode() { 23 | t.Fatal("not in setup mode") 24 | } 25 | } 26 | 27 | var TestGUID = util.EFIGUID{0xa7717414, 0xc616, 0x4977, [8]uint8{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}} 28 | 29 | func Enroll(efifs *efivarfs.Efivarfs, cert, signerKey, signerPem []byte, efivar efivar.Efivar) error { 30 | c := signature.NewSignatureDatabase() 31 | c.AppendSignature(signature.CERT_X509_GUID, &signature.SignatureData{Owner: TestGUID, Data: cert}) 32 | key, _ := util.ReadKey(signerKey) 33 | pem, _ := util.ReadCert(signerPem) 34 | return efifs.WriteSignedUpdate(efivar, c, key, pem) 35 | } 36 | 37 | func TestEnrollKeys(t *testing.T) { 38 | guest.SkipIfNotInVM(t) 39 | 40 | PKKey, _ := os.ReadFile("/testdata/PK/PK.key") 41 | PKPem, _ := os.ReadFile("/testdata/PK/PK.pem") 42 | KEKKey, _ := os.ReadFile("/testdata/KEK/KEK.key") 43 | KEKPem, _ := os.ReadFile("/testdata/KEK/KEK.pem") 44 | dbPem, _ := os.ReadFile("/testdata/db/db.pem") 45 | 46 | efifs := efivarfs.NewFS(). 47 | CheckImmutable(). 48 | UnsetImmutable(). 49 | Open() 50 | 51 | if err := Enroll(efifs, dbPem, KEKKey, KEKPem, efivar.Db); err != nil { 52 | t.Fatal(err) 53 | } 54 | if err := Enroll(efifs, KEKPem, PKKey, PKPem, efivar.KEK); err != nil { 55 | t.Fatal(err) 56 | } 57 | if err := Enroll(efifs, PKPem, PKKey, PKPem, efivar.PK); err != nil { 58 | t.Fatal(err) 59 | } 60 | } 61 | 62 | func TestOutOfSetupMode(t *testing.T) { 63 | guest.SkipIfNotInVM(t) 64 | 65 | if efi.GetSetupMode() { 66 | t.Fatal("still inside setup mode") 67 | } 68 | 69 | if efi.GetSecureBoot() { 70 | t.Fatal("in secure boot mode") 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tests/tests/modify_dbx/modify_dbx_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "testing" 7 | "time" 8 | 9 | "github.com/foxboron/go-uefi/efi/signature" 10 | "github.com/foxboron/go-uefi/efi/util" 11 | "github.com/foxboron/go-uefi/efivar" 12 | "github.com/foxboron/go-uefi/efivarfs" 13 | "github.com/hugelgupf/vmtest/guest" 14 | ) 15 | 16 | var sigdata = []signature.SignatureData{ 17 | signature.SignatureData{Owner: util.EFIGUID{Data1: 0xc1095e1b, Data2: 0x8a3b, Data3: 0x4cf5, Data4: [8]uint8{0x9d, 0x4a, 0xaf, 0xc7, 0xd7, 0x5d, 0xca, 0x68}}, Data: []uint8{0x81, 0xb4, 0xd9, 0x69, 0x31, 0xbf, 0xd, 0x2, 0xfd, 0x91, 0xa6, 0x1e, 0x19, 0xd1, 0x4f, 0x1d, 0xa4, 0x52, 0xe6, 0x6d, 0xb2, 0x40, 0x8c, 0xa8, 0x60, 0x4d, 0x41, 0x1f, 0x92, 0x65, 0x9f, 0xa}}, 18 | signature.SignatureData{Owner: util.EFIGUID{Data1: 0xc1095e1b, Data2: 0x8a3b, Data3: 0x4cf5, Data4: [8]uint8{0x9d, 0x4a, 0xaf, 0xc7, 0xd7, 0x5d, 0xca, 0x68}}, Data: []uint8{0x82, 0xb4, 0xd9, 0x69, 0x31, 0xbf, 0xd, 0x2, 0xfd, 0x91, 0xa6, 0x1e, 0x19, 0xd1, 0x4f, 0x1d, 0xa4, 0x52, 0xe6, 0x6d, 0xb2, 0x40, 0x8c, 0xa8, 0x60, 0x4d, 0x41, 0x1f, 0x92, 0x65, 0x9f, 0xa}}, 19 | signature.SignatureData{Owner: util.EFIGUID{Data1: 0xc1095e1b, Data2: 0x8a3b, Data3: 0x4cf5, Data4: [8]uint8{0x9d, 0x4a, 0xaf, 0xc7, 0xd7, 0x5d, 0xca, 0x68}}, Data: []uint8{0x83, 0xb4, 0xd9, 0x69, 0x31, 0xbf, 0xd, 0x2, 0xfd, 0x91, 0xa6, 0x1e, 0x19, 0xd1, 0x4f, 0x1d, 0xa4, 0x52, 0xe6, 0x6d, 0xb2, 0x40, 0x8c, 0xa8, 0x60, 0x4d, 0x41, 0x1f, 0x92, 0x65, 0x9f, 0xa}}, 20 | } 21 | 22 | var ( 23 | key, _ = util.ReadKeyFromFile("/testdata/KEK/KEK.key") 24 | cert, _ = util.ReadCertFromFile("/testdata/KEK/KEK.pem") 25 | ) 26 | 27 | func TestRemoveRewritedbx(t *testing.T) { 28 | guest.SkipIfNotInVM(t) 29 | 30 | efifs := efivarfs.NewFS(). 31 | CheckImmutable(). 32 | UnsetImmutable(). 33 | Open() 34 | 35 | wsb := signature.NewSignatureDatabase() 36 | for _, sig := range sigdata { 37 | wsb.AppendSignature(signature.CERT_SHA256_GUID, &sig) 38 | 39 | err := efifs.WriteSignedUpdate(efivar.Dbx, wsb, key, cert) 40 | if err != nil { 41 | t.Error(err) 42 | } 43 | time.Sleep(2 * time.Second) 44 | } 45 | 46 | var sigdb signature.SignatureDatabase 47 | if err := efifs.GetVar(efivar.Dbx, &sigdb); err != nil { 48 | t.Fatalf("failed reading dbx: %v", err) 49 | } 50 | 51 | if len(sigdb) != 1 { 52 | t.Fatal("sigdb does not have one list") 53 | } 54 | if len(sigdb[0].Signatures) != 3 { 55 | t.Fatal("signature list does not have 3 signatures") 56 | } 57 | } 58 | 59 | func TestRemoveDBX(t *testing.T) { 60 | guest.SkipIfNotInVM(t) 61 | 62 | efifs := efivarfs.NewFS(). 63 | CheckImmutable(). 64 | UnsetImmutable(). 65 | Open() 66 | 67 | // Write empty signature database 68 | wsb := signature.NewSignatureDatabase() 69 | err := efifs.WriteSignedUpdate(efivar.Dbx, wsb, key, cert) 70 | if err != nil { 71 | t.Error(err) 72 | } 73 | 74 | var sigdb signature.SignatureDatabase 75 | if err := efifs.GetVar(efivar.Dbx, &sigdb); !errors.Is(err, os.ErrNotExist) { 76 | t.Fatalf("should return os.ErrNotExist, returned: %v", err) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /tests/tests/remove_pk/remove_pk_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto" 5 | "crypto/x509" 6 | "os" 7 | "testing" 8 | 9 | "github.com/foxboron/go-uefi/asntest" 10 | "github.com/foxboron/go-uefi/efi/signature" 11 | "github.com/foxboron/go-uefi/efi/util" 12 | "github.com/foxboron/go-uefi/efivar" 13 | "github.com/foxboron/go-uefi/efivarfs" 14 | "github.com/hugelgupf/vmtest/guest" 15 | ) 16 | 17 | var TestGUID = util.EFIGUID{0xa7717414, 0xc616, 0x4977, [8]uint8{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}} 18 | 19 | func Enroll(efifs *efivarfs.Efivarfs, cert *x509.Certificate, signerKey crypto.Signer, signerPem *x509.Certificate, efivar efivar.Efivar) error { 20 | c := signature.NewSignatureDatabase() 21 | c.AppendSignature(signature.CERT_X509_GUID, &signature.SignatureData{Owner: TestGUID, Data: asntest.CertToBytes(cert)}) 22 | return efifs.WriteSignedUpdate(efivar, c, signerKey, signerPem) 23 | } 24 | 25 | func TestRemovePK(t *testing.T) { 26 | guest.SkipIfNotInVM(t) 27 | 28 | PKKey, _ := os.ReadFile("/testdata/PK/PK.key") 29 | PKPem, _ := os.ReadFile("/testdata/PK/PK.pem") 30 | readKey, _ := util.ReadKey(PKKey) 31 | readCert, _ := util.ReadCert(PKPem) 32 | 33 | efifs := efivarfs.NewFS(). 34 | CheckImmutable(). 35 | UnsetImmutable(). 36 | Open() 37 | 38 | wsb := signature.NewSignatureDatabase() 39 | err := efifs.WriteSignedUpdate(efivar.PK, wsb, readKey, readCert) 40 | if err != nil { 41 | t.Error(err) 42 | } 43 | 44 | if ok, _ := efifs.GetSecureBoot(); !ok { 45 | t.Fatal("Not in secure boot mode") 46 | } 47 | 48 | if ok, _ := efifs.GetSetupMode(); !ok { 49 | t.Fatal("Not in setup mode") 50 | } 51 | if err := Enroll(efifs, readCert, readKey, readCert, efivar.PK); err != nil { 52 | t.Fatal(err) 53 | } 54 | 55 | if ok, _ := efifs.GetSetupMode(); ok { 56 | t.Fatal("Still in setup mode") 57 | } 58 | } 59 | 60 | func TestRotateKeys(t *testing.T) { 61 | guest.SkipIfNotInVM(t) 62 | 63 | PKKeyOld, _ := os.ReadFile("/testdata/PK/PK.key") 64 | PKPemOld, _ := os.ReadFile("/testdata/PK/PK.pem") 65 | 66 | // New Keys 67 | PKPem, _ := asntest.InitCert() 68 | KEKPem, _ := asntest.InitCert() 69 | dbPem, _ := asntest.InitCert() 70 | 71 | PKKeyOldK, _ := util.ReadKey(PKKeyOld) 72 | PKPemOldK, _ := util.ReadCert(PKPemOld) 73 | 74 | efifs := efivarfs.NewFS(). 75 | CheckImmutable(). 76 | UnsetImmutable(). 77 | Open() 78 | 79 | // For rotating keys we need to go from the classic enrollment 80 | // db -> KEK -> PK 81 | // To 82 | // KEK -> db -> PK 83 | // Where all the new keys are signed by the old Platform key 84 | 85 | if err := Enroll(efifs, KEKPem, PKKeyOldK, PKPemOldK, efivar.KEK); err != nil { 86 | t.Fatalf("failed KEK enroll: %v", err) 87 | } 88 | if err := Enroll(efifs, dbPem, PKKeyOldK, PKPemOldK, efivar.Db); err != nil { 89 | t.Fatalf("failed db enroll: %v", err) 90 | } 91 | if err := Enroll(efifs, PKPem, PKKeyOldK, PKPemOldK, efivar.PK); err != nil { 92 | t.Fatalf("failed PK enroll: %v", err) 93 | } 94 | if ok, _ := efifs.GetSetupMode(); ok { 95 | t.Fatal("Still in setup mode") 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /tests/tests/secure_boot_enabled/secure_boot_enabled_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/foxboron/go-uefi/efi" 7 | "github.com/hugelgupf/vmtest/guest" 8 | ) 9 | 10 | func TestSecureBootEnabled(t *testing.T) { 11 | guest.SkipIfNotInVM(t) 12 | 13 | if !efi.GetSecureBoot() { 14 | t.Fatal("not in secure boot mode") 15 | } 16 | 17 | if efi.GetSetupMode() { 18 | t.Fatal("in setup mode") 19 | } 20 | } 21 | --------------------------------------------------------------------------------