From ee10adc91e79bca395a6b069797a99863fc957dd Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 2 Mar 2017 14:01:20 -0800 Subject: crypto: reading and writing recovery keys This commit adds in the concept of recovery codes: human-readable strings that contain the necessary information to rederive a cryptographic key. These keys look like: 73PZBXVP-DKJX7SKV-NNTFIC7A-QEGRPZUX-4K5ORRH2-MTKMKP3B-HFCA==== They are input or output directly to a io.Reader or io.Writer respectively. This prevents the data from passing through unsecured memory before it gets to its destination. Of course, if the provided io.Reader or io.Writer is insecure, there is nothing we can do. In most cases the provided io.Reader or io.Writer will be stdin or stdout. In some rare cases you might want to pipe the output to another key. This commit also adds tests and benchmarks for encoding/decoding recovery codes. It also tests that encoding/decoding will fail in the correct situations. A benchmark is also added to measure the effect of locking the keys in memory. Change-Id: Ifa0bc4c08582789785cf1cdd9a4acfe76c79534f --- util/util.go | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) (limited to 'util/util.go') diff --git a/util/util.go b/util/util.go index 7574f35..dc1b85d 100644 --- a/util/util.go +++ b/util/util.go @@ -24,9 +24,72 @@ package util import ( + "io" "unsafe" ) +// ErrReader wraps an io.Reader, passing along calls to Read() until a read +// fails. Then, the error is stored, and all subsequent calls to Read() do +// nothing. This allows you to write code which has many subsequent reads and +// do all of the error checking at the end. For example: +// +// r := NewErrReader(reader) +// r.Read(foo) +// io.ReadFull(r, bar) +// if r.Err() != nil { +// // Handle error +// } +// +// Taken from https://blog.golang.org/errors-are-values by Rob Pike. +type ErrReader struct { + r io.Reader + err error +} + +// NewErrReader creates an ErrReader which wraps the provided reader. +func NewErrReader(reader io.Reader) *ErrReader { + return &ErrReader{r: reader, err: nil} +} + +// Read runs ReadFull on the wrapped reader if no errors have occurred. +// Otherwise, the previous error is just returned and no reads are attempted. +func (e *ErrReader) Read(p []byte) (n int, err error) { + if e.err == nil { + n, e.err = io.ReadFull(e.r, p) + } + return n, e.err +} + +// Err returns the first encountered err (or nil if no errors occurred). +func (e *ErrReader) Err() error { + return e.err +} + +// ErrWriter works exactly like ErrReader, except with io.Writer. +type ErrWriter struct { + w io.Writer + err error +} + +// NewErrWriter creates an ErrWriter which wraps the provided reader. +func NewErrWriter(writer io.Writer) *ErrWriter { + return &ErrWriter{w: writer, err: nil} +} + +// Write runs the wrapped writer's Write if no errors have occurred. Otherwise, +// the previous error is just returned and no writes are attempted. +func (e *ErrWriter) Write(p []byte) (n int, err error) { + if e.err == nil { + n, e.err = e.w.Write(p) + } + return n, e.err +} + +// Err returns the first encountered err (or nil if no errors occurred). +func (e *ErrWriter) Err() error { + return e.err +} + // Ptr converts an Go byte array to a pointer to the start of the array. func Ptr(slice []byte) unsafe.Pointer { return unsafe.Pointer(&slice[0]) @@ -53,3 +116,11 @@ func Lookup(inVal int64, inArray, outArray []int64) (outVal int64, ok bool) { } return outArray[index], true } + +// MinInt returns the lesser of a and b. +func MinInt(a, b int) int { + if a < b { + return a + } + return b +} -- cgit v1.2.3