mirror of
https://github.com/hairyhenderson/go-onerng.git
synced 2025-04-04 17:50:12 -05:00
Fixing some linting errors and refactoring
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
This commit is contained in:
parent
1e19f9f3ec
commit
fc519b98e2
@ -12,8 +12,6 @@ linters-settings:
|
||||
goconst:
|
||||
min-len: 2
|
||||
min-occurrences: 4
|
||||
lll:
|
||||
line-length: 140
|
||||
nolintlint:
|
||||
allow-leading-space: true # don't require machine-readable nolint directives (i.e. with no leading space)
|
||||
allow-unused: false # report any unused nolint directives
|
||||
@ -29,37 +27,36 @@ linters:
|
||||
- depguard
|
||||
- dogsled
|
||||
- dupl
|
||||
# - errcheck
|
||||
- errcheck
|
||||
- exhaustive
|
||||
- exportloopref
|
||||
# - funlen
|
||||
# - gci
|
||||
- gci
|
||||
# - gochecknoglobals
|
||||
- gochecknoinits
|
||||
# - gocognit
|
||||
- gocognit
|
||||
- goconst
|
||||
# - gocritic
|
||||
# - gocyclo
|
||||
- gocritic
|
||||
- gocyclo
|
||||
# - godox
|
||||
- gofmt
|
||||
- gofumpt
|
||||
- goheader
|
||||
- goimports
|
||||
- golint
|
||||
# - gomnd
|
||||
- gomnd
|
||||
- gomodguard
|
||||
- goprintffuncname
|
||||
# - gosec
|
||||
- gosec
|
||||
- gosimple
|
||||
# - govet
|
||||
- govet
|
||||
- ineffassign
|
||||
# - interfacer
|
||||
# - lll
|
||||
- interfacer
|
||||
- maligned
|
||||
- misspell
|
||||
- nakedret
|
||||
# - nestif
|
||||
# - nlreturn
|
||||
- nestif
|
||||
- nlreturn
|
||||
- noctx
|
||||
- nolintlint
|
||||
- prealloc
|
||||
@ -71,7 +68,7 @@ linters:
|
||||
- stylecheck
|
||||
- typecheck
|
||||
- unconvert
|
||||
# - unparam
|
||||
- unparam
|
||||
- unused
|
||||
- varcheck
|
||||
- whitespace
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
# go-onerng
|
||||
|
||||
This is a Go port of the OneRNG tools distributed at https://onerng.info/. Much credit is due to the OneRNG creators - all I'm doing here is porting a bunch of Bash and Python code to Go.
|
||||
This is an unofficial Go version of the OneRNG tools distributed at https://onerng.info/. Much credit is due to the OneRNG creators - this all started as a port of a bunch of Bash and Python code to Go.
|
||||
|
||||
The different commands available were discovered by reading [the firmware source code](https://github.com/OneRNG/firmware/blob/master/cdc_app.c#L346).
|
||||
|
||||
## Roadmap
|
||||
|
||||
|
204
cmd/onerng/commands.go
Normal file
204
cmd/onerng/commands.go
Normal file
@ -0,0 +1,204 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/hairyhenderson/go-onerng"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func createORNG(cmd *cobra.Command) *onerng.OneRNG {
|
||||
return &onerng.OneRNG{Path: cmd.Flag("device").Value.String()}
|
||||
}
|
||||
|
||||
func idCmd(cmd *cobra.Command, args []string) error {
|
||||
o := createORNG(cmd)
|
||||
id, err := o.Identify(cmd.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("OneRNG Hardware ID: %s\n", id)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func versionCmd(cmd *cobra.Command, args []string) error {
|
||||
o := createORNG(cmd)
|
||||
version, err := o.Version(cmd.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("OneRNG Hardware Version: %d\n", version)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func flushCmd(cmd *cobra.Command, args []string) error {
|
||||
o := createORNG(cmd)
|
||||
|
||||
return o.Flush(cmd.Context())
|
||||
}
|
||||
|
||||
func initCmd(cmd *cobra.Command, args []string) error {
|
||||
o := createORNG(cmd)
|
||||
|
||||
return o.Init(cmd.Context())
|
||||
}
|
||||
|
||||
func verifyCmd(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Context()
|
||||
o := createORNG(cmd)
|
||||
err := o.Init(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("init failed before image verification: %w", err)
|
||||
}
|
||||
image, err := o.Image(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("image extraction failed before verification: %w", err)
|
||||
}
|
||||
err = onerng.Verify(ctx, bytes.NewBuffer(image), publicKey)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func imageCmd(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Context()
|
||||
o := createORNG(cmd)
|
||||
|
||||
err := o.Init(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("init failed before image extraction: %w", err)
|
||||
}
|
||||
image, err := o.Image(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out := os.Stdout
|
||||
imgOut := cmd.Flag("out").Value.String()
|
||||
if imgOut != "-" {
|
||||
out, err = os.OpenFile(imgOut, os.O_RDWR|os.O_CREATE, 0o644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
n, err := out.Write(image)
|
||||
fmt.Fprintf(os.Stderr, "Wrote %db to %s\n", n, imgOut)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func readFlags(cmd *cobra.Command) (onerng.NoiseMode, error) {
|
||||
disableAvalanche, err := cmd.Flags().GetBool("disable-avalanche")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
enableRF, err := cmd.Flags().GetBool("enable-rf")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
disableWhitener, err := cmd.Flags().GetBool("disable-whitener")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// set flags based on commandline options
|
||||
flags := onerng.Default
|
||||
if disableAvalanche {
|
||||
flags |= onerng.DisableAvalanche
|
||||
}
|
||||
if enableRF {
|
||||
flags |= onerng.EnableRF
|
||||
}
|
||||
if disableWhitener {
|
||||
flags |= onerng.DisableWhitener
|
||||
}
|
||||
|
||||
return flags, nil
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func readCmd(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Context()
|
||||
o := createORNG(cmd)
|
||||
err := o.Init(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("init failed before read: %w", err)
|
||||
}
|
||||
|
||||
enableAESWhiten, err := cmd.Flags().GetBool("aes-whitener")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count, err := cmd.Flags().GetInt64("count")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
flags, err := readFlags(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// waste some entropy...
|
||||
devNull, err := os.OpenFile("/dev/null", os.O_WRONLY, 0o200)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wasteAmount := 10240
|
||||
_, err = o.Read(ctx, devNull, int64(wasteAmount), flags)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "warning: entropy wasteage failed or incomplete, continuing anyway\n")
|
||||
}
|
||||
|
||||
out := io.WriteCloser(os.Stdout)
|
||||
readOut := cmd.Flag("out").Value.String()
|
||||
if readOut != "-" {
|
||||
out, err = os.OpenFile(readOut, os.O_RDWR|os.O_CREATE, 0o644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if enableAESWhiten {
|
||||
out, err = o.AESWhitener(ctx, out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
written, err := o.Read(ctx, out, count, flags)
|
||||
delta := time.Since(start)
|
||||
rate := float64(written) / delta.Seconds()
|
||||
fmt.Fprintf(os.Stderr, "%s written in %s (%s/s)\n", humanizeBytes(float64(written)), delta, humanizeBytes(rate))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// humanizeBytes produces a human readable representation of an IEC size.
|
||||
// Taken from github.com/dustin/go-humanize
|
||||
//nolint:gomnd
|
||||
func humanizeBytes(s float64) string {
|
||||
base := 1024.0
|
||||
sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}
|
||||
if s < 10 {
|
||||
return fmt.Sprintf("%f B", s)
|
||||
}
|
||||
|
||||
e := math.Floor(math.Log(s) / math.Log(base))
|
||||
suffix := sizes[int(e)]
|
||||
val := math.Floor(s/math.Pow(base, e)*10+0.5) / 10
|
||||
f := "%.0f %s"
|
||||
if val < 10 {
|
||||
f = "%.1f %s"
|
||||
}
|
||||
|
||||
return fmt.Sprintf(f, val, suffix)
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hairyhenderson/go-onerng"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// flushCmd represents the flush command
|
||||
func flushCmd(ctx context.Context) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "flush",
|
||||
Short: "Flush the OneRNG's entropy pool",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
o := onerng.OneRNG{Path: opts.Device}
|
||||
return o.Flush(ctx)
|
||||
},
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hairyhenderson/go-onerng"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// idCmd represents the id command
|
||||
func idCmd(ctx context.Context) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "id",
|
||||
Short: "Display the OneRNG's hardware id",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
o := onerng.OneRNG{Path: opts.Device}
|
||||
id, err := o.Identify(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("OneRNG Hardware ID: %s\n", id)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/hairyhenderson/go-onerng"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// imageCmd represents the image command
|
||||
func imageCmd(ctx context.Context) *cobra.Command {
|
||||
var imgOut string
|
||||
cmd := &cobra.Command{
|
||||
Use: "image",
|
||||
Short: "Dump the OneRNG's firmware image",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
o := onerng.OneRNG{Path: opts.Device}
|
||||
err := o.Init(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "init failed before image extraction")
|
||||
}
|
||||
image, err := o.Image(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var out *os.File
|
||||
if imgOut == "-" {
|
||||
out = os.Stdout
|
||||
} else {
|
||||
out, err = os.OpenFile(imgOut, os.O_RDWR|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
n, err := out.Write(image)
|
||||
fmt.Fprintf(os.Stderr, "Wrote %db to %s\n", n, imgOut)
|
||||
return err
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVarP(&imgOut, "out", "o", "onerng.img", "output file for image (use - for stdout)")
|
||||
return cmd
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hairyhenderson/go-onerng"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// initCmd represents the init command
|
||||
func initCmd(ctx context.Context) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "init",
|
||||
Short: "Initialize the RNG",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
o := onerng.OneRNG{Path: opts.Device}
|
||||
err := o.Init(ctx)
|
||||
return err
|
||||
},
|
||||
}
|
||||
}
|
@ -7,18 +7,89 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"github.com/hairyhenderson/go-onerng/version"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
func commands() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "onerng [opts] COMMAND",
|
||||
Short: "Tool for the OneRNG open source hardware entropy generator",
|
||||
Long: `OneRNG is an open source hardware entropy generator in a USB dongle.
|
||||
|
||||
This tool can be used to verify that the OneRNG device operates
|
||||
correctly, and that the firmware has not been tampered with.`,
|
||||
Version: version.Version,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
cmd.SilenceErrors = true
|
||||
cmd.SilenceUsage = true
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cmd.PersistentFlags().StringP("device", "d", "/dev/ttyACM0", "the OneRNG device")
|
||||
|
||||
flush := &cobra.Command{
|
||||
Use: "flush",
|
||||
Short: "Flush the OneRNG's entropy pool",
|
||||
RunE: flushCmd,
|
||||
}
|
||||
id := &cobra.Command{
|
||||
Use: "id",
|
||||
Short: "Display the OneRNG's hardware id",
|
||||
RunE: idCmd,
|
||||
}
|
||||
init := &cobra.Command{
|
||||
Use: "init",
|
||||
Short: "Initialize the RNG",
|
||||
RunE: initCmd,
|
||||
}
|
||||
verify := &cobra.Command{
|
||||
Use: "verify",
|
||||
Short: "Verify that OneRNG's firmware has not been tampered with.",
|
||||
RunE: verifyCmd,
|
||||
}
|
||||
version := &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Display the OneRNG's hardware version",
|
||||
RunE: versionCmd,
|
||||
}
|
||||
image := &cobra.Command{
|
||||
Use: "image",
|
||||
Short: "Dump the OneRNG's firmware image",
|
||||
RunE: imageCmd,
|
||||
}
|
||||
image.Flags().StringP("out", "o", "onerng.img", "output file for image (use - for stdout)")
|
||||
|
||||
read := &cobra.Command{
|
||||
Use: "read",
|
||||
Short: "read some random data from the OneRNG",
|
||||
RunE: readCmd,
|
||||
}
|
||||
read.Flags().StringP("out", "o", "-", "output file for data (use - for stdout)")
|
||||
read.Flags().Bool("disable-avalanche", false, "Disable noise generation from the Avalanche Diode")
|
||||
read.Flags().Bool("enable-rf", false, "Enable noise generation from RF")
|
||||
read.Flags().Bool("disable-whitener", false, "Disable the on-board CRC16 generator")
|
||||
read.Flags().Int64P("count", "n", -1, "Read only N bytes (use -1 for unlimited)")
|
||||
read.Flags().Bool("aes-whitener", true, "encrypt with AES-128 to 'whiten' the input stream with a random key obtained from the OneRNG")
|
||||
|
||||
cmd.AddCommand(flush, id, init, image, read, verify, version)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func main() {
|
||||
returncode := 0
|
||||
defer func() { os.Exit(returncode) }()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
defer func() {
|
||||
signal.Stop(c)
|
||||
cancel()
|
||||
}()
|
||||
defer signal.Stop(c)
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case <-c:
|
||||
@ -27,10 +98,9 @@ func main() {
|
||||
}
|
||||
}()
|
||||
|
||||
cmd := rootCmd(ctx)
|
||||
initConfig(ctx, cmd)
|
||||
if err := cmd.Execute(); err != nil {
|
||||
cmd := commands()
|
||||
if err := cmd.ExecuteContext(ctx); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
returncode = 1
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/hairyhenderson/go-onerng"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
publicKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
// extracted from onerng_verify.py bundled in
|
||||
// https://github.com/OneRNG/onerng.github.io/raw/master/sw/onerng_3.6-1_all.deb
|
||||
// SHA256: a9ccf7b04ee317dbfc91518542301e2d60ebe205d38e80563f29aac7cd845ccb
|
||||
const publicKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: GnuPG v1
|
||||
|
||||
mQINBFPXhxIBEADHeR56yhuF77hOErNk6LXTvbNIViVBG/Ss6cHJcnarnLjaGZ5y
|
||||
@ -75,24 +67,3 @@ W2KHfJBcr1Ag0zZ5q1SoyMiqFmhgo0i+D58QIjtNw7JVyOYZPw==
|
||||
=IjnI
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
`
|
||||
)
|
||||
|
||||
func verifyCmd(ctx context.Context) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "verify",
|
||||
Short: "Verify that OneRNG's firmware has not been tampered with.",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
o := onerng.OneRNG{Path: opts.Device}
|
||||
err := o.Init(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "init failed before image verification")
|
||||
}
|
||||
image, err := o.Image(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "image extraction failed before verification")
|
||||
}
|
||||
err = onerng.Verify(ctx, bytes.NewBuffer(image), publicKey)
|
||||
return err
|
||||
},
|
||||
}
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/hairyhenderson/go-onerng"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func readCmd(ctx context.Context) *cobra.Command {
|
||||
readOut := ""
|
||||
disableAvalanche := false
|
||||
enableRF := false
|
||||
disableWhitener := false
|
||||
enableAESWhiten := true
|
||||
|
||||
count := int64(-1)
|
||||
cmd := &cobra.Command{
|
||||
Use: "read",
|
||||
Short: "read some random data from the OneRNG",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
o := &onerng.OneRNG{Path: opts.Device}
|
||||
err := o.Init(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "init failed")
|
||||
}
|
||||
|
||||
// set flags based on commandline options
|
||||
flags := onerng.Default
|
||||
if disableAvalanche {
|
||||
flags |= onerng.DisableAvalanche
|
||||
}
|
||||
if enableRF {
|
||||
flags |= onerng.EnableRF
|
||||
}
|
||||
if disableWhitener {
|
||||
flags |= onerng.DisableWhitener
|
||||
}
|
||||
|
||||
// waste some entropy...
|
||||
devNull, err := os.OpenFile("/dev/null", os.O_WRONLY, 0200)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = o.Read(ctx, devNull, 10*1024, flags)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "warning: entropy wasteage failed or incomplete, continuing anyway\n")
|
||||
}
|
||||
|
||||
var out io.WriteCloser
|
||||
if readOut == "-" {
|
||||
out = os.Stdout
|
||||
} else {
|
||||
out, err = os.OpenFile(readOut, os.O_RDWR|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if enableAESWhiten {
|
||||
out, err = o.AESWhitener(ctx, out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
written, err := o.Read(ctx, out, count, flags)
|
||||
delta := time.Since(start)
|
||||
rate := float64(written) / delta.Seconds()
|
||||
fmt.Fprintf(os.Stderr, "%s written in %s (%s/s)\n", humanize.Bytes(uint64(written)), delta, humanize.Bytes(uint64(rate)))
|
||||
return err
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVarP(&readOut, "out", "o", "-", "output file for data (use - for stdout)")
|
||||
cmd.Flags().BoolVar(&disableAvalanche, "disable-avalanche", false, "Disable noise generation from the Avalanche Diode")
|
||||
cmd.Flags().BoolVar(&enableRF, "enable-rf", false, "Enable noise generation from RF")
|
||||
cmd.Flags().BoolVar(&disableWhitener, "disable-whitener", false, "Disable the on-board CRC16 generator")
|
||||
cmd.Flags().Int64VarP(&count, "count", "n", -1, "Read only N bytes (use -1 for unlimited)")
|
||||
cmd.Flags().BoolVar(&enableAESWhiten, "aes-whitener", true, "encrypt with AES-128 to 'whiten' the input stream with a random key obtained from the OneRNG")
|
||||
return cmd
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var (
|
||||
cfgFile string
|
||||
opts config
|
||||
)
|
||||
|
||||
func rootCmd(ctx context.Context) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "onerng [opts] COMMAND",
|
||||
Short: "Tool for the OneRNG open source hardware entropy generator",
|
||||
Long: `OneRNG is an open source hardware entropy generator in a USB dongle.
|
||||
|
||||
This tool can be used to verify that the OneRNG device operates
|
||||
correctly, and that the firmware has not been tampered with.`,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
cmd.SilenceErrors = true
|
||||
cmd.SilenceUsage = true
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func initConfig(ctx context.Context, cmd *cobra.Command) {
|
||||
cmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.go-onerng.yaml)")
|
||||
opts = config{}
|
||||
cmd.PersistentFlags().StringVarP(&opts.Device, "device", "d", "/dev/ttyACM0", "the OneRNG device")
|
||||
|
||||
cmd.AddCommand(
|
||||
verifyCmd(ctx),
|
||||
versionCmd(ctx),
|
||||
idCmd(ctx),
|
||||
flushCmd(ctx),
|
||||
imageCmd(ctx),
|
||||
initCmd(ctx),
|
||||
readCmd(ctx),
|
||||
)
|
||||
|
||||
if cfgFile != "" { // enable ability to specify config file via flag
|
||||
viper.SetConfigFile(cfgFile)
|
||||
}
|
||||
|
||||
viper.SetConfigName(".go-onerng") // name of config file (without extension)
|
||||
viper.AddConfigPath(os.Getenv("HOME")) // adding home directory as first search path
|
||||
viper.AutomaticEnv() // read in environment variables that match
|
||||
|
||||
// If a config file is found, read it in.
|
||||
if err := viper.ReadInConfig(); err == nil {
|
||||
fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
|
||||
}
|
||||
}
|
||||
|
||||
type config struct {
|
||||
Device string
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hairyhenderson/go-onerng"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// versionCmd represents the version command
|
||||
func versionCmd(ctx context.Context) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Display the OneRNG's hardware version",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
o := &onerng.OneRNG{Path: opts.Device}
|
||||
version, err := o.Version(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("OneRNG Hardware Version: %d\n", version)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
3
go.mod
3
go.mod
@ -3,10 +3,7 @@ module github.com/hairyhenderson/go-onerng
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/spf13/cobra v1.1.1
|
||||
github.com/spf13/viper v1.7.1
|
||||
github.com/stretchr/testify v1.6.1
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897
|
||||
)
|
||||
|
24
go.sum
24
go.sum
@ -11,7 +11,6 @@ cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqCl
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
@ -37,10 +36,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
@ -68,7 +64,6 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
@ -89,7 +84,6 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
@ -100,7 +94,6 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
@ -112,7 +105,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
@ -125,19 +117,15 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
@ -157,33 +145,25 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
|
||||
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
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/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
|
||||
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
@ -252,11 +232,9 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@ -304,13 +282,11 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
72
onerng.go
72
onerng.go
@ -35,14 +35,13 @@ import (
|
||||
"context"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"fmt"
|
||||
"io"
|
||||
mrand "math/rand"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// OneRNG - a OneRNG device
|
||||
@ -51,6 +50,8 @@ type OneRNG struct {
|
||||
device io.ReadWriteCloser
|
||||
}
|
||||
|
||||
const copyReadTimeout = 500 * time.Millisecond
|
||||
|
||||
// cmd sends one or more commands to the OneRNG. The device is not closed on
|
||||
// completion, as it's usually being read from simultaneously.
|
||||
func (o *OneRNG) cmd(ctx context.Context, c ...string) error {
|
||||
@ -61,7 +62,7 @@ func (o *OneRNG) cmd(ctx context.Context, c ...string) error {
|
||||
for _, v := range c {
|
||||
_, err = o.device.Write([]byte(v))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Errored on command %q", v)
|
||||
return fmt.Errorf("errored on command %q: %w", v, err)
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@ -69,6 +70,7 @@ func (o *OneRNG) cmd(ctx context.Context, c ...string) error {
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -79,6 +81,7 @@ func (o *OneRNG) open() (err error) {
|
||||
return nil
|
||||
}
|
||||
o.device, err = os.OpenFile(o.Path, os.O_RDWR, 0600)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@ -89,6 +92,7 @@ func (o *OneRNG) close() error {
|
||||
}
|
||||
err := o.device.Close()
|
||||
o.device = nil
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@ -105,7 +109,7 @@ func (o *OneRNG) Version(ctx context.Context) (int, error) {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
_, cancel := context.WithCancel(ctx)
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
buf := make(chan string)
|
||||
errc := make(chan error, 1)
|
||||
@ -127,9 +131,10 @@ loop:
|
||||
if strings.HasPrefix(b, "Version ") {
|
||||
verString = b
|
||||
cancel()
|
||||
|
||||
break loop
|
||||
}
|
||||
case err := <-errc:
|
||||
case err = <-errc:
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
@ -141,6 +146,7 @@ loop:
|
||||
|
||||
n := strings.Replace(verString, "Version ", "", 1)
|
||||
version, err := strconv.Atoi(n)
|
||||
|
||||
return version, err
|
||||
}
|
||||
|
||||
@ -174,9 +180,10 @@ loop:
|
||||
if strings.HasPrefix(b, "___") {
|
||||
idString = b
|
||||
cancel()
|
||||
|
||||
break loop
|
||||
}
|
||||
case err := <-errc:
|
||||
case err = <-errc:
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
@ -199,14 +206,15 @@ func (o *OneRNG) Flush(ctx context.Context) error {
|
||||
|
||||
_, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
err = o.cmd(ctx, cmdFlush)
|
||||
return err
|
||||
|
||||
return o.cmd(ctx, cmdFlush)
|
||||
}
|
||||
|
||||
// Image extracts the firmware image. This image is padded with random data to
|
||||
// either 128Kb or 256Kb (depending on hardware), and signed.
|
||||
//
|
||||
// See also the Verify function.
|
||||
//nolint:gocyclo
|
||||
func (o *OneRNG) Image(ctx context.Context) ([]byte, error) {
|
||||
err := o.open()
|
||||
if err != nil {
|
||||
@ -219,9 +227,7 @@ func (o *OneRNG) Image(ctx context.Context) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
_, cancel := context.WithCancel(ctx)
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
buf := make(chan []byte)
|
||||
errc := make(chan error, 1)
|
||||
@ -234,25 +240,24 @@ func (o *OneRNG) Image(ctx context.Context) ([]byte, error) {
|
||||
|
||||
image := []byte{}
|
||||
zeros := 0
|
||||
loop:
|
||||
for {
|
||||
// stream data until we're done, or until we have 200+ consecutive zeroes
|
||||
//nolint:gomnd
|
||||
for zeros <= 200 {
|
||||
var b []byte
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case b = <-buf:
|
||||
image = append(image, b...)
|
||||
// count consecutive zeroes - if we hit non-zero, reset
|
||||
for _, v := range b {
|
||||
if v == 0 {
|
||||
zeros++
|
||||
} else {
|
||||
zeros = 0
|
||||
}
|
||||
if zeros > 200 {
|
||||
break loop
|
||||
}
|
||||
}
|
||||
case err := <-errc:
|
||||
case err = <-errc:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@ -278,6 +283,7 @@ func (o *OneRNG) Init(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
// fmt.Fprintf(os.Stderr, "Initialized after %d loops\n", i)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -298,13 +304,15 @@ func (o *OneRNG) Read(ctx context.Context, out io.Writer, n int64, flags NoiseMo
|
||||
return 0, err
|
||||
}
|
||||
|
||||
//nolint:errcheck
|
||||
defer o.cmd(ctx, cmdPause)
|
||||
|
||||
written, err = copyWithContext(ctx, out, o.device, n)
|
||||
|
||||
return written, err
|
||||
}
|
||||
|
||||
// readData - try to read some data from the RNG
|
||||
// readData - try to read some data from the RNG (during initialization)
|
||||
func (o *OneRNG) readData(ctx context.Context) (int, error) {
|
||||
err := o.open()
|
||||
if err != nil {
|
||||
@ -312,7 +320,8 @@ func (o *OneRNG) readData(ctx context.Context) (int, error) {
|
||||
}
|
||||
defer o.close()
|
||||
|
||||
_, cancel := context.WithTimeout(ctx, 50*time.Millisecond)
|
||||
const readTimeout = 50 * time.Millisecond
|
||||
_, cancel := context.WithTimeout(ctx, readTimeout)
|
||||
defer cancel()
|
||||
|
||||
buf := make(chan []byte)
|
||||
@ -325,6 +334,7 @@ func (o *OneRNG) readData(ctx context.Context) (int, error) {
|
||||
}
|
||||
|
||||
// make sure we always end with a pause/silence/flush
|
||||
//nolint:errcheck
|
||||
defer o.cmd(ctx, cmdPause, noiseCommand(Silent), cmdFlush)
|
||||
|
||||
// blocking read from the channel, with a timeout (from context)
|
||||
@ -344,6 +354,7 @@ func (o *OneRNG) stream(ctx context.Context, bs int, buf chan []byte, errc chan
|
||||
err := o.open()
|
||||
if err != nil {
|
||||
errc <- err
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@ -354,10 +365,12 @@ func (o *OneRNG) stream(ctx context.Context, bs int, buf chan []byte, errc chan
|
||||
n, err := io.ReadAtLeast(o.device, b, len(b))
|
||||
if err != nil {
|
||||
errc <- err
|
||||
|
||||
return
|
||||
}
|
||||
if n < len(b) {
|
||||
errc <- errors.Errorf("unexpected short read - wanted %db, read %db", len(b), n)
|
||||
errc <- fmt.Errorf("unexpected short read: wanted %db, read %db", len(b), n)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@ -373,6 +386,7 @@ func (o *OneRNG) scan(ctx context.Context, buf chan string, errc chan error) {
|
||||
err := o.open()
|
||||
if err != nil {
|
||||
errc <- err
|
||||
|
||||
return
|
||||
}
|
||||
defer close(buf)
|
||||
@ -408,7 +422,7 @@ const (
|
||||
// predictable ways.
|
||||
//
|
||||
// This uses AES-128.
|
||||
func (o *OneRNG) AESWhitener(ctx context.Context, out io.WriteCloser) (io.WriteCloser, error) {
|
||||
func (o *OneRNG) AESWhitener(ctx context.Context, out io.Writer) (io.WriteCloser, error) {
|
||||
k, err := o.key(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -418,8 +432,10 @@ func (o *OneRNG) AESWhitener(ctx context.Context, out io.WriteCloser) (io.WriteC
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create a random IV with math/rand - doesn't need to be cryptographically-random
|
||||
// create a random IV with math/rand - doesn't need to be cryptographically-random,
|
||||
// and we don't want to consume entropy while trying to generate entropy...
|
||||
iv := make([]byte, aes.BlockSize)
|
||||
//nolint:gosec
|
||||
_, err = mrand.Read(iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -427,6 +443,7 @@ func (o *OneRNG) AESWhitener(ctx context.Context, out io.WriteCloser) (io.WriteC
|
||||
|
||||
stream := cipher.NewCFBEncrypter(block, iv)
|
||||
s := &cipher.StreamWriter{S: stream, W: out}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@ -442,7 +459,10 @@ func (o *OneRNG) key(ctx context.Context) ([]byte, error) {
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
//nolint:errcheck
|
||||
defer o.cmd(ctx, cmdPause)
|
||||
|
||||
// 16 bytes == AES-128
|
||||
_, err = copyWithContext(ctx, buf, o.device, 16)
|
||||
k := buf.Bytes()
|
||||
@ -464,12 +484,13 @@ const (
|
||||
// Default mode - Avalanche enabled, RF disabled, Whitener enabled.
|
||||
Default NoiseMode = 0
|
||||
// Silent - a convenience - everything disabled
|
||||
Silent NoiseMode = 4
|
||||
Silent NoiseMode = DisableAvalanche
|
||||
)
|
||||
|
||||
// noiseCommand converts the given mode to the appropriate command to send to the OneRNG
|
||||
func noiseCommand(flags NoiseMode) string {
|
||||
num := strconv.Itoa(int(flags))
|
||||
|
||||
return "cmd" + num + "\n"
|
||||
}
|
||||
|
||||
@ -485,7 +506,7 @@ func copyWithContext(ctx context.Context, dst io.Writer, src io.Reader, n int64)
|
||||
rf := func(p []byte) (int, error) {
|
||||
if f, ok := src.(*os.File); ok {
|
||||
// I don't want reads to block forever, but I also don't want to time out immediately
|
||||
err := f.SetReadDeadline(time.Now().Add(500 * time.Millisecond))
|
||||
err := f.SetReadDeadline(time.Now().Add(copyReadTimeout))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -499,9 +520,11 @@ func copyWithContext(ctx context.Context, dst io.Writer, src io.Reader, n int64)
|
||||
if allowedTimeouts > 0 {
|
||||
if err != nil && os.IsTimeout(err) {
|
||||
allowedTimeouts--
|
||||
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
@ -509,5 +532,6 @@ func copyWithContext(ctx context.Context, dst io.Writer, src io.Reader, n int64)
|
||||
if n < 0 {
|
||||
return io.Copy(dst, readerFunc(rf))
|
||||
}
|
||||
|
||||
return io.CopyN(dst, readerFunc(rf), n)
|
||||
}
|
||||
|
@ -8,31 +8,25 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// func TestNoiseCommand(t *testing.T) {
|
||||
// fmt.Println(DisableWhitener)
|
||||
// fmt.Println(EnableRF)
|
||||
// fmt.Println(DisableAvalanche)
|
||||
|
||||
// fmt.Println(Default)
|
||||
// fmt.Println(Silent)
|
||||
|
||||
// testdata := []struct {
|
||||
// flags NoiseMode
|
||||
// cmd string
|
||||
// }{
|
||||
// {Default, "cmd0\n"},
|
||||
// {DisableWhitener, "cmd1\n"},
|
||||
// {EnableRF, "cmd2\n"},
|
||||
// {EnableRF | DisableWhitener, "cmd3\n"},
|
||||
// {DisableAvalanche, "cmd4\n"},
|
||||
// {DisableAvalanche | DisableWhitener, "cmd5\n"},
|
||||
// {DisableAvalanche | EnableRF, "cmd6\n"},
|
||||
// {DisableAvalanche | EnableRF | DisableWhitener, "cmd7\n"},
|
||||
// }
|
||||
// for _, d := range testdata {
|
||||
// assert.Equal(t, d.cmd, noiseCommand(d.flags), d.cmd, d.flags)
|
||||
// }
|
||||
// }
|
||||
func TestNoiseCommand(t *testing.T) {
|
||||
testdata := []struct {
|
||||
flags NoiseMode
|
||||
cmd string
|
||||
}{
|
||||
{Default, "cmd0\n"},
|
||||
{DisableWhitener, "cmd1\n"},
|
||||
{EnableRF, "cmd2\n"},
|
||||
{EnableRF | DisableWhitener, "cmd3\n"},
|
||||
{Silent, "cmd4\n"},
|
||||
{DisableAvalanche, "cmd4\n"},
|
||||
{DisableAvalanche | DisableWhitener, "cmd5\n"},
|
||||
{DisableAvalanche | EnableRF, "cmd6\n"},
|
||||
{DisableAvalanche | EnableRF | DisableWhitener, "cmd7\n"},
|
||||
}
|
||||
for _, d := range testdata {
|
||||
assert.Equal(t, d.cmd, noiseCommand(d.flags), d.cmd, d.flags)
|
||||
}
|
||||
}
|
||||
|
||||
type fakeDev struct {
|
||||
closed bool
|
||||
@ -48,6 +42,7 @@ func (d *fakeDev) reset() {
|
||||
|
||||
func (d *fakeDev) Close() error {
|
||||
d.closed = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -101,3 +96,19 @@ func TestVersion(t *testing.T) {
|
||||
assert.Equal(t, "cmdo\ncmd4\ncmdv\ncmdO\ncmdo\n", d.wbuf.String())
|
||||
assert.Equal(t, 3, v)
|
||||
}
|
||||
|
||||
func TestIdentify(t *testing.T) {
|
||||
d := &fakeDev{
|
||||
wbuf: &bytes.Buffer{},
|
||||
rbuf: bytes.NewBufferString("dfoawiuhf98h9inf2oifoi2jr\n" +
|
||||
"dfkjawflihjwfoiuh2rliu13he487631487645t98y23rtoqu3rbno9q34htgfv\n" +
|
||||
"\r\nVersion 3\r\nas;dlfjaw;oihf2ih2o3iuf2ofnlo2jnlfuhf2iou\n\n" +
|
||||
"dlfkhadslfihwaflkhjw\n___lskdjfalsdkjflsd___\n\n"),
|
||||
}
|
||||
o := &OneRNG{Path: "/dev/null", device: d}
|
||||
ctx := context.Background()
|
||||
id, err := o.Identify(ctx)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "cmd4\ncmdI\ncmdO\ncmdo\n", d.wbuf.String())
|
||||
assert.Equal(t, "___lskdjfalsdkjflsd___", id)
|
||||
}
|
||||
|
209
verify.go
209
verify.go
@ -7,7 +7,6 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/crypto/openpgp"
|
||||
)
|
||||
|
||||
@ -16,66 +15,137 @@ import (
|
||||
//
|
||||
// Details are printed to Stderr on success, otherwise an error is returned.
|
||||
//
|
||||
// This is a more-or-less straight port from the official onerng_verify.py
|
||||
// script distributed alongside the OneRNG package.
|
||||
// The general logic is ported from the official onerng_verify.py script
|
||||
// distributed alongside the OneRNG package.
|
||||
func Verify(ctx context.Context, image io.Reader, pubkey string) error {
|
||||
var x byte
|
||||
length := 0
|
||||
version := 0
|
||||
state := int8(0)
|
||||
for {
|
||||
if err := readMagic(image); err != nil {
|
||||
return fmt.Errorf("failed to find magic number: %w", err)
|
||||
}
|
||||
length, version, err := readHeader(image)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read header: %w", err)
|
||||
}
|
||||
|
||||
return readAndVerify(image, version, length, pubkey)
|
||||
}
|
||||
|
||||
func read(r io.Reader, p []byte) error {
|
||||
n, err := r.Read(p)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read failed: %w", err)
|
||||
}
|
||||
if n == 0 {
|
||||
return fmt.Errorf("short read")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// readHeader reads the header and returns the length and the version
|
||||
//nolint:gomnd
|
||||
func readHeader(r io.Reader) (length, version int, err error) {
|
||||
// read the length
|
||||
l := make([]byte, 3)
|
||||
if err := read(r, l); err != nil {
|
||||
return 0, 0, fmt.Errorf("failed reading length: %w", err)
|
||||
}
|
||||
length = int(l[0])
|
||||
length |= int(l[1]) << 8
|
||||
length |= int(l[2]) << 16
|
||||
|
||||
// read the version
|
||||
l = make([]byte, 2)
|
||||
if err := read(r, l); err != nil {
|
||||
return 0, 0, fmt.Errorf("failed reading version: %w", err)
|
||||
}
|
||||
version = int(l[0])
|
||||
version |= int(l[1]) << 8
|
||||
|
||||
// read the actual code size - we don't use it yet
|
||||
if err := read(r, make([]byte, 2)); err != nil {
|
||||
return 0, 0, fmt.Errorf("failed reading actual code size: %w", err)
|
||||
}
|
||||
|
||||
return length, version, nil
|
||||
}
|
||||
|
||||
// readMagic reads the input until the magic sequence 0xfeedbeef2014 is found
|
||||
//nolint:gomnd, gocyclo
|
||||
func readMagic(r io.Reader) error {
|
||||
for i := int8(0); i < 6; i++ {
|
||||
c := make([]byte, 1)
|
||||
n, err := io.ReadAtLeast(image, c, len(c))
|
||||
if err := read(r, c); err != nil {
|
||||
return fmt.Errorf("couldn't read header: %w", err)
|
||||
}
|
||||
x := c[0]
|
||||
|
||||
switch {
|
||||
case i == 0 && x == 0xfe,
|
||||
i == 1 && x == 0xed,
|
||||
i == 2 && x == 0xbe,
|
||||
i == 3 && x == 0xef,
|
||||
i == 4 && x == 0x20,
|
||||
i == 5 && x == 0x14:
|
||||
// as long as this looks like the magic number, keep going!
|
||||
continue
|
||||
default:
|
||||
i = 0
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func readAndVerify(image io.Reader, version, length int, pubkey string) error {
|
||||
signed, signature, err := parseImage(image, version, length)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n == 0 {
|
||||
return errors.Errorf("Short image")
|
||||
}
|
||||
x = c[0]
|
||||
|
||||
// 1. read magic (6 bytes)
|
||||
if state == 0 && x == 0xfe {
|
||||
state++
|
||||
} else if state == 1 && x == 0xed {
|
||||
state++
|
||||
} else if state == 2 && x == 0xbe {
|
||||
state++
|
||||
} else if state == 3 && x == 0xef {
|
||||
state++
|
||||
} else if state == 4 && x == 0x20 {
|
||||
state++
|
||||
} else if state == 5 && x == 0x14 {
|
||||
state++
|
||||
// 2. read length (3 bytes)
|
||||
} else if state == 6 {
|
||||
length = int(x)
|
||||
state++
|
||||
} else if state == 7 {
|
||||
length = length | (int(x) << 8)
|
||||
state++
|
||||
} else if state == 8 {
|
||||
length = length | (int(x) << 16)
|
||||
state++
|
||||
// 3. read version (2 bytes)
|
||||
} else if state == 9 {
|
||||
version = int(x)
|
||||
state++
|
||||
} else if state == 10 {
|
||||
version = version | (int(x) << 8)
|
||||
state++
|
||||
} else if state == 11 {
|
||||
// skip a padding byte I guess...
|
||||
state++
|
||||
} else if state == 12 {
|
||||
// 4. read image
|
||||
signer, err := verifyImage(signed, signature, pubkey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "firmware verification passed OK - version=%d\n", version)
|
||||
for _, id := range signer.Identities {
|
||||
fmt.Fprintf(os.Stderr, "signed by: %#v\n", id.Name)
|
||||
fmt.Fprintf(os.Stderr, "\tcreated: %q\n", id.SelfSignature.CreationTime)
|
||||
fmt.Fprintf(os.Stderr, "\tfingerprint: %X\n", signer.PrimaryKey.Fingerprint)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyImage(signed, sig []byte, pubkey string) (signer *openpgp.Entity, err error) {
|
||||
// read public key
|
||||
keyring, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(pubkey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// verify
|
||||
signer, err = openpgp.CheckDetachedSignature(
|
||||
keyring,
|
||||
bytes.NewBuffer(signed),
|
||||
bytes.NewBuffer(sig),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to verify firmware signature: %w", err)
|
||||
}
|
||||
|
||||
return signer, nil
|
||||
}
|
||||
|
||||
//nolint:gomnd
|
||||
func parseImage(image io.Reader, version, length int) (signed, sig []byte, err error) {
|
||||
c := make([]byte, length)
|
||||
n, err := io.ReadAtLeast(image, c, length)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, nil, err
|
||||
}
|
||||
if n != length {
|
||||
return errors.Errorf("Bad image")
|
||||
return nil, nil, fmt.Errorf("bad image: wrong length: was %d, expected %d", n, length)
|
||||
}
|
||||
|
||||
// determine end offset
|
||||
@ -86,40 +156,13 @@ func Verify(ctx context.Context, image io.Reader, pubkey string) error {
|
||||
endOff = 600
|
||||
}
|
||||
|
||||
// read length of signature - 2 bytes between image and signature
|
||||
x = c[length-endOff]
|
||||
klen := int(x)
|
||||
x = c[length-endOff+1]
|
||||
klen = klen | (int(x) << 8)
|
||||
// signature length - 2 bytes between image and signature
|
||||
slen := int(c[length-endOff])
|
||||
slen |= int(c[length-endOff+1]) << 8
|
||||
|
||||
// split last part into image (signed part) & signature
|
||||
signature := bytes.NewBuffer(c[length-endOff+2 : length-endOff+2+klen])
|
||||
signed := bytes.NewBuffer(c[0 : length-endOff])
|
||||
signed = c[0 : length-endOff]
|
||||
sig = c[length-endOff+2 : length-endOff+2+slen]
|
||||
|
||||
// read public key
|
||||
keyring, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(pubkey))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// verify
|
||||
signer, err := openpgp.CheckDetachedSignature(keyring, signed, signature)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to verify firmware signature")
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "firmware verification passed OK - version=%d\n", version)
|
||||
for _, id := range signer.Identities {
|
||||
fmt.Fprintf(os.Stderr, "signed by: %#v\n", id.Name)
|
||||
fmt.Fprintf(os.Stderr, "\tcreated: %q\n", id.SelfSignature.CreationTime)
|
||||
fmt.Fprintf(os.Stderr, "\tfingerprint: %X\n", signer.PrimaryKey.Fingerprint)
|
||||
}
|
||||
|
||||
break
|
||||
} else {
|
||||
// something didn't line up, so we need to begin again...
|
||||
state = 0
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return signed, sig, nil
|
||||
}
|
||||
|
52
verify_test.go
Normal file
52
verify_test.go
Normal file
@ -0,0 +1,52 @@
|
||||
package onerng
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestReadMagic(t *testing.T) {
|
||||
r := &bytes.Buffer{}
|
||||
err := readMagic(r)
|
||||
assert.Error(t, err)
|
||||
|
||||
r = bytes.NewBufferString("abcdefg")
|
||||
err = readMagic(r)
|
||||
assert.Error(t, err)
|
||||
|
||||
r = bytes.NewBuffer([]byte{0x00, 0x01, 0x02, 0xfe, 0xed, 0xbe, 0xee, 0xff})
|
||||
err = readMagic(r)
|
||||
assert.Error(t, err)
|
||||
|
||||
r = bytes.NewBuffer([]byte{0x00, 0x01, 0x02, 0xfe, 0xed, 0xbe, 0xef, 0x20, 0x14})
|
||||
err = readMagic(r)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestReadHeader(t *testing.T) {
|
||||
r := &bytes.Buffer{}
|
||||
_, _, err := readHeader(r)
|
||||
assert.Error(t, err)
|
||||
|
||||
r = bytes.NewBuffer([]byte{0x00, 0x00, 0x00})
|
||||
_, _, err = readHeader(r)
|
||||
assert.Error(t, err)
|
||||
|
||||
r = bytes.NewBuffer([]byte{0x00, 0x00, 0x00, 0x00})
|
||||
_, _, err = readHeader(r)
|
||||
assert.Error(t, err)
|
||||
|
||||
r = bytes.NewBuffer([]byte{0x0f, 0x00, 0x00, 0x07, 0x00, 0xff, 0xee})
|
||||
l, v, err := readHeader(r)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 15, l)
|
||||
assert.Equal(t, 7, v)
|
||||
|
||||
r = bytes.NewBuffer([]byte{0x0f, 0xf0, 0x01, 0x07, 0x70, 0xff, 0xee, 0x11, 0x42})
|
||||
l, v, err = readHeader(r)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0x01f00f, l)
|
||||
assert.Equal(t, 0x7007, v)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user