/
auth.go
124 lines (105 loc) · 3.19 KB
/
auth.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package zcrypto
import (
"github.com/pkg/errors"
"golang.org/x/crypto/blake2b"
"golang.org/x/crypto/ed25519"
)
// AuthPair provides authentication via public/private keys
type AuthPair struct {
private ed25519.PrivateKey
public ed25519.PublicKey
raw []byte
}
// NewAuthPair generates a new public/private key pair
func NewAuthPair() (AuthPair, error) {
pub, priv, err := ed25519.GenerateKey(nil)
if err != nil {
return AuthPair{}, err
}
return AuthPair{
private: priv,
public: pub,
raw: priv,
}, nil
}
// AuthPairFromBytes creates an AuthPair from a byte slice
//
// b must be either AuthFullSize or AuthHalfSize in length
//
// If len(b) is AuthFullSize, the AuthPair will be able to sign & verify messages
//
// If len(b) is AuthHalfSize, the AuthPair will only be able to verify messages
func AuthPairFromBytes(b []byte) (AuthPair, error) {
pair := AuthPair{}
pair.raw = make([]byte, len(b))
copy(pair.raw, b[:])
if len(b) == AuthFullSize {
pair.private = ed25519.PrivateKey(pair.raw)
pair.public = ed25519.PublicKey(pair.raw[AuthHalfSize:])
} else if len(b) == AuthHalfSize {
pair.public = ed25519.PublicKey(pair.raw)
} else {
return AuthPair{}, AuthPairBadSizeError{len(b)}
}
return pair, nil
}
// Bytes returns a byte slice representing the AuthPair
func (pair AuthPair) Bytes() []byte {
if pair.private != nil {
return pair.private
}
return pair.public
}
// Sign generates a signature for a message.
// If the AuthPair does not have a private key this will return an error.
func (pair AuthPair) Sign(msg []byte) ([]byte, error) {
if pair.private == nil {
return nil, NoPrivKeyError{}
}
return ed25519.Sign(pair.private, msg), nil
}
// Verify checks a message against its signature
//
// If the signature is valid, it will return true. Otherwise, it will return false.
//
// If the AuthPair does not have a public key, it will return false
func (pair AuthPair) Verify(msg, testSig []byte) bool {
if pair.public == nil {
return false
}
return ed25519.Verify(pair.public, msg, testSig)
}
// SignFile generates a signature for a file.
// If the AuthPair does not have a private key this will return an error.
//
// SignFile first calculates the blake2b-512 hash of the file and then calls AuthPair.Sign on the resultant hash
func (pair AuthPair) SignFile(path string) ([]byte, error) {
if pair.private == nil {
return nil, NoPrivKeyError{}
}
blake, _ := blake2b.New512(nil)
fileHash, err := HashFile(path, blake)
if err != nil {
return nil, err
}
sig, err := pair.Sign(fileHash)
if err != nil {
// this should be impossible, due to the earlier check but i'll check anyways
// maybe the state of pair.private has changed from another thread?
return nil, errors.Wrap(err, "AuthPair: error signing file")
}
return sig, nil
}
// VerifyFile checks a file against its signature
//
// If the signature is valid, it will return true. Otherwise, it will return false.
//
// If the AuthPair does not have a public key, it will return false
func (pair AuthPair) VerifyFile(path string, testSig []byte) (bool, error) {
blake, _ := blake2b.New512(nil)
fileHash, err := HashFile(path, blake)
if err != nil {
return false, err
}
return pair.Verify(fileHash, testSig), nil
}