From 5af207437fd8f84c51c48ca8bfdf626f9e720ec5 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Tue, 2 May 2017 10:04:54 +0200 Subject: Rename key package --- key/key.go | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 key/key.go (limited to 'key/key.go') diff --git a/key/key.go b/key/key.go new file mode 100644 index 0000000..57978e0 --- /dev/null +++ b/key/key.go @@ -0,0 +1,148 @@ +package key + +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 Pub struct { + PKAlg [2]byte + KeyNum [8]byte + Key [ed25519.PublicKeySize]byte +} + +type Enc struct { + PKAlg [2]byte + KDFAlg [2]byte + KDFRounds uint32 + Salt [16]byte + Checksum [8]byte + KeyNum [8]byte + Key [ed25519.PrivateKeySize]byte +} + +func (v *Sig) Check() error { + if v.PKAlg != pkAlg { + return ErrInvalidPK + } + return nil +} + +func (v *Pub) Check() error { + if v.PKAlg != pkAlg { + return ErrInvalidPK + } + return nil +} + +func (v *Pub) Verify(message []byte, sig *Sig) error { + if v.KeyNum != sig.KeyNum { + return ErrKeyNum + } + if !ed25519.Verify(ed25519.PublicKey(v.Key[:]), message, sig.Sig[:]) { + return ErrInvalidSig + } + return nil +} + +func (v *Enc) Sign(message []byte) *Sig { + sig := &Sig{PKAlg: v.PKAlg, KeyNum: v.KeyNum} + copy(sig.Sig[:], ed25519.Sign(ed25519.PrivateKey(v.Key[:]), message)) + return sig +} + +func (v *Enc) Check() error { + if v.PKAlg != pkAlg { + return ErrInvalidPK + } + if v.KDFAlg != kdfAlg { + return ErrInvalidKDF + } + sum := sha512.Sum512(v.Key[:]) + if !bytes.Equal(sum[:len(v.Checksum)], v.Checksum[:]) { + return ErrInvalidKey + } + return nil +} + +func (e *Enc) Kdf(ask func() (string, error)) error { + if e.KDFRounds == 0 { + return nil + } + pass, err := ask() + if err != nil { + return err + } + xor := bhash.Pbkdf([]byte(pass), e.Salt[:], int(e.KDFRounds), len(e.Key)) + for i := range xor { + e.Key[i] ^= xor[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() (Pub, Enc, error) { + pub, sec, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + return Pub{}, Enc{}, err + } + + pubKey := Pub{PKAlg: pkAlg} + encKey := Enc{PKAlg: pkAlg, KDFAlg: kdfAlg, KDFRounds: DefaultRounds} + + copy(pubKey.Key[:], pub) + copy(encKey.Key[:], 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 +} -- cgit v1.2.3