mirror of
https://github.com/hairyhenderson/go-onerng.git
synced 2025-04-04 09:40:12 -05:00
A bunch of changes
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
This commit is contained in:
parent
16a4d2826c
commit
a1879b1eb0
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
!*/onerng
|
||||
onerng
|
||||
|
10
cmd/onerng/doc.go
Normal file
10
cmd/onerng/doc.go
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
|
||||
onerng is a OneRNG hardware random number generation utility.
|
||||
|
||||
Usage
|
||||
|
||||
TODO...
|
||||
|
||||
*/
|
||||
package main
|
@ -1,4 +1,4 @@
|
||||
package cmd
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
@ -1,4 +1,4 @@
|
||||
package cmd
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
@ -1,4 +1,4 @@
|
||||
package cmd
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
@ -1,4 +1,4 @@
|
||||
package cmd
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
@ -1,11 +1,12 @@
|
||||
// onerng: OneRNG hardware random number generation utility
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"github.com/hairyhenderson/go-onerng/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -26,5 +27,10 @@ func main() {
|
||||
}
|
||||
}()
|
||||
|
||||
cmd.Execute(ctx)
|
||||
cmd := rootCmd(ctx)
|
||||
initConfig(ctx, cmd)
|
||||
if err := cmd.Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package cmd
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -26,7 +26,7 @@ func readCmd(ctx context.Context) *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}
|
||||
o := &onerng.OneRNG{Path: opts.Device}
|
||||
err := o.Init(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "init failed")
|
@ -1,4 +1,4 @@
|
||||
package cmd
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -11,7 +11,7 @@ import (
|
||||
|
||||
var (
|
||||
cfgFile string
|
||||
opts Config
|
||||
opts config
|
||||
)
|
||||
|
||||
func rootCmd(ctx context.Context) *cobra.Command {
|
||||
@ -30,19 +30,9 @@ correctly, and that the firmware has not been tampered with.`,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute -
|
||||
func Execute(ctx context.Context) {
|
||||
cmd := rootCmd(ctx)
|
||||
initConfig(ctx, cmd)
|
||||
if err := cmd.Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func initConfig(ctx context.Context, cmd *cobra.Command) {
|
||||
cmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.go-onerng.yaml)")
|
||||
opts = Config{}
|
||||
opts = config{}
|
||||
cmd.PersistentFlags().StringVarP(&opts.Device, "device", "d", "/dev/ttyACM0", "the OneRNG device")
|
||||
|
||||
cmd.AddCommand(
|
||||
@ -69,7 +59,6 @@ func initConfig(ctx context.Context, cmd *cobra.Command) {
|
||||
}
|
||||
}
|
||||
|
||||
// Config -
|
||||
type Config struct {
|
||||
type config struct {
|
||||
Device string
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package cmd
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
@ -1,4 +1,4 @@
|
||||
package cmd
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -14,7 +14,7 @@ func versionCmd(ctx context.Context) *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}
|
||||
o := &onerng.OneRNG{Path: opts.Device}
|
||||
version, err := o.Version(ctx)
|
||||
if err != nil {
|
||||
return err
|
47
commands.go
47
commands.go
@ -1,47 +0,0 @@
|
||||
package onerng
|
||||
|
||||
const (
|
||||
// CmdVersion - print firmware version (as "Version n")
|
||||
CmdVersion = "cmdv\n"
|
||||
// CmdFlush - flush entropy pool
|
||||
CmdFlush = "cmdw\n"
|
||||
// CmdImage - extract the signed firmware image for verification
|
||||
CmdImage = "cmdX\n"
|
||||
// CmdID - print hardware ID
|
||||
CmdID = "cmdI\n"
|
||||
// CmdRun - start the task
|
||||
CmdRun = "cmdO\n"
|
||||
// CmdPause - stop/pause the task
|
||||
CmdPause = "cmdo\n"
|
||||
|
||||
// CmdAvalancheWhitener - Avalanche noise with whitener (default)
|
||||
CmdAvalancheWhitener = "cmd0\n"
|
||||
// CmdAvalanche - Raw avalanche noise
|
||||
CmdAvalanche = "cmd1\n"
|
||||
// CmdAvalancheRFWhitener - Avalanche noise and RF noise with whitener
|
||||
CmdAvalancheRFWhitener = "cmd2\n"
|
||||
// CmdAvalancheRF - Raw avalanche noise and RF noise
|
||||
CmdAvalancheRF = "cmd3\n"
|
||||
// CmdSilent - No noise (necessary for image extraction)
|
||||
CmdSilent = "cmd4\n"
|
||||
// CmdSilent2 - No noise
|
||||
CmdSilent2 = "cmd5\n"
|
||||
// CmdRFWhitener - RF noise with whitener
|
||||
CmdRFWhitener = "cmd6\n"
|
||||
// CmdRF - Raw RF noise
|
||||
CmdRF = "cmd7\n"
|
||||
)
|
||||
|
||||
const (
|
||||
// DisableWhitener - Disable the on-board CRC16 generator - no effect if both noise generators are disabled
|
||||
DisableWhitener ReadMode = 1 << iota
|
||||
// EnableRF - Enable noise generation from RF
|
||||
EnableRF
|
||||
// DisableAvalanche - Disable noise generation from the Avalanche Diode
|
||||
DisableAvalanche
|
||||
|
||||
// Default - Avalanche enabled, RF disabled, Whitener enabled.
|
||||
Default ReadMode = 0
|
||||
// Silent - a convenience - everything disabled
|
||||
Silent ReadMode = 4
|
||||
)
|
46
copy.go
46
copy.go
@ -1,46 +0,0 @@
|
||||
package onerng
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type readerFunc func(p []byte) (n int, err error)
|
||||
|
||||
func (rf readerFunc) Read(p []byte) (n int, err error) { return rf(p) }
|
||||
|
||||
// io.CopyN/io.Copy with context support
|
||||
func copyWithContext(ctx context.Context, dst io.Writer, src *os.File, n int64) (int64, error) {
|
||||
// allow 10 500ms timeouts, for a total of 5s. After this, it's probably worth just giving up
|
||||
allowedTimeouts := 10
|
||||
|
||||
rf := func(p []byte) (int, error) {
|
||||
// I don't want reads to block forever, but I also don't want to time out immediately
|
||||
err := src.SetReadDeadline(time.Now().Add(500 * time.Millisecond))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return 0, ctx.Err()
|
||||
default:
|
||||
n, err := src.Read(p)
|
||||
if allowedTimeouts > 0 {
|
||||
if err != nil && strings.HasSuffix(err.Error(), "i/o timeout") {
|
||||
allowedTimeouts--
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
|
||||
if n < 0 {
|
||||
return io.Copy(dst, readerFunc(rf))
|
||||
}
|
||||
return io.CopyN(dst, readerFunc(rf), n)
|
||||
}
|
180
onerng.go
180
onerng.go
@ -1,3 +1,32 @@
|
||||
/*
|
||||
Package onerng provides functions to help interface with the OneRNG hardware RNG.
|
||||
|
||||
See http://onerng.info for information about the device, and see especially
|
||||
http://www.moonbaseotago.com/onerng/theory.html for the theory of operation.
|
||||
|
||||
To use this package, you must first plug the OneRNG into an available USB port,
|
||||
and your OS should auto-detect the device as a USB serial modem. On Linux, you
|
||||
may need to load the cdc_acm module.
|
||||
|
||||
Once you know which device file points to the OneRNG, you can instantiate a
|
||||
*OneRNG struct instance. All communication with the OneRNG is done through
|
||||
this instance.
|
||||
|
||||
o := &OneRNG{Path: "/dev/ttyACM0"}
|
||||
version, err := o.Version(context.TODO())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("version is %d\n", version)
|
||||
|
||||
Reading data from the OneRNG can be done with the Read function:
|
||||
|
||||
o := &OneRNG{Path: "/dev/ttyACM0"}
|
||||
_, err = o.Read(context.TODO(), os.Stdout, -1, EnableRF | DisableWhitener)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*/
|
||||
package onerng
|
||||
|
||||
import (
|
||||
@ -6,7 +35,6 @@ import (
|
||||
"context"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"fmt"
|
||||
"io"
|
||||
mrand "math/rand"
|
||||
"os"
|
||||
@ -20,21 +48,20 @@ import (
|
||||
// OneRNG - a OneRNG device
|
||||
type OneRNG struct {
|
||||
Path string
|
||||
device *os.File
|
||||
device io.ReadWriteCloser
|
||||
}
|
||||
|
||||
// ReadMode -
|
||||
type ReadMode uint32
|
||||
|
||||
// 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 {
|
||||
err := o.open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range c {
|
||||
_, err = o.device.WriteString(v)
|
||||
_, err = o.device.Write([]byte(v))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Errored on command %s", v)
|
||||
return errors.Wrapf(err, "Errored on command %q", v)
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@ -45,6 +72,8 @@ func (o *OneRNG) cmd(ctx context.Context, c ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// open the OneRNG device for read/write, if it hasn't already been opened.
|
||||
// Access it as o.device.
|
||||
func (o *OneRNG) open() (err error) {
|
||||
if o.device != nil {
|
||||
return nil
|
||||
@ -53,6 +82,7 @@ func (o *OneRNG) open() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// close the OneRNG device if it hasn't already been closed
|
||||
func (o *OneRNG) close() error {
|
||||
if o.device == nil {
|
||||
return nil
|
||||
@ -62,7 +92,7 @@ func (o *OneRNG) close() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Version -
|
||||
// Version - query the OneRNG for its hardware version
|
||||
func (o *OneRNG) Version(ctx context.Context) (int, error) {
|
||||
err := o.open()
|
||||
if err != nil {
|
||||
@ -70,7 +100,7 @@ func (o *OneRNG) Version(ctx context.Context) (int, error) {
|
||||
}
|
||||
defer o.close()
|
||||
|
||||
err = o.cmd(ctx, CmdPause)
|
||||
err = o.cmd(ctx, cmdPause)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -81,7 +111,7 @@ func (o *OneRNG) Version(ctx context.Context) (int, error) {
|
||||
errc := make(chan error, 1)
|
||||
go o.scan(ctx, buf, errc)
|
||||
|
||||
err = o.cmd(ctx, CmdSilent, CmdVersion, CmdRun)
|
||||
err = o.cmd(ctx, noiseCommand(Silent), cmdVersion, cmdRun)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -104,7 +134,7 @@ loop:
|
||||
}
|
||||
}
|
||||
|
||||
err = o.cmd(ctx, CmdPause)
|
||||
err = o.cmd(ctx, cmdPause)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -114,7 +144,7 @@ loop:
|
||||
return version, err
|
||||
}
|
||||
|
||||
// Identify -
|
||||
// Identify - query the OneRNG for its ID
|
||||
func (o *OneRNG) Identify(ctx context.Context) (string, error) {
|
||||
err := o.open()
|
||||
if err != nil {
|
||||
@ -128,7 +158,7 @@ func (o *OneRNG) Identify(ctx context.Context) (string, error) {
|
||||
errc := make(chan error, 1)
|
||||
go o.scan(ctx, buf, errc)
|
||||
|
||||
err = o.cmd(ctx, CmdSilent, CmdID, CmdRun)
|
||||
err = o.cmd(ctx, noiseCommand(Silent), cmdID, cmdRun)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -151,7 +181,7 @@ loop:
|
||||
}
|
||||
}
|
||||
|
||||
err = o.cmd(ctx, CmdPause)
|
||||
err = o.cmd(ctx, cmdPause)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -159,7 +189,7 @@ loop:
|
||||
return idString, err
|
||||
}
|
||||
|
||||
// Flush -
|
||||
// Flush the OneRNG's entropy pool
|
||||
func (o *OneRNG) Flush(ctx context.Context) error {
|
||||
err := o.open()
|
||||
if err != nil {
|
||||
@ -169,11 +199,14 @@ func (o *OneRNG) Flush(ctx context.Context) error {
|
||||
|
||||
_, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
err = o.cmd(ctx, CmdFlush)
|
||||
err = o.cmd(ctx, cmdFlush)
|
||||
return err
|
||||
}
|
||||
|
||||
// Image -
|
||||
// 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.
|
||||
func (o *OneRNG) Image(ctx context.Context) ([]byte, error) {
|
||||
err := o.open()
|
||||
if err != nil {
|
||||
@ -181,7 +214,7 @@ func (o *OneRNG) Image(ctx context.Context) ([]byte, error) {
|
||||
}
|
||||
defer o.close()
|
||||
|
||||
err = o.cmd(ctx, CmdPause, CmdSilent)
|
||||
err = o.cmd(ctx, cmdPause, noiseCommand(Silent))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -194,7 +227,7 @@ func (o *OneRNG) Image(ctx context.Context) ([]byte, error) {
|
||||
errc := make(chan error, 1)
|
||||
go o.stream(ctx, 4, buf, errc)
|
||||
|
||||
err = o.cmd(ctx, CmdSilent, CmdImage, CmdRun)
|
||||
err = o.cmd(ctx, noiseCommand(Silent), cmdImage, cmdRun)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -224,7 +257,7 @@ loop:
|
||||
}
|
||||
}
|
||||
|
||||
err = o.cmd(ctx, CmdPause)
|
||||
err = o.cmd(ctx, cmdPause)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -244,24 +277,28 @@ func (o *OneRNG) Init(ctx context.Context) error {
|
||||
break
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Initialized after %d loops\n", i)
|
||||
// fmt.Fprintf(os.Stderr, "Initialized after %d loops\n", i)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read -
|
||||
func (o *OneRNG) Read(ctx context.Context, out io.WriteCloser, n int64, flags ReadMode) (written int64, err error) {
|
||||
// Read n bytes of data from the OneRNG into the given Writer. Set flags to
|
||||
// configure the OneRNG's. Set n to -1 to continuously read until an error is
|
||||
// encountered, or the context is cancelled.
|
||||
//
|
||||
// The OneRNG device will be closed when the operation completes.
|
||||
func (o *OneRNG) Read(ctx context.Context, out io.Writer, n int64, flags NoiseMode) (written int64, err error) {
|
||||
err = o.open()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer o.close()
|
||||
|
||||
err = o.cmd(ctx, NoiseCommand(flags), CmdRun)
|
||||
err = o.cmd(ctx, noiseCommand(flags), cmdRun)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
defer o.cmd(ctx, CmdPause)
|
||||
defer o.cmd(ctx, cmdPause)
|
||||
|
||||
written, err = copyWithContext(ctx, out, o.device, n)
|
||||
return written, err
|
||||
@ -282,13 +319,13 @@ func (o *OneRNG) readData(ctx context.Context) (int, error) {
|
||||
errc := make(chan error, 1)
|
||||
go o.stream(ctx, 1, buf, errc)
|
||||
|
||||
err = o.cmd(ctx, CmdAvalanche, CmdRun)
|
||||
err = o.cmd(ctx, noiseCommand(Default), cmdRun)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// make sure we always end with a pause/silence/flush
|
||||
defer o.cmd(ctx, CmdPause, CmdSilent, CmdFlush)
|
||||
defer o.cmd(ctx, cmdPause, noiseCommand(Silent), cmdFlush)
|
||||
|
||||
// blocking read from the channel, with a timeout (from context)
|
||||
select {
|
||||
@ -350,15 +387,20 @@ func (o *OneRNG) scan(ctx context.Context, buf chan string, errc chan error) {
|
||||
}
|
||||
}
|
||||
|
||||
// NoiseCommand - returns the appropriate noise-generation command for the given flags
|
||||
func NoiseCommand(flags ReadMode) string {
|
||||
num := strconv.Itoa(int(flags))
|
||||
return "cmd" + num + "\n"
|
||||
}
|
||||
|
||||
type aesWhitener struct {
|
||||
out io.WriteCloser
|
||||
}
|
||||
const (
|
||||
// cmdVersion - print firmware version (as "Version n")
|
||||
cmdVersion = "cmdv\n"
|
||||
// cmdFlush - flush entropy pool
|
||||
cmdFlush = "cmdw\n"
|
||||
// cmdImage - extract the signed firmware image for verification
|
||||
cmdImage = "cmdX\n"
|
||||
// cmdID - print hardware ID
|
||||
cmdID = "cmdI\n"
|
||||
// cmdRun - start the task
|
||||
cmdRun = "cmdO\n"
|
||||
// cmdPause - stop/pause the task
|
||||
cmdPause = "cmdo\n"
|
||||
)
|
||||
|
||||
// AESWhitener creates a "whitener" that wraps the provided writer. The random
|
||||
// data that the OneRNG generates is sometimes a little "too" random for some
|
||||
@ -396,14 +438,76 @@ func (o *OneRNG) key(ctx context.Context) ([]byte, error) {
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
err = o.cmd(ctx, CmdAvalanche, CmdRun)
|
||||
err = o.cmd(ctx, noiseCommand(Default), cmdRun)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
defer o.cmd(ctx, CmdPause)
|
||||
defer o.cmd(ctx, cmdPause)
|
||||
// 16 bytes == AES-128
|
||||
_, err = copyWithContext(ctx, buf, o.device, 16)
|
||||
k := buf.Bytes()
|
||||
|
||||
return k, err
|
||||
}
|
||||
|
||||
// NoiseMode represents the different noise-generation modes available to the OneRNG
|
||||
type NoiseMode uint32
|
||||
|
||||
const (
|
||||
// DisableWhitener - Disable the on-board CRC16 generator - no effect if both noise generators are disabled
|
||||
DisableWhitener NoiseMode = 1 << iota
|
||||
// EnableRF - Enable noise generation from RF
|
||||
EnableRF
|
||||
// DisableAvalanche - Disable noise generation from the Avalanche Diode
|
||||
DisableAvalanche
|
||||
|
||||
// Default mode - Avalanche enabled, RF disabled, Whitener enabled.
|
||||
Default NoiseMode = 0
|
||||
// Silent - a convenience - everything disabled
|
||||
Silent NoiseMode = 4
|
||||
)
|
||||
|
||||
// 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"
|
||||
}
|
||||
|
||||
type readerFunc func(p []byte) (n int, err error)
|
||||
|
||||
func (rf readerFunc) Read(p []byte) (n int, err error) { return rf(p) }
|
||||
|
||||
// io.CopyN/io.Copy with cancellation support
|
||||
func copyWithContext(ctx context.Context, dst io.Writer, src io.Reader, n int64) (int64, error) {
|
||||
// allow 10 500ms timeouts, for a total of 5s. After this, it's probably worth just giving up
|
||||
allowedTimeouts := 10
|
||||
|
||||
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))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return 0, ctx.Err()
|
||||
default:
|
||||
n, err := src.Read(p)
|
||||
if allowedTimeouts > 0 {
|
||||
if err != nil && os.IsTimeout(err) {
|
||||
allowedTimeouts--
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
|
||||
if n < 0 {
|
||||
return io.Copy(dst, readerFunc(rf))
|
||||
}
|
||||
return io.CopyN(dst, readerFunc(rf), n)
|
||||
}
|
||||
|
115
onerng_test.go
115
onerng_test.go
@ -1,34 +1,103 @@
|
||||
package onerng
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"bytes"
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNoiseCommand(t *testing.T) {
|
||||
fmt.Println(DisableWhitener)
|
||||
fmt.Println(EnableRF)
|
||||
fmt.Println(DisableAvalanche)
|
||||
// func TestNoiseCommand(t *testing.T) {
|
||||
// fmt.Println(DisableWhitener)
|
||||
// fmt.Println(EnableRF)
|
||||
// fmt.Println(DisableAvalanche)
|
||||
|
||||
fmt.Println(Default)
|
||||
fmt.Println(Silent)
|
||||
// fmt.Println(Default)
|
||||
// fmt.Println(Silent)
|
||||
|
||||
testdata := []struct {
|
||||
flags ReadMode
|
||||
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)
|
||||
}
|
||||
// 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)
|
||||
// }
|
||||
// }
|
||||
|
||||
type fakeDev struct {
|
||||
closed bool
|
||||
rbuf *bytes.Buffer
|
||||
wbuf *bytes.Buffer
|
||||
}
|
||||
|
||||
func (d *fakeDev) reset() {
|
||||
d.closed = false
|
||||
d.rbuf.Reset()
|
||||
d.wbuf.Reset()
|
||||
}
|
||||
|
||||
func (d *fakeDev) Close() error {
|
||||
d.closed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *fakeDev) Read(b []byte) (int, error) {
|
||||
return d.rbuf.Read(b)
|
||||
}
|
||||
|
||||
func (d *fakeDev) Write(b []byte) (int, error) {
|
||||
return d.wbuf.Write(b)
|
||||
}
|
||||
|
||||
func TestCmd(t *testing.T) {
|
||||
d := &fakeDev{rbuf: &bytes.Buffer{}, wbuf: &bytes.Buffer{}}
|
||||
o := &OneRNG{Path: "/dev/null", device: d}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
err := o.cmd(ctx, "foo", "bar")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "foobar", d.wbuf.String())
|
||||
|
||||
d.reset()
|
||||
cancel()
|
||||
err = o.cmd(ctx, "foo", "bar")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "foo", d.wbuf.String())
|
||||
}
|
||||
|
||||
func TestClose(t *testing.T) {
|
||||
d := &fakeDev{}
|
||||
o := &OneRNG{Path: "/dev/null", device: d}
|
||||
err := o.close()
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, d.closed)
|
||||
|
||||
o = &OneRNG{Path: "/dev/null", device: nil}
|
||||
err = o.close()
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, o.device)
|
||||
}
|
||||
|
||||
func TestVersion(t *testing.T) {
|
||||
d := &fakeDev{
|
||||
wbuf: &bytes.Buffer{},
|
||||
rbuf: bytes.NewBufferString("dfoawiuhf98h9inf2oifoi2jr\n" +
|
||||
"dfkjawflihjwfoiuh2rliu13he487631487645t98y23rtoqu3rbno9q34htgfv\n" +
|
||||
"\r\nVersion 3\r\nas;dlfjaw;oihf2ih2o3iuf2ofnlo2jnlfuhf2iou\n\n"),
|
||||
}
|
||||
o := &OneRNG{Path: "/dev/null", device: d}
|
||||
ctx := context.Background()
|
||||
v, err := o.Version(ctx)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "cmdo\ncmd4\ncmdv\ncmdO\ncmdo\n", d.wbuf.String())
|
||||
assert.Equal(t, 3, v)
|
||||
}
|
||||
|
29
verify.go
29
verify.go
@ -11,25 +11,13 @@ import (
|
||||
"golang.org/x/crypto/openpgp"
|
||||
)
|
||||
|
||||
// looks like:
|
||||
// fe ed be ef 20 14 - magic number
|
||||
// 00 00 04 - len (little endian)
|
||||
// 00 00 - version (little endian)
|
||||
// image - len bytes of image
|
||||
type fwImage struct {
|
||||
magic [6]byte // must be 0xfeedbeef2014
|
||||
length [3]byte // LE
|
||||
version [2]byte // LE
|
||||
_ [1]byte //
|
||||
fullimg [262144]byte // full image
|
||||
// image [261536]byte // 256kb minus end offset
|
||||
// slen [2]byte // signature length
|
||||
// sig [543]byte // signature
|
||||
// endOff [608]byte
|
||||
}
|
||||
|
||||
// Verify - this is a more-or-less straight port from the onerng_verify.py script
|
||||
// distributed with the OneRNG package
|
||||
// Verify reads a signed firmware image, extracts the signature, and verifies
|
||||
// it against the given public key.
|
||||
//
|
||||
// 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.
|
||||
func Verify(ctx context.Context, image io.Reader, pubkey string) error {
|
||||
var x byte
|
||||
length := 0
|
||||
@ -108,9 +96,6 @@ func Verify(ctx context.Context, image io.Reader, pubkey string) error {
|
||||
signature := bytes.NewBuffer(c[length-endOff+2 : length-endOff+2+klen])
|
||||
signed := bytes.NewBuffer(c[0 : length-endOff])
|
||||
|
||||
// leftovers := c[length-endOff+2+klen : length]
|
||||
// fmt.Fprintf(os.Stderr, "leftovers (%d):\n%#x\n", len(leftovers), leftovers)
|
||||
|
||||
// read public key
|
||||
keyring, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(pubkey))
|
||||
if err != nil {
|
||||
|
Loading…
x
Reference in New Issue
Block a user