Skip to content

Commit

Permalink
Support signature creation
Browse files Browse the repository at this point in the history
  • Loading branch information
gballet committed Dec 1, 2020
1 parent 4576602 commit 4acd739
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 1 deletion.
98 changes: 97 additions & 1 deletion minisign.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
package minisign

import (
"bytes"
"encoding/base64"
"errors"
"fmt"
"io/ioutil"
"strings"
"time"

"golang.org/x/crypto/ed25519"
"crypto/ed25519"
)

type PrivateKey struct {
SignatureAlgorithm [2]byte
KDFAlgorithm [2]byte
KDFRounds [4]byte
Salt [16]byte
Checksum [8]byte
KeyId [8]byte
PrivateKey [64]byte
}

type PublicKey struct {
SignatureAlgorithm [2]byte
KeyId [8]byte
Expand Down Expand Up @@ -115,3 +128,86 @@ func (publicKey *PublicKey) VerifyFromFile(file string, signature Signature) (bo
}
return publicKey.Verify(bin, signature)
}

func DecodePrivateKey(in string) (PrivateKey, error) {
lines := strings.SplitN(in, "\n", 2)
if len(lines) < 2 {
return PrivateKey{}, errors.New("Incomplete encoded private key")
}
return NewPrivateKey(lines[1])
}

func NewPrivateKey(in string) (PrivateKey, error) {
var privateKey PrivateKey
keydata, err := base64.StdEncoding.DecodeString(in)
if err != nil {
return privateKey, err
}

if len(keydata) != 104 {
return privateKey, errors.New("Invalid encoded secret key")
}

if string(keydata[:2]) != "Ed" {
return privateKey, errors.New("Unsupported signature scheme")
}

copy(privateKey.SignatureAlgorithm[:], keydata[:2])
copy(privateKey.KDFAlgorithm[:], keydata[2:4])
copy(privateKey.KDFRounds[:], keydata[4:8])
copy(privateKey.Salt[:], keydata[8:24])
copy(privateKey.Checksum[:], keydata[24:32])
copy(privateKey.KeyId[:], keydata[32:40])
copy(privateKey.PrivateKey[:], keydata[40:])
return privateKey, nil
}

func NewPrivateKeyFromFile(file string) (PrivateKey, error) {
var privateKey PrivateKey
bin, err := ioutil.ReadFile(file)
if err != nil {
return privateKey, err
}
return DecodePrivateKey(string(bin))
}

func commentIsMoreThanOneLine(comment string) bool {
firstLFIndex := strings.IndexByte(comment, 10)
return (firstLFIndex >= 0 && firstLFIndex < len(comment)-1)
}

func (privateKey *PrivateKey) Sign(bin []byte, unTrustedComment string, trustedComment string) ([]byte, error) {
out := bytes.NewBuffer(nil)

rawSig := ed25519.Sign(privateKey.PrivateKey[:], bin)

var sigdata []byte
sigdata = append(sigdata, privateKey.SignatureAlgorithm[:]...)
sigdata = append(sigdata, privateKey.KeyId[:]...)
sigdata = append(sigdata, rawSig...)

if unTrustedComment == "" {
return nil, errors.New("Missing untrusted comment")
}
// Check that the trusted comment fits in one line
if commentIsMoreThanOneLine(unTrustedComment) {
return nil, errors.New("Untrusted comment must fit on a single line")
}
out.WriteString(fmt.Sprintf("untrusted comment: %s\n%s\n", unTrustedComment, base64.StdEncoding.EncodeToString(sigdata)))

// Add the trusted comment if unavailable
if trustedComment == "" {
trustedComment = fmt.Sprintf("timestamp:%d", time.Now().Unix())
}
// Check that the trusted comment fits in one line
if commentIsMoreThanOneLine(trustedComment) {
return nil, errors.New("Trusted comment must fit on a single line")
}

var sigAndComment []byte
sigAndComment = append(sigAndComment, rawSig...)
sigAndComment = append(sigAndComment, []byte(trustedComment)...)
out.WriteString(fmt.Sprintf("trusted comment: %s\n%s\n", trustedComment, base64.StdEncoding.EncodeToString(ed25519.Sign(privateKey.PrivateKey[:], sigAndComment))))

return out.Bytes(), nil
}
39 changes: 39 additions & 0 deletions minisign_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package minisign

import (
"testing"
)

var (
testPKey = "untrusted comment: minisign public key\nRWTAPRW2qy9FjsBiMFGCEFv9Jk3iPhAh7tZb+VOFmtmBxDyHrFT8kZuT"
testSKey = "untrusted comment: minisign secret key\nRWRCSwAAAABVN5lr2JViGBN8DhX3/Qb/0g0wBdsNAR/APRW2qy9Fjsfr12sK2cd3URUFis1jgzQzaoayK8x4syT4G3Gvlt9RwGIwUYIQW/0mTeI+ECHu1lv5U4Wa2YHEPIesVPyRm5M="
)

func TestRoundTrip(t *testing.T) {
pkey, err := DecodePublicKey(testPKey)
if err != nil {
t.Fatalf("error decoding the public key: %v", err)
}
skey, err := DecodePrivateKey(testSKey)
if err != nil {
t.Fatalf("error decoding the private key: %v", err)
}

sig, err := skey.Sign([]byte("hello"), "verify with minisign", "")
if err != nil {
t.Fatalf("error signing: %v", err)
}

signature, err := DecodeSignature(string(sig))
if err != nil {
t.Fatalf("error when decoding signature: %v", err)
}

ok, err := pkey.Verify([]byte("hello"), signature)
if err != nil {
t.Fatalf("error verifying signature: %v", err)
}
if !ok {
t.Fatal("signature could not be verified")
}
}

0 comments on commit 4acd739

Please sign in to comment.