-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathminisign.go
133 lines (120 loc) · 3.79 KB
/
minisign.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
125
126
127
128
129
130
131
132
133
package minisign
import (
"encoding/base64"
"errors"
"io/ioutil"
"strings"
"golang.org/x/crypto/blake2b"
"golang.org/x/crypto/ed25519"
)
type PublicKey struct {
SignatureAlgorithm [2]byte
KeyId [8]byte
PublicKey [32]byte
}
type Signature struct {
UntrustedComment string
SignatureAlgorithm [2]byte
KeyId [8]byte
Signature [64]byte
TrustedComment string
GlobalSignature [64]byte
}
func NewPublicKey(publicKeyStr string) (PublicKey, error) {
var publicKey PublicKey
bin, err := base64.StdEncoding.DecodeString(publicKeyStr)
if err != nil || len(bin) != 42 {
return publicKey, errors.New("Invalid encoded public key")
}
copy(publicKey.SignatureAlgorithm[:], bin[0:2])
copy(publicKey.KeyId[:], bin[2:10])
copy(publicKey.PublicKey[:], bin[10:42])
return publicKey, nil
}
func DecodePublicKey(in string) (PublicKey, error) {
var publicKey PublicKey
lines := strings.SplitN(in, "\n", 2)
if len(lines) < 2 {
return publicKey, errors.New("Incomplete encoded public key")
}
return NewPublicKey(lines[1])
}
func trimCarriageReturn(input string) string {
return strings.TrimRight(input, "\r")
}
func DecodeSignature(in string) (Signature, error) {
var signature Signature
lines := strings.SplitN(in, "\n", 4)
if len(lines) < 4 {
return signature, errors.New("Incomplete encoded signature")
}
signature.UntrustedComment = trimCarriageReturn(lines[0])
bin1, err := base64.StdEncoding.DecodeString(lines[1])
if err != nil || len(bin1) != 74 {
return signature, errors.New("Invalid encoded signature")
}
signature.TrustedComment = trimCarriageReturn(lines[2])
bin2, err := base64.StdEncoding.DecodeString(lines[3])
if err != nil || len(bin2) != 64 {
return signature, errors.New("Invalid encoded signature")
}
copy(signature.SignatureAlgorithm[:], bin1[0:2])
copy(signature.KeyId[:], bin1[2:10])
copy(signature.Signature[:], bin1[10:74])
copy(signature.GlobalSignature[:], bin2)
return signature, nil
}
func NewPublicKeyFromFile(file string) (PublicKey, error) {
var publicKey PublicKey
bin, err := ioutil.ReadFile(file)
if err != nil {
return publicKey, err
}
return DecodePublicKey(string(bin))
}
func NewSignatureFromFile(file string) (Signature, error) {
var signature Signature
bin, err := ioutil.ReadFile(file)
if err != nil {
return signature, err
}
return DecodeSignature(string(bin))
}
func (publicKey *PublicKey) Verify(bin []byte, signature Signature) (bool, error) {
if publicKey.SignatureAlgorithm != [2]byte{'E', 'd'} {
return false, errors.New("Incompatible signature algorithm")
}
prehashed := false
if signature.SignatureAlgorithm[0] == 0x45 && signature.SignatureAlgorithm[1] == 0x64 {
prehashed = false
} else if signature.SignatureAlgorithm[0] == 0x45 && signature.SignatureAlgorithm[1] == 0x44 {
prehashed = true
} else {
return false, errors.New("Unsupported signature algorithm")
}
if publicKey.KeyId != signature.KeyId {
return false, errors.New("Incompatible key identifiers")
}
if !strings.HasPrefix(signature.TrustedComment, "trusted comment: ") {
return false, errors.New("Unexpected format for the trusted comment")
}
if prehashed {
h, _ := blake2b.New512(nil)
h.Write(bin)
bin = h.Sum(nil)
}
if !ed25519.Verify(ed25519.PublicKey(publicKey.PublicKey[:]), bin, signature.Signature[:]) {
return false, errors.New("Invalid signature")
}
if !ed25519.Verify(ed25519.PublicKey(publicKey.PublicKey[:]), append(signature.Signature[:], []byte(signature.TrustedComment)[17:]...), signature.GlobalSignature[:]) {
return false, errors.New("Invalid global signature")
}
return true, nil
}
func (publicKey *PublicKey) VerifyFromFile(file string, signature Signature) (bool, error) {
bin, err := ioutil.ReadFile(file)
if err != nil {
return false, err
}
return publicKey.Verify(bin, signature)
}