// Package b64file implements signify file format package b64file import ( "bufio" "encoding" "encoding/base64" "errors" "fmt" "io" "io/ioutil" "strings" ) const untrusted = "untrusted comment: " // Original Error: "invalid comment in %s; must start with 'untrusted comment: '" var ( ErrUntrusted = errors.New("comment must start with 'untrusted comment: '") ErrMissingHeader = errors.New("missing required header fields") ) type Header struct { Comment string Bytes []byte } type Reader struct { Header r io.Reader } type Writer struct { Header w io.Writer wroteHeader bool } func NewReader(r io.Reader) (*Reader, error) { buf := bufio.NewReader(r) comment, err := buf.ReadString('\n') if err != nil { return nil, err } if !strings.HasPrefix(comment, untrusted) { return nil, ErrUntrusted } comment = strings.TrimSpace(comment[len(untrusted):]) raw, err := buf.ReadString('\n') if err != nil { return nil, err } b, err := base64.StdEncoding.DecodeString(raw) if err != nil { return nil, err } return &Reader{ Header: Header{ Comment: comment, Bytes: b, }, r: buf, }, nil } func (r *Reader) Read(p []byte) (n int, err error) { return r.r.Read(p) } func (r *Reader) Close() error { return nil } func Decode(r io.Reader, u encoding.BinaryUnmarshaler) (string, []byte, error) { buf := bufio.NewReader(r) comment, err := buf.ReadString('\n') if err != nil { return "", nil, err } if !strings.HasPrefix(comment, untrusted) { return "", nil, ErrUntrusted } comment = strings.TrimSpace(comment[len(untrusted):]) raw, err := buf.ReadString('\n') if err != nil { return "", nil, err } b, err := base64.StdEncoding.DecodeString(raw) if err != nil { return "", nil, err } if err := u.UnmarshalBinary(b); err != nil { return "", nil, err } message, err := ioutil.ReadAll(buf) if err != nil { return "", nil, err } return comment, message, nil } func NewWriter(w io.Writer) (*Writer, error) { return &Writer{w: w}, nil } func (w *Writer) writeHeader() error { if !w.wroteHeader { if w.Comment == "" || w.Bytes == nil { return ErrMissingHeader } fmt.Fprintf(w.w, "%s%s\n", untrusted, w.Comment) fmt.Fprintf(w.w, "%s\n", base64.StdEncoding.EncodeToString(w.Bytes)) w.wroteHeader = true } return nil } func (w *Writer) Write(p []byte) (n int, err error) { if err := w.writeHeader(); err != nil { return 0, err } return w.w.Write(p) } func (w *Writer) Close() error { return w.writeHeader() } func Encode(w io.Writer, u encoding.BinaryMarshaler, comment string, msg []byte) error { raw, err := u.MarshalBinary() if err != nil { return err } fmt.Fprintf(w, "%s%s\n", untrusted, comment) fmt.Fprintf(w, "%s\n", base64.StdEncoding.EncodeToString(raw)) w.Write(msg) return nil }