From eae17147e143094e48050494b2da570f42d21986 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Mon, 1 May 2017 16:57:53 +0200 Subject: Reorder package --- signify/file.go | 220 ++++++++++++++++++++++++++++++++++++++++ signify/file_test.go | 42 ++++++++ signify/keys.go | 148 +++++++++++++++++++++++++++ signify/keys_test.go | 69 +++++++++++++ signify/testdata/dim13.pub | 2 + signify/testdata/dim13.sec | 2 + signify/testdata/isc.txt.gz | Bin 0 -> 501 bytes signify/testdata/isc.txt.gz.sig | Bin 0 -> 775 bytes signify/testdata/kdf.pub | 2 + signify/testdata/kdf.sec | 2 + signify/testdata/kdf.txt | 1 + signify/testdata/key.pub | 2 + signify/testdata/key.sec | 2 + signify/testdata/key.txt | 1 + signify/testdata/key.txt.sig | 3 + signify/testdata/test | 1 + signify/testdata/test.sig | 3 + 17 files changed, 500 insertions(+) create mode 100644 signify/file.go create mode 100644 signify/file_test.go create mode 100644 signify/keys.go create mode 100644 signify/keys_test.go create mode 100644 signify/testdata/dim13.pub create mode 100644 signify/testdata/dim13.sec create mode 100644 signify/testdata/isc.txt.gz create mode 100644 signify/testdata/isc.txt.gz.sig create mode 100644 signify/testdata/kdf.pub create mode 100644 signify/testdata/kdf.sec create mode 100644 signify/testdata/kdf.txt create mode 100644 signify/testdata/key.pub create mode 100644 signify/testdata/key.sec create mode 100644 signify/testdata/key.txt create mode 100644 signify/testdata/key.txt.sig create mode 100644 signify/testdata/test create mode 100644 signify/testdata/test.sig (limited to 'signify') diff --git a/signify/file.go b/signify/file.go new file mode 100644 index 0000000..8c956ae --- /dev/null +++ b/signify/file.go @@ -0,0 +1,220 @@ +package signify + +import ( + "bufio" + "bytes" + "encoding/base64" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "strings" + + "dim13.org/signify/sig" +) + +type File struct { + Comment string + RawKey []byte + Message []byte +} + +const commentHdr = "untrusted comment:" + +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) +} + +//////////////////////////////////////////////////////////////////////// + +func WriteEnc(fname, comment string, key *EncKey) error { + fd, err := os.OpenFile(fname, os.O_WRONLY|os.O_CREATE, SecMode) + if err != nil { + return err + } + defer fd.Close() + b, err := Marshal(key) + if err != nil { + return err + } + block := &sig.Block{ + Comment: comment + " secret key", + Bytes: b, + } + return sig.Encode(fd, block) +} + +func WritePub(fname, comment string, key *PubKey) error { + fd, err := os.OpenFile(fname, os.O_WRONLY|os.O_CREATE, PubMode) + if err != nil { + return err + } + defer fd.Close() + b, err := Marshal(key) + if err != nil { + return err + } + block := &sig.Block{ + Comment: comment + " public key", + Bytes: b, + } + return sig.Encode(fd, block) +} + +func WriteSig(fname, comment string, message []byte, key *Sig) error { + fd, err := os.OpenFile(fname, os.O_WRONLY|os.O_CREATE, PubMode) + if err != nil { + return err + } + defer fd.Close() + b, err := Marshal(key) + if err != nil { + return err + } + block := &sig.Block{ + Comment: "verify with " + comment, + Bytes: b, + Message: message, + } + return sig.Encode(fd, block) +} + +//////////////////////////////////////////////////////////////////////// + +func OpenEnc(fname string) (*EncKey, error) { + block, err := sig.DecodeFile(fname) + if err != nil { + return nil, err + } + key := new(EncKey) + if err := Unmarshal(block.Bytes, key); err != nil { + return nil, err + } + return key, nil +} + +func OpenPub(fname string) (*PubKey, error) { + block, err := sig.DecodeFile(fname) + if err != nil { + return nil, err + } + key := new(PubKey) + if err := Unmarshal(block.Bytes, key); err != nil { + return nil, err + } + return key, nil +} + +func OpenSig(fname string) (*Sig, error) { + block, err := sig.DecodeFile(fname) + if err != nil { + return nil, err + } + key := new(Sig) + if err := Unmarshal(block.Bytes, key); err != nil { + return nil, err + } + return key, nil +} diff --git a/signify/file_test.go b/signify/file_test.go new file mode 100644 index 0000000..092c36c --- /dev/null +++ b/signify/file_test.go @@ -0,0 +1,42 @@ +package signify + +import ( + "bytes" + "io/ioutil" + "path" + "testing" +) + +func TestParseFile(t *testing.T) { + testCases := []string{ + "dim13.sec", + "dim13.pub", + "test.sig", + "kdf.sec", + "kdf.pub", + } + for _, tc := range testCases { + t.Run(tc, func(t *testing.T) { + fileName := path.Join("testdata", tc) + + body, err := ioutil.ReadFile(fileName) + if err != nil { + t.Error(err) + } + + f, err := Parse(body) + if err != nil { + t.Error(err) + } + + res, err := f.Bytes() + if err != nil { + t.Error(err) + } + + if !bytes.Equal(res, body) { + t.Errorf("got %s, want %s", res, body) + } + }) + } +} diff --git a/signify/keys.go b/signify/keys.go new file mode 100644 index 0000000..b489385 --- /dev/null +++ b/signify/keys.go @@ -0,0 +1,148 @@ +package signify + +import ( + "bytes" + "crypto/rand" + "crypto/sha512" + "encoding/binary" + "errors" + + "dim13.org/signify/bhash" + + "golang.org/x/crypto/ed25519" +) + +const DefaultRounds = 42 + +var ( + ErrInvalidPK = errors.New("unsupported format") + ErrInvalidKDF = errors.New("unsupported KDF") + ErrPassphrase = errors.New("incorrect passphrase") + ErrInvalidKey = errors.New("invalid key") + ErrKeyNum = errors.New("verification failed: checked against wrong key") + ErrInvalidSig = errors.New("signature verfication failed") +) + +var ( + pkAlg = [2]byte{'E', 'd'} + kdfAlg = [2]byte{'B', 'K'} +) + +type Sig struct { + PKAlg [2]byte + KeyNum [8]byte + Sig [ed25519.SignatureSize]byte +} + +type PubKey struct { + PKAlg [2]byte + KeyNum [8]byte + PubKey [ed25519.PublicKeySize]byte +} + +type EncKey struct { + PKAlg [2]byte + KDFAlg [2]byte + KDFRounds uint32 + Salt [16]byte + Checksum [8]byte + KeyNum [8]byte + SecKey [ed25519.PrivateKeySize]byte +} + +func (v *Sig) Check() error { + if v.PKAlg != pkAlg { + return ErrInvalidPK + } + return nil +} + +func (v *PubKey) Check() error { + if v.PKAlg != pkAlg { + return ErrInvalidPK + } + return nil +} + +func (v *PubKey) Verify(message []byte, sig *Sig) error { + if v.KeyNum != sig.KeyNum { + return ErrKeyNum + } + if !ed25519.Verify(ed25519.PublicKey(v.PubKey[:]), message, sig.Sig[:]) { + return ErrInvalidSig + } + return nil +} + +func (v *EncKey) Sign(message []byte) *Sig { + sig := &Sig{PKAlg: v.PKAlg, KeyNum: v.KeyNum} + copy(sig.Sig[:], ed25519.Sign(ed25519.PrivateKey(v.SecKey[:]), message)) + return sig +} + +func (v *EncKey) Check() error { + if v.PKAlg != pkAlg { + return ErrInvalidPK + } + if v.KDFAlg != kdfAlg { + return ErrInvalidKDF + } + sum := sha512.Sum512(v.SecKey[:]) + if !bytes.Equal(sum[:len(v.Checksum)], v.Checksum[:]) { + return ErrInvalidKey + } + return nil +} + +func (e *EncKey) Kdf(ask func() (string, error)) error { + if e.KDFRounds == 0 { + return nil + } + pass, err := ask() + if err != nil { + return err + } + xorkey := bhash.Pbkdf([]byte(pass), e.Salt[:], int(e.KDFRounds), len(e.SecKey)) + for i := range xorkey { + e.SecKey[i] ^= xorkey[i] + } + return e.Check() +} + +func Unmarshal(b []byte, v interface{}) error { + buf := bytes.NewReader(b) + if err := binary.Read(buf, binary.BigEndian, v); err != nil { + return err + } + return nil +} + +func Marshal(v interface{}) ([]byte, error) { + buf := new(bytes.Buffer) + if err := binary.Write(buf, binary.BigEndian, v); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func NewKey() (PubKey, EncKey, error) { + pub, sec, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + return PubKey{}, EncKey{}, err + } + + pubKey := PubKey{PKAlg: pkAlg} + encKey := EncKey{PKAlg: pkAlg, KDFAlg: kdfAlg, KDFRounds: DefaultRounds} + + copy(pubKey.PubKey[:], pub) + copy(encKey.SecKey[:], sec) + + checkSum := sha512.Sum512(sec) + copy(encKey.Checksum[:], checkSum[:len(encKey.Checksum)]) + + rand.Read(encKey.Salt[:]) + rand.Read(encKey.KeyNum[:]) + pubKey.KeyNum = encKey.KeyNum + + return pubKey, encKey, nil +} diff --git a/signify/keys_test.go b/signify/keys_test.go new file mode 100644 index 0000000..4788f90 --- /dev/null +++ b/signify/keys_test.go @@ -0,0 +1,69 @@ +package signify + +import ( + "bytes" + "encoding/base64" + "testing" +) + +func decode(s string) ([]byte, error) { + return base64.StdEncoding.DecodeString(s) +} + +func TestUnmarshalSig(t *testing.T) { + raw, err := decode("RWRbOC0bBf7abaGwGtq45KLDK63tgcF7CO4qTZSlTKCSbZTYlDfFm/DISQ60u+/jEzrk22suvXXAEsxQTe2xUOfV90get1YRGQo=") + if err != nil { + t.Fatal(err) + } + v := new(Sig) + Unmarshal(raw, v) + out, _ := Marshal(v) + if !bytes.Equal(raw, out) { + t.Errorf("want %v, got %v", raw, out) + } +} + +func TestUnmarshalPub(t *testing.T) { + raw, err := decode("RWRbOC0bBf7abfanaXuTYfCa6+YO69Kxyz8RD5nL/3Ta7umY6iOwnBrG") + if err != nil { + t.Fatal(err) + } + v := new(PubKey) + Unmarshal(raw, v) + out, _ := Marshal(v) + if !bytes.Equal(raw, out) { + t.Errorf("want %v, got %v", raw, out) + } +} + +func TestUnmarshalEnc(t *testing.T) { + raw, err := decode("RWRCSwAAAACzJBN2gC5//jVvDiV76rs4m2aKXkljqDpbOC0bBf7abZhV/Zygr6b0KIbSI56JQutwzsQeouxnnHuVTZp3IW4M9qdpe5Nh8Jrr5g7r0rHLPxEPmcv/dNru6ZjqI7CcGsY=") + if err != nil { + t.Fatal(err) + } + v := new(EncKey) + Unmarshal(raw, v) + out, _ := Marshal(v) + if !bytes.Equal(raw, out) { + t.Errorf("want %v, got %v", raw, out) + } + if err := v.Kdf(func() (string, error) { return "", nil }); err != nil { + t.Error(err) + } +} + +func TestUnmarshalEncKDF(t *testing.T) { + raw, err := decode("RWRCSwAAACoXv4r2lp3RYYLEWZRsY+1Z+1mJtEScNBaKdOKcMdhUHrztnf8a4sUNGY19MoV3wX2cyW2Mn1MduKxi9s3Se070TGF0IZG/hH4SKiNUYi+yi1mandWAwmhY3ahIHApigTk=") + if err != nil { + t.Fatal(err) + } + v := new(EncKey) + Unmarshal(raw, v) + out, _ := Marshal(v) + if !bytes.Equal(raw, out) { + t.Errorf("want %v, got %v", raw, out) + } + if err := v.Kdf(func() (string, error) { return "test", nil }); err != nil { + t.Error(err) + } +} diff --git a/signify/testdata/dim13.pub b/signify/testdata/dim13.pub new file mode 100644 index 0000000..0efc253 --- /dev/null +++ b/signify/testdata/dim13.pub @@ -0,0 +1,2 @@ +untrusted comment: dim13.org public key +RWRbOC0bBf7abfanaXuTYfCa6+YO69Kxyz8RD5nL/3Ta7umY6iOwnBrG diff --git a/signify/testdata/dim13.sec b/signify/testdata/dim13.sec new file mode 100644 index 0000000..51e0d28 --- /dev/null +++ b/signify/testdata/dim13.sec @@ -0,0 +1,2 @@ +untrusted comment: dim13.org secret key +RWRCSwAAAACzJBN2gC5//jVvDiV76rs4m2aKXkljqDpbOC0bBf7abZhV/Zygr6b0KIbSI56JQutwzsQeouxnnHuVTZp3IW4M9qdpe5Nh8Jrr5g7r0rHLPxEPmcv/dNru6ZjqI7CcGsY= diff --git a/signify/testdata/isc.txt.gz b/signify/testdata/isc.txt.gz new file mode 100644 index 0000000..9800c17 Binary files /dev/null and b/signify/testdata/isc.txt.gz differ diff --git a/signify/testdata/isc.txt.gz.sig b/signify/testdata/isc.txt.gz.sig new file mode 100644 index 0000000..0b2ab0e Binary files /dev/null and b/signify/testdata/isc.txt.gz.sig differ diff --git a/signify/testdata/kdf.pub b/signify/testdata/kdf.pub new file mode 100644 index 0000000..abc3893 --- /dev/null +++ b/signify/testdata/kdf.pub @@ -0,0 +1,2 @@ +untrusted comment: signify public key +RWSKdOKcMdhUHtD9TWgPRZnRRTwl2WTeRpEZLCjtf4TTS24EfqT7uoPz diff --git a/signify/testdata/kdf.sec b/signify/testdata/kdf.sec new file mode 100644 index 0000000..b69b5e8 --- /dev/null +++ b/signify/testdata/kdf.sec @@ -0,0 +1,2 @@ +untrusted comment: signify secret key +RWRCSwAAACoXv4r2lp3RYYLEWZRsY+1Z+1mJtEScNBaKdOKcMdhUHrztnf8a4sUNGY19MoV3wX2cyW2Mn1MduKxi9s3Se070TGF0IZG/hH4SKiNUYi+yi1mandWAwmhY3ahIHApigTk= diff --git a/signify/testdata/kdf.txt b/signify/testdata/kdf.txt new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/signify/testdata/kdf.txt @@ -0,0 +1 @@ +test diff --git a/signify/testdata/key.pub b/signify/testdata/key.pub new file mode 100644 index 0000000..07f9107 --- /dev/null +++ b/signify/testdata/key.pub @@ -0,0 +1,2 @@ +untrusted comment: signify public key +RWTkgMtoLot+Y1KOfKO8Q4JMnmMp40AwMqkSU++oUOVVloMtv/Y0tDbe diff --git a/signify/testdata/key.sec b/signify/testdata/key.sec new file mode 100644 index 0000000..32abaed --- /dev/null +++ b/signify/testdata/key.sec @@ -0,0 +1,2 @@ +untrusted comment: signify secret key +RWRCSwAAACpjn/Y5du0k4LT23ConAYrBM6A/ML4cEQrkgMtoLot+YyzUwyr5f1hUz8W0pkzPx+wZz51Z2oyM6gf/PaG7KPzyO/6VC0l29ZzLUfQGVoOGGKZxuPA6k8iMHvqONFGQ7hA= diff --git a/signify/testdata/key.txt b/signify/testdata/key.txt new file mode 100644 index 0000000..1a4db74 --- /dev/null +++ b/signify/testdata/key.txt @@ -0,0 +1 @@ +Password: test diff --git a/signify/testdata/key.txt.sig b/signify/testdata/key.txt.sig new file mode 100644 index 0000000..c8326bd --- /dev/null +++ b/signify/testdata/key.txt.sig @@ -0,0 +1,3 @@ +untrusted comment: verify with key.pub +RWTkgMtoLot+Y4mRk5LYyq04fwaGaMeFxbvyZLEA+xxv0TmHQzpmirMxTgSp9D9jT+rtW2d1X9VKK3mBoIKZNMatkdBsU1gKCgc= +Password: test diff --git a/signify/testdata/test b/signify/testdata/test new file mode 100644 index 0000000..557db03 --- /dev/null +++ b/signify/testdata/test @@ -0,0 +1 @@ +Hello World diff --git a/signify/testdata/test.sig b/signify/testdata/test.sig new file mode 100644 index 0000000..1a1bdf0 --- /dev/null +++ b/signify/testdata/test.sig @@ -0,0 +1,3 @@ +untrusted comment: verify with dim13.pub +RWRbOC0bBf7abaGwGtq45KLDK63tgcF7CO4qTZSlTKCSbZTYlDfFm/DISQ60u+/jEzrk22suvXXAEsxQTe2xUOfV90get1YRGQo= +Hello World -- cgit v1.2.3