2018-01-13 23:52:44 +01:00
|
|
|
package minisign
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/base64"
|
|
|
|
"errors"
|
|
|
|
"io/ioutil"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"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 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 = lines[0]
|
|
|
|
bin1, err := base64.StdEncoding.DecodeString(lines[1])
|
|
|
|
if err != nil || len(bin1) != 74 {
|
|
|
|
return signature, errors.New("Invalid encoded signature")
|
|
|
|
}
|
|
|
|
signature.TrustedComment = 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 != signature.SignatureAlgorithm {
|
|
|
|
return false, errors.New("Incompatible signature algorithm")
|
|
|
|
}
|
2019-04-28 23:19:52 +02:00
|
|
|
if signature.SignatureAlgorithm[0] != 0x45 || signature.SignatureAlgorithm[1] != 0x64 {
|
2018-01-13 23:52:44 +01:00
|
|
|
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 !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)
|
|
|
|
}
|