diff options
author | axtloss <axtlos@getcryst.al> | 2024-02-05 15:29:21 +0100 |
---|---|---|
committer | axtloss <axtlos@getcryst.al> | 2024-02-05 15:29:21 +0100 |
commit | f1524db2c9d935daabd3b1557caf54fbdd63dde5 (patch) | |
tree | 04dc65245855f5975ab3002e9749ba666ea1af37 | |
parent | a215d3358f35e3331b4ce6c7f5a03b4544e04d43 (diff) | |
download | fsverify-f1524db2c9d935daabd3b1557caf54fbdd63dde5.tar.gz fsverify-f1524db2c9d935daabd3b1557caf54fbdd63dde5.tar.bz2 |
verifysetup storage reader
-rw-r--r-- | core/storage.go | 31 | ||||
-rw-r--r-- | fsverify-paper.md | 58 | ||||
-rw-r--r-- | verifysetup/cmd/setup.go | 61 | ||||
-rw-r--r-- | verifysetup/core/crypt.go | 18 | ||||
-rw-r--r-- | verifysetup/core/storage.go | 75 | ||||
-rw-r--r-- | verifysetup/go.mod | 12 | ||||
-rw-r--r-- | verifysetup/go.sum | 22 | ||||
-rw-r--r-- | verifysetup/main.go | 13 |
8 files changed, 256 insertions, 34 deletions
diff --git a/core/storage.go b/core/storage.go index 6e9245a..8334271 100644 --- a/core/storage.go +++ b/core/storage.go @@ -173,37 +173,6 @@ func OpenDB(dbpath string, readonly bool) (*bolt.DB, error) { return db, nil } -func AddNode(node Node, db *bolt.DB) error { - var err error - var deferDB bool - if db == nil { - db, err = OpenDB("my.db", false) - if err != nil { - return err - } - deferDB = true - } else if db.IsReadOnly() { - return fmt.Errorf("Error: database is opened read only, unable to add nodes") - } - err = db.Update(func(tx *bolt.Tx) error { - nodes, err := tx.CreateBucketIfNotExists([]byte("Nodes")) - if err != nil { - return err - } - if buf, err := json.Marshal(node); err != nil { - return err - } else if err := nodes.Put([]byte(node.PrevNodeSum), buf); err != nil { - return err - } - return nil - - }) - if deferDB { - defer db.Close() - } - return err -} - func GetNode(checksum string, db *bolt.DB) (Node, error) { var err error var deferDB bool diff --git a/fsverify-paper.md b/fsverify-paper.md index b19ec52..052f782 100644 --- a/fsverify-paper.md +++ b/fsverify-paper.md @@ -65,7 +65,6 @@ through this, the slightest change in one of the nodes will result in a wrong ha The first Node will have `PrevNodeSum` as "Entrypoint" since the PrevNodeSum field is also used to access each node, using EntryPoint allows fsverify to start the verification by always being able to read the first node - # Verification Process The verification step consists of multiple steps: @@ -74,6 +73,63 @@ The verification step consists of multiple steps: 3. Verifying the database using the previously read keys 4. Verifying the target partition using the database +# Storing the Public key +The public key can be stored in multiple ways: +- Partition which has the key directly flashed onto it +- A text file +- TPM2 +- A device that prints the key over usb serial + +Fsverify does not verify the integrity or trustworthiness of these storage forms, so the integrity will have to be verified manually. + +## Partition which has the key directly flashed onto it +This is quite simple, all thats required is to write the key directly into a block device: +Assuming that sda1 is the block device which should be used, it can be as simple as: +````bash +echo -n "public key" > /dev/sda1 +```` +It is important to make sure that only the public key is written, no extra newlines (hence the `-n`) or whitespace surrounding the key. + +Once this is done, `config/config.go` can be modified to set the `KeyStore` value to `1` and `KeyLocation` to `/dev/sda1` + +Any form of accessing the block device directly is viable, but it is recommended to specify the uuid of the device instead of using a label or name, as that would always verify that the right block device is used and makes spoofing the device harder. + +## Text file +The simplest form, and only recommended to be used in a secureboot verified UKI or a device/partition that can always be trusted (i.e. it is always read-only) + +All that is required is to write the public key into a text file: +```bash +echo -n "public key" > /path/to/publickey +``` +It is important to make sure that only the public key is written, no extra newlines (hence the `-n`) or whitespace surrounding the key. + +Once this is done, `config/config.go` can be modified to set the `KeyStore` value to `0` and `KeyLocation` to `/path/to/publickey` + +## TPM2 +to be done + +## Device that prints the key over usb serial +This is the safest form of storing the key, if done properly. + +Any device, like a microcontroller, that has the option to write into a serial tty (`/dev/ttyACM*`) with the baud rate `9600` can be used for this. +It is recommended to make sure that the microcontroller cannot be reflashed once the key is written successfully, to ensure that the device does not get overwritten with the key of an attacker. + +An (unsafe) example using an arduino can look like this: +```C +// fsverify-serial.ino + +void setup() { + Serial.setup(9600) // set up a serial tty with the baud rate 9600 + Serial.print("\tpublic key\t") // Write the public key to the tty +} + +void loop() {} +``` + +This will print the public key once the arduino is connected to the device, make sure that the public key is always surrounded by a `\t`, as the tab sequence is used by fsverify to figure out where a key starts and where it ends. + +To ensure the safety of using this method, in addition to having the microcontroller be read only, it also has to be made sure that the microcontroller is embedded in the device in a way where it cannot be easily replaced since using an external arduino makes no checks to see if the ttyACM device being accessed is actually the legitimate device. + ## Reading the Signature and Public Key The header only contains parts of the signature, the Trusted Hash and the Untrusted Hash, using this a complete signature is constructed, this allows for easier storage of the signature as the full signature contains data that can change over time (but is not required for signing) and break the header by becoming too big. diff --git a/verifysetup/cmd/setup.go b/verifysetup/cmd/setup.go index 3897903..c2676fc 100644 --- a/verifysetup/cmd/setup.go +++ b/verifysetup/cmd/setup.go @@ -1,10 +1,16 @@ package cmd import ( + "bytes" + "fmt" + verify "github.com/axtloss/fsverify/core" + "github.com/axtloss/fsverify/verifysetup/core" "github.com/spf13/cobra" + "math" + "os" ) -func NewVerifyCommand() *cobra.Command { +func NewSetupCommand() *cobra.Command { cmd := &cobra.Command{ Use: "setup", Short: "Set up fsverify", @@ -16,5 +22,56 @@ func NewVerifyCommand() *cobra.Command { } func SetupCommand(_ *cobra.Command, args []string) error { - return nil + if len(args) != 1 { + return fmt.Errorf("Usage: verifysetup setup [partition]") + } + fmt.Println("Using partition: ", args[0]) + disk, err := os.Open(args[0]) + if err != nil { + return err + } + defer disk.Close() + fmt.Println("Reading from disk") + diskInfo, err := disk.Stat() + if err != nil { + return err + } + diskSize := diskInfo.Size() + blockCount := math.Floor(float64(diskSize / 4000)) + lastBlockSize := float64(diskSize) - blockCount*4000.0 + fmt.Println(diskSize) + fmt.Println(blockCount) + fmt.Println(lastBlockSize) + node := verify.Node{} + block := make([]byte, 4000) + diskBytes := make([]byte, diskSize) + _, err = disk.Read(diskBytes) + if err != nil { + return err + } + reader := bytes.NewReader(diskBytes) + for i := 0; i < int(blockCount); i++ { + reader = bytes.NewReader(diskBytes) + block, err = core.ReadBlock(i*4000, (i*4000)+4000, reader) + if err != nil { + return err + } + node, err = core.CreateNode(i*4000, (i*4000)+4000, block, &node) + if err != nil { + return err + } + fmt.Println(node) + err = core.AddNode(node, nil, "./fsverify.db") + } + finalBlock, err := core.ReadBlock(int(blockCount*4000), int((blockCount*4000)+lastBlockSize), reader) + if err != nil { + return err + } + finalNode, err := core.CreateNode(int(blockCount*4000), int((blockCount*4000)+lastBlockSize), finalBlock, &node) + if err != nil { + return err + } + fmt.Println(finalNode) + err = core.AddNode(finalNode, nil, "./fsverify.db") + return err } diff --git a/verifysetup/core/crypt.go b/verifysetup/core/crypt.go new file mode 100644 index 0000000..81130a3 --- /dev/null +++ b/verifysetup/core/crypt.go @@ -0,0 +1,18 @@ +package core + +import ( + "bytes" + "crypto/sha256" + "fmt" + "io" + "strings" +) + +func CalculateBlockHash(block []byte) (string, error) { + hash := sha256.New() + if _, err := io.Copy(hash, bytes.NewReader(block)); err != nil { + return "", err + } + hashInBytes := hash.Sum(nil)[:32] + return strings.TrimSpace(fmt.Sprintf("%x", hashInBytes)), nil +} diff --git a/verifysetup/core/storage.go b/verifysetup/core/storage.go new file mode 100644 index 0000000..831d637 --- /dev/null +++ b/verifysetup/core/storage.go @@ -0,0 +1,75 @@ +package core + +import ( + "bytes" + "encoding/json" + "fmt" + verify "github.com/axtloss/fsverify/core" + bolt "go.etcd.io/bbolt" +) + +var TotalReadBlocks = 0 + +func ReadBlock(start int, end int, device *bytes.Reader) ([]byte, error) { + block := make([]byte, end-start) + _, err := device.Seek(int64(start), 0) + if err != nil { + return []byte{}, err + } + _, err = device.Read(block) + TotalReadBlocks = TotalReadBlocks + (end - start) + return block, err +} + +func CreateNode(blockStart int, blockEnd int, block []byte, prevNode *verify.Node) (verify.Node, error) { + node := verify.Node{} + node.BlockStart = blockStart + node.BlockEnd = blockEnd + blockHash, err := CalculateBlockHash(block) + if err != nil { + return verify.Node{}, err + } + node.BlockSum = blockHash + var prevNodeHash string + if prevNode.PrevNodeSum != "" { + prevNodeHash, err = prevNode.GetHash() + if err != nil { + return verify.Node{}, err + } + } else { + prevNodeHash = "Entrypoint" + } + node.PrevNodeSum = prevNodeHash + return node, nil +} + +func AddNode(node verify.Node, db *bolt.DB, dbPath string) error { + var err error + var deferDB bool + if db == nil { + db, err = bolt.Open(dbPath, 0777, nil) + if err != nil { + return err + } + deferDB = true + } else if db.IsReadOnly() { + return fmt.Errorf("Error: database is opened read only, unable to add nodes") + } + err = db.Update(func(tx *bolt.Tx) error { + nodes, err := tx.CreateBucketIfNotExists([]byte("Nodes")) + if err != nil { + return err + } + if buf, err := json.Marshal(node); err != nil { + return err + } else if err := nodes.Put([]byte(node.PrevNodeSum), buf); err != nil { + return err + } + return nil + + }) + if deferDB { + defer db.Close() + } + return err +} diff --git a/verifysetup/go.mod b/verifysetup/go.mod index 25642c6..7acfa12 100644 --- a/verifysetup/go.mod +++ b/verifysetup/go.mod @@ -1,3 +1,15 @@ module github.com/axtloss/fsverify/verifysetup go 1.21.6 + +require ( + github.com/axtloss/fsverify v0.0.0-20240204141913-a215d3358f35 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 // indirect + github.com/spf13/cobra v1.8.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 // indirect + go.etcd.io/bbolt v1.3.8 // indirect + golang.org/x/crypto v0.17.0 // indirect + golang.org/x/sys v0.15.0 // indirect +) diff --git a/verifysetup/go.sum b/verifysetup/go.sum new file mode 100644 index 0000000..9ad7abe --- /dev/null +++ b/verifysetup/go.sum @@ -0,0 +1,22 @@ +github.com/axtloss/fsverify v0.0.0-20240204141913-a215d3358f35 h1:GnvD9HxKht9mD1NLjDaffqNfTJcxbOgXfkexYDGAJ5E= +github.com/axtloss/fsverify v0.0.0-20240204141913-a215d3358f35/go.mod h1:Tve3hFV8BxkGp5iY/k+sKSF/Qv1SGLDoHFYQI3xjdHQ= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 h1:TMtDYDHKYY15rFihtRfck/bfFqNfvcabqvXAFQfAUpY= +github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 h1:UyzmZLoiDWMRywV4DUYb9Fbt8uiOSooupjTq10vpvnU= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= +go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/verifysetup/main.go b/verifysetup/main.go new file mode 100644 index 0000000..2c1bd69 --- /dev/null +++ b/verifysetup/main.go @@ -0,0 +1,13 @@ +package main + +import ( + "github.com/axtloss/fsverify/verifysetup/cmd" +) + +var ( + Version = "0.1.0" +) + +func main() { + cmd.Execute() +} |