package signify import ( "bufio" "bytes" "encoding/base64" "errors" "fmt" "io" "io/ioutil" "os" "strings" ) type EncFile struct { Comment string Enc *EncKey } func ParseEnc(r io.Reader) (*EncFile, error) { comment, raw, _, err := read(r) if err != nil { return nil, err } enc := new(EncKey) if err := Unmarshal(raw, enc); err != nil { return nil, err } return &EncFile{Comment: comment, Enc: enc}, nil } func WriteEnc(w io.Writer, enc *EncFile) error { raw, err := Marshal(enc.Enc) if err != nil { return err } return write(w, enc.Comment, raw, nil) } type PubFile struct { Comment string Pub *PubKey } func ParsePub(r io.Reader) (*PubFile, error) { comment, raw, _, err := read(r) if err != nil { return nil, err } pub := new(PubKey) if err := Unmarshal(raw, pub); err != nil { return nil, err } return &PubFile{Comment: comment, Pub: pub}, nil } type SigFile struct { Comment string Sig *Sig Message []byte } func WritePub(w io.Writer, pub *PubFile) error { raw, err := Marshal(pub.Pub) if err != nil { return err } return write(w, pub.Comment, raw, nil) } func ParseSig(r io.Reader) (*SigFile, error) { comment, raw, message, err := read(r) if err != nil { return nil, err } sig := new(Sig) if err := Unmarshal(raw, sig); err != nil { return nil, err } return &SigFile{Comment: comment, Sig: sig, Message: message}, nil } func WriteSig(w io.Writer, sig *SigFile) error { raw, err := Marshal(sig.Sig) if err != nil { return err } return write(w, sig.Comment, raw, sig.Message) } type File struct { Comment string RawKey []byte Message []byte } const ( commentHdr = "untrusted comment:" sigFrom = "signature from %s" ) var ( ErrCommentSize = errors.New("comment line to long") ErrUntrustedComment = errors.New("expected untrusted comment") ) func checkComment(comment string) error { // compatibility with original version if len(comment) > 1024 { return ErrCommentSize } if !strings.HasPrefix(comment, commentHdr) { return ErrUntrustedComment } return nil } func (f *File) Embedded() bool { return len(f.Message) > 0 } func read(r io.Reader) (string, []byte, []byte, error) { buf := bufio.NewReader(r) comment, err := buf.ReadString('\n') if err != nil { return "", nil, nil, err } if err := checkComment(comment); err != nil { return "", nil, nil, err } comment = strings.TrimSpace(comment[len(commentHdr):]) raw, err := buf.ReadString('\n') if err != nil { return "", nil, nil, err } rawKey, err := base64.StdEncoding.DecodeString(raw) if err != nil { return "", nil, nil, err } message, err := ioutil.ReadAll(buf) if err != nil { return "", nil, nil, err } return comment, rawKey, message, nil } func (f *File) ReadFrom(r io.Reader) (err error) { f.Comment, f.RawKey, f.Message, err = read(r) if err != nil { return err } return nil } func Parse(b []byte) (*File, error) { r := bytes.NewReader(b) f := new(File) return f, f.ReadFrom(r) } func ParseFile(fname string) (*File, error) { fd, err := os.Open(fname) if err != nil { return nil, err } defer fd.Close() f := new(File) return f, f.ReadFrom(fd) } func (f File) Bytes() ([]byte, error) { buf := new(bytes.Buffer) err := f.WriteTo(buf) if err != nil { return nil, err } return buf.Bytes(), nil } func write(w io.Writer, comment string, key, message []byte) error { fmt.Fprintln(w, commentHdr, comment) fmt.Fprintln(w, base64.StdEncoding.EncodeToString(key)) if message != nil { w.Write(message) } return nil } func (f File) WriteTo(w io.Writer) error { return write(w, f.Comment, f.RawKey, f.Message) } const ( SecMode os.FileMode = 0600 PubMode os.FileMode = 0644 ) func (f File) WriteFile(fname string, perm os.FileMode) error { fd, err := os.OpenFile(fname, os.O_WRONLY|os.O_CREATE, perm) if err != nil { return err } defer fd.Close() return f.WriteTo(fd) }