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)
}
|