Move minisign to util/

This commit is contained in:
moloch--
2023-11-10 15:31:35 -07:00
parent 21468a6310
commit eddfa20684
34 changed files with 13 additions and 1432 deletions
+1 -1
View File
@@ -36,7 +36,7 @@ import (
"github.com/bishopfox/sliver/client/command/extensions"
"github.com/bishopfox/sliver/client/command/settings"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/server/cryptography/minisign"
"github.com/bishopfox/sliver/util/minisign"
)
// ArmoryIndex - Index JSON containing alias/extension/bundle information
+1 -1
View File
@@ -32,7 +32,7 @@ import (
"github.com/bishopfox/sliver/client/command/extensions"
"github.com/bishopfox/sliver/client/console"
"github.com/bishopfox/sliver/client/constants"
"github.com/bishopfox/sliver/server/cryptography/minisign"
"github.com/bishopfox/sliver/util/minisign"
)
// ErrPackageNotFound - The package was not found
+1 -1
View File
@@ -33,7 +33,7 @@ import (
"time"
"github.com/bishopfox/sliver/client/assets"
"github.com/bishopfox/sliver/server/cryptography/minisign"
"github.com/bishopfox/sliver/util/minisign"
)
// ArmoryIndexParser - Generic interface to fetch armory indexes
+8 -8
View File
@@ -67,6 +67,14 @@ else
exit 1
fi
# util / minisign
if go test -tags=server,$TAGS ./util/minisign ; then
:
else
cat ~/.sliver/logs/sliver.log
exit 1
fi
## Implant
# implant / sliver / extension
@@ -131,14 +139,6 @@ else
exit 1
fi
# server / cryptography / minisign
if go test -tags=server,$TAGS ./server/cryptography/minisign ; then
:
else
cat ~/.sliver/logs/sliver.log
exit 1
fi
# server / gogo
if go test -tags=server,$TAGS ./server/gogo ; then
:
+1 -1
View File
@@ -36,9 +36,9 @@ import (
"sync"
"filippo.io/age"
"github.com/bishopfox/sliver/server/cryptography/minisign"
"github.com/bishopfox/sliver/server/db"
"github.com/bishopfox/sliver/util/encoders"
"github.com/bishopfox/sliver/util/minisign"
"golang.org/x/crypto/chacha20poly1305"
)
+1 -1
View File
@@ -27,7 +27,7 @@ import (
"testing"
implantCrypto "github.com/bishopfox/sliver/implant/sliver/cryptography"
"github.com/bishopfox/sliver/server/cryptography/minisign"
"github.com/bishopfox/sliver/util/minisign"
)
var (
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2021 Andreas Auernhammer
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -1 +0,0 @@
Hello World!
@@ -1,4 +0,0 @@
untrusted comment: signature from minisign secret key
RWRQhGcHOBlzwxrJCyuC+rJfHSfyRKRxkuwa3JJ0bWEs7RHjL1OUmqnTr+V1B9JzFuJIH/ybR2Eus9oEZKt9RbitpF/L4D3+5wg=
trusted comment: timestamp:1614549543 file:message.txt
P/722+ynQ+tIy0qadFHwLx5MsyNz/jDKJkDWQj4dDD2OKnVte8m/M14mwPE/1NMwzShPMSBhMXqZGdbe+UZjDg==
@@ -1,2 +0,0 @@
untrusted comment: minisign encrypted secret key
RWRTY0Iytaz5znJmUO5kBt5xVkvpBl+29A7pZH86phD4h8vD3V8AAAACAAAAAAAAAEAAAAAA9vH9EcS6NdXNIEGhYGoqG1CiL4aptyJreJ4IfuT4+1h+OgVaY/vi0HsbCP0Y6n/wcy0AN0wOXmVDPP33jZqv82YCj2fH+/6MRuAfzNQYoLvc3sH/8bIwqdfpKIjDRZhvqRf063RFYoI=
@@ -1,2 +0,0 @@
untrusted comment: minisign public key C373193807678450
RWRQhGcHOBlzw4CoKyugkk4ioDfoxlXxC9LBx+VNhJ3w9w+cAxgvPsuo
@@ -1,4 +0,0 @@
untrusted comment: signature from minisign secret key
RWQ3ly9IPenQ6XE4gvV0tpJPSRdw/Si+Q4r97LbpLj0Hb3sV+XFydynJg3iFT2PjIlE3xViNOmFT9XrIoidedDr41+Ly0AYbUQg=
trusted comment: timestamp:1617721023 file:robtest.ps1
HkxuqHSvipJo/unNKgDS+JGDB0+Q5d8nOeoJ0NGOnKBNsNdvAj8FWf7fhaPV7mzRJ1ooLvYpI0yUsD7lpaDwBQ==
-267
View File
@@ -1,267 +0,0 @@
// Copyright (c) 2021 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
// Modifications moloch--
// Package minisign implements the minisign signature scheme.
package minisign
import (
"bytes"
"crypto/ed25519"
"encoding/binary"
"hash"
"io"
"strconv"
"strings"
"time"
"golang.org/x/crypto/blake2b"
)
const (
// EdDSA refers to the Ed25519 signature scheme.
//
// Minisign uses this signature scheme to sign and
// verify (non-hashed) messages.
EdDSA uint16 = 0x6445
// HashEdDSA refers to a Ed25519 signature scheme
// with pre-hashed messages.
//
// Minisign uses this signature scheme to sign and
// verify message that don't fit into memory.
HashEdDSA uint16 = 0x4445
RawSigSize = 2 + 8 + ed25519.SignatureSize
)
// GenerateKey generates a public/private key pair using entropy
// from random. If random is nil, crypto/rand.Reader will be used.
func GenerateKey(random io.Reader) (PublicKey, PrivateKey, error) {
pub, priv, err := ed25519.GenerateKey(random)
if err != nil {
return PublicKey{}, PrivateKey{}, err
}
id := blake2b.Sum256(pub[:])
publicKey := PublicKey{
id: binary.LittleEndian.Uint64(id[:8]),
}
copy(publicKey.bytes[:], pub)
privateKey := PrivateKey{
RawID: publicKey.ID(),
}
copy(privateKey.RawBytes[:], priv)
return publicKey, privateKey, nil
}
// Reader is an io.Reader that reads a message
// while, at the same time, computes its digest.
//
// At any point, typically at the end of the message,
// Reader can sign the message digest with a private
// key or try to verify the message with a public key
// and signature.
type Reader struct {
message io.Reader
hash hash.Hash
}
// NewReader returns a new Reader that reads from r
// and computes a digest of the read data.
func NewReader(r io.Reader) *Reader {
h, err := blake2b.New512(nil)
if err != nil {
panic(err)
}
return &Reader{
message: r,
hash: h,
}
}
// Read reads from the underlying io.Reader as specified
// by the io.Reader interface.
func (r *Reader) Read(p []byte) (int, error) {
n, err := r.message.Read(p)
r.hash.Write(p[:n])
return n, err
}
// Sign signs whatever has been read from the underlying
// io.Reader up to this point in time with the given private
// key.
//
// It behaves like SignWithComments but uses some generic comments.
func (r *Reader) Sign(privateKey PrivateKey) []byte {
var (
trustedComment = strconv.FormatInt(time.Now().Unix(), 10)
)
return r.SignWithComments(privateKey, trustedComment, "")
}
// SignWithComments signs whatever has been read from the underlying
// io.Reader up to this point in time with the given private key.
//
// The trustedComment as well as the untrustedComment are embedded into the
// returned signature. The trustedComment is signed and will be checked
// when the signature is verified. The untrustedComment is not signed and
// must not be trusted.
//
// SignWithComments computes the digest as a snapshot. So, it is possible
// to create multiple signatures of different message prefixes by reading
// up to a certain byte, signing this message prefix, and then continue
// reading.
func (r *Reader) SignWithComments(privateKey PrivateKey, trustedComment, untrustedComment string) []byte {
const isHashed = true
return sign(privateKey, r.hash.Sum(nil), trustedComment, untrustedComment, isHashed)
}
// Verify checks whether whatever has been read from the underlying
// io.Reader up to this point in time is authentic by verifying it
// with the given public key and signature.
//
// Verify computes the digest as a snapshot. Therefore, Verify can
// verify any signature produced by Sign or SignWithComments,
// including signatures of partial messages, given the correct
// public key and signature.
func (r *Reader) Verify(publicKey PublicKey, signature []byte) bool {
const isHashed = true
return verify(publicKey, r.hash.Sum(nil), signature, isHashed)
}
// Sign signs the given message with the private key.
//
// It behaves like SignWithComments with some generic comments.
func Sign(privateKey PrivateKey, message []byte) []byte {
var (
trustedComment = strconv.FormatInt(time.Now().Unix(), 10)
)
return SignWithComments(privateKey, message, trustedComment, "")
}
// SignRawBuf - Sign buffer with raw signature
func SignRawBuf(privateKey PrivateKey, message []byte) [RawSigSize]byte {
return signRaw(privateKey, message, false)
}
// VerifyRawBuf - Verify buffer with raw signature
func VerifyRawBuf(publicKey PublicKey, rawMessage []byte) bool {
return verifyRaw(publicKey, rawMessage, false)
}
// SignWithComments signs the given message with the private key.
//
// The trustedComment as well as the untrustedComment are embedded
// into the returned signature. The trustedComment is signed and
// will be checked when the signature is verified.
// The untrustedComment is not signed and must not be trusted.
func SignWithComments(privateKey PrivateKey, message []byte, trustedComment, untrustedComment string) []byte {
const isHashed = false
return sign(privateKey, message, trustedComment, untrustedComment, isHashed)
}
// Verify checks whether message is authentic by verifying
// it with the given public key and signature. It returns
// true if and only if the signature verification is successful.
func Verify(publicKey PublicKey, message, signature []byte) bool {
const isHashed = false
return verify(publicKey, message, signature, isHashed)
}
func signRaw(privateKey PrivateKey, message []byte, isHashed bool) [RawSigSize]byte {
var algorithm = EdDSA
if isHashed {
algorithm = HashEdDSA
}
var (
msgSignature = ed25519.Sign(ed25519.PrivateKey(privateKey.RawBytes[:]), message)
)
var rawSig [RawSigSize]byte
binary.LittleEndian.PutUint16(rawSig[:2], algorithm)
binary.LittleEndian.PutUint64(rawSig[2:10], privateKey.ID())
copy(rawSig[10:], msgSignature[:])
return rawSig
}
func verifyRaw(publicKey PublicKey, rawMessage []byte, isHashed bool) bool {
if len(rawMessage) < RawSigSize+1 {
return false
}
rawSigBuf := rawMessage[:RawSigSize]
algorithm := binary.LittleEndian.Uint16(rawSigBuf[:2])
keyID := binary.LittleEndian.Uint64(rawSigBuf[2:10])
signature := rawSigBuf[10:]
message := rawMessage[RawSigSize:]
if keyID != publicKey.ID() {
return false
}
if algorithm == HashEdDSA && !isHashed {
h := blake2b.Sum512(message)
message = h[:]
}
if !ed25519.Verify(ed25519.PublicKey(publicKey.bytes[:]), message, signature[:]) {
return false
}
return ed25519.Verify(ed25519.PublicKey(publicKey.bytes[:]), message, signature[:])
}
func sign(privateKey PrivateKey, message []byte, trustedComment, untrustedComment string, isHashed bool) []byte {
var algorithm = EdDSA
if isHashed {
algorithm = HashEdDSA
}
var (
msgSignature = ed25519.Sign(ed25519.PrivateKey(privateKey.RawBytes[:]), message)
commentSignature = ed25519.Sign(ed25519.PrivateKey(privateKey.RawBytes[:]), append(msgSignature, []byte(trustedComment)...))
)
signature := Signature{
Algorithm: algorithm,
KeyID: privateKey.ID(),
TrustedComment: trustedComment,
UntrustedComment: untrustedComment,
}
copy(signature.Signature[:], msgSignature)
copy(signature.CommentSignature[:], commentSignature)
text, err := signature.MarshalText()
if err != nil {
panic(err)
}
return text
}
func verify(publicKey PublicKey, message, signature []byte, isHashed bool) bool {
var s Signature
if err := s.UnmarshalText(signature); err != nil {
return false
}
if s.KeyID != publicKey.ID() {
return false
}
if s.Algorithm == HashEdDSA && !isHashed {
h := blake2b.Sum512(message)
message = h[:]
}
if !ed25519.Verify(ed25519.PublicKey(publicKey.bytes[:]), message, s.Signature[:]) {
return false
}
globalMessage := append(s.Signature[:], []byte(s.TrustedComment)...)
return ed25519.Verify(ed25519.PublicKey(publicKey.bytes[:]), globalMessage, s.CommentSignature[:])
}
// trimUntrustedComment returns text with a potential
// untrusted comment line.
func trimUntrustedComment(text []byte) []byte {
s := bytes.SplitN(text, []byte{'\n'}, 2)
if len(s) == 2 && strings.HasPrefix(string(s[0]), "untrusted comment: ") {
return s[1]
}
return s[0]
}
@@ -1,63 +0,0 @@
// Copyright (c) 2021 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
package minisign
import (
"io"
"os"
"testing"
)
func TestRoundtrip(t *testing.T) {
const Password = "correct horse battery staple"
privateKey, err := PrivateKeyFromFile(Password, "./internal/testdata/minisign.key")
if err != nil {
t.Fatalf("Failed to load private key: %v", err)
}
message, err := os.ReadFile("./internal/testdata/message.txt")
if err != nil {
t.Fatalf("Failed to load message: %v", err)
}
signature := Sign(privateKey, message)
publicKey, err := PublicKeyFromFile("./internal/testdata/minisign.pub")
if err != nil {
t.Fatalf("Failed to load public key: %v", err)
}
if !Verify(publicKey, message, signature) {
t.Fatalf("Verification failed: signature %q - public key %q", signature, publicKey)
}
}
func TestReaderRoundtrip(t *testing.T) {
const Password = "correct horse battery staple"
privateKey, err := PrivateKeyFromFile(Password, "./internal/testdata/minisign.key")
if err != nil {
t.Fatalf("Failed to load private key: %v", err)
}
file, err := os.Open("./internal/testdata/message.txt")
if err != nil {
t.Fatalf("Failed to open message: %v", err)
}
defer file.Close()
reader := NewReader(file)
if _, err = io.Copy(io.Discard, reader); err != nil {
t.Fatalf("Failed to read message: %v", err)
}
signature := reader.Sign(privateKey)
publicKey, err := PublicKeyFromFile("./internal/testdata/minisign.pub")
if err != nil {
t.Fatalf("Failed to load public key: %v", err)
}
if !reader.Verify(publicKey, signature) {
t.Fatalf("Verification failed: signature %q - public key %q", signature, publicKey)
}
}
-319
View File
@@ -1,319 +0,0 @@
// Copyright (c) 2021 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
package minisign
import (
"crypto"
"crypto/ed25519"
"crypto/rand"
"crypto/subtle"
"encoding/base64"
"encoding/binary"
"errors"
"io"
"os"
"strconv"
"strings"
"time"
"golang.org/x/crypto/blake2b"
"golang.org/x/crypto/scrypt"
)
// PrivateKeyFromFile reads and decrypts the private key
// file with the given password.
func PrivateKeyFromFile(password, path string) (PrivateKey, error) {
bytes, err := os.ReadFile(path)
if err != nil {
return PrivateKey{}, err
}
return DecryptKey(password, bytes)
}
// PrivateKey is a minisign private key.
//
// A private key can sign messages to prove the
// their origin and authenticity.
//
// PrivateKey implements the crypto.Signer interface.
type PrivateKey struct {
_ [0]func() // prevent direct comparison: p1 == p2.
RawID uint64
RawBytes [ed25519.PrivateKeySize]byte
}
func (p PrivateKey) Bytes() []byte {
return p.RawBytes[:]
}
var _ crypto.Signer = (*PrivateKey)(nil) // compiler check
// ID returns the 64 bit key ID.
func (p PrivateKey) ID() uint64 { return p.RawID }
// Public returns the corresponding public key.
func (p PrivateKey) Public() crypto.PublicKey {
var bytes [ed25519.PublicKeySize]byte
copy(bytes[:], p.RawBytes[32:])
return PublicKey{
id: p.ID(),
bytes: bytes,
}
}
// Sign signs the given message.
//
// The minisign signature scheme relies on Ed25519 and supports
// plain as well as pre-hashed messages. Therefore, opts can be
// either crypto.Hash(0) to signal that the message has not been
// hashed or crypto.BLAKE2b_512 to signal that the message is a
// BLAKE2b-512 digest. If opts is crypto.BLAKE2b_512 then message
// must be a 64 bytes long.
//
// Minisign signatures are deterministic such that no randomness
// is necessary.
func (p PrivateKey) Sign(_ io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
var (
trustedComment = "timestamp:" + strconv.FormatInt(time.Now().Unix(), 10)
untrustedComment = "signature from private key: " + strings.ToUpper(strconv.FormatUint(p.ID(), 16))
)
switch h := opts.HashFunc(); h {
case crypto.Hash(0):
const isHashed = false
return sign(p, message, trustedComment, untrustedComment, isHashed), nil
case crypto.BLAKE2b_512:
const isHashed = true
if n := len(message); n != blake2b.Size {
return nil, errors.New("minisign: invalid message length " + strconv.Itoa(n))
}
return sign(p, message, trustedComment, untrustedComment, isHashed), nil
default:
return nil, errors.New("minisign: cannot sign messages hashed with " + strconv.Itoa(int(h)))
}
}
// Equal returns true if and only if p and x have equivalent values.
func (p PrivateKey) Equal(x crypto.PrivateKey) bool {
xx, ok := x.(PrivateKey)
if !ok {
return false
}
return p.RawID == xx.RawID && subtle.ConstantTimeCompare(p.RawBytes[:], xx.RawBytes[:]) == 1
}
const (
scryptAlgorithm = 0x6353 // hex value for "Sc"
blake2bAlgorithm = 0x3242 // hex value for "B2"
scryptOpsLimit = 0x2000000 // max. Scrypt ops limit based on libsodium
scryptMemLimit = 0x40000000 // max. Scrypt mem limit based on libsodium
privateKeySize = 158 // 2 + 2 + 2 + 32 + 8 + 8 + 104
)
// EncryptKey encrypts the private key with the given password
// using some entropy from the RNG of the OS.
func EncryptKey(password string, privateKey PrivateKey) ([]byte, error) {
var privateKeyBytes [72]byte
binary.LittleEndian.PutUint64(privateKeyBytes[:], privateKey.ID())
copy(privateKeyBytes[8:], privateKey.RawBytes[:])
var salt [32]byte
if _, err := io.ReadFull(rand.Reader, salt[:]); err != nil {
return nil, err
}
var bytes [privateKeySize]byte
binary.LittleEndian.PutUint16(bytes[0:], EdDSA)
binary.LittleEndian.PutUint16(bytes[2:], scryptAlgorithm)
binary.LittleEndian.PutUint16(bytes[4:], blake2bAlgorithm)
const ( // TODO(aead): Callers may want to customize the cost parameters
defaultOps = 33554432 // libsodium OPS_LIMIT_SENSITIVE
defaultMem = 1073741824 // libsodium MEM_LIMIT_SENSITIVE
)
copy(bytes[6:38], salt[:])
binary.LittleEndian.PutUint64(bytes[38:], defaultOps)
binary.LittleEndian.PutUint64(bytes[46:], defaultMem)
copy(bytes[54:], encryptKey(password, salt[:], defaultOps, defaultMem, privateKeyBytes[:]))
const comment = "untrusted comment: minisign encrypted secret key\n"
encodedBytes := make([]byte, len(comment)+base64.StdEncoding.EncodedLen(len(bytes)))
copy(encodedBytes, []byte(comment))
base64.StdEncoding.Encode(encodedBytes[len(comment):], bytes[:])
return encodedBytes, nil
}
var errDecrypt = errors.New("minisign: decryption failed")
// DecryptKey tries to decrypt the encrypted private key with
// the given password.
func DecryptKey(password string, privateKey []byte) (PrivateKey, error) {
privateKey = trimUntrustedComment(privateKey)
bytes := make([]byte, base64.StdEncoding.DecodedLen(len(privateKey)))
n, err := base64.StdEncoding.Decode(bytes, privateKey)
if err != nil {
return PrivateKey{}, err
}
bytes = bytes[:n]
if len(bytes) != privateKeySize {
return PrivateKey{}, errDecrypt
}
if a := binary.LittleEndian.Uint16(bytes[:2]); a != EdDSA {
return PrivateKey{}, errDecrypt
}
if a := binary.LittleEndian.Uint16(bytes[2:4]); a != scryptAlgorithm {
return PrivateKey{}, errDecrypt
}
if a := binary.LittleEndian.Uint16(bytes[4:6]); a != blake2bAlgorithm {
return PrivateKey{}, errDecrypt
}
var (
scryptOps = binary.LittleEndian.Uint64(bytes[38:46])
scryptMem = binary.LittleEndian.Uint64(bytes[46:54])
)
if scryptOps > scryptOpsLimit {
return PrivateKey{}, errDecrypt
}
if scryptMem > scryptMemLimit {
return PrivateKey{}, errDecrypt
}
var salt [32]byte
copy(salt[:], bytes[6:38])
privateKeyBytes, err := decryptKey(password, salt[:], scryptOps, scryptMem, bytes[54:])
if err != nil {
return PrivateKey{}, err
}
key := PrivateKey{
RawID: binary.LittleEndian.Uint64(privateKeyBytes[:8]),
}
copy(key.RawBytes[:], privateKeyBytes[8:])
return key, nil
}
// encryptKey encrypts the plaintext and returns a ciphertext by:
// 1. tag = BLAKE2b-256(EdDSA-const || plaintext)
// 2. keystream = Scrypt(password, salt, convert(ops, mem))
// 3. ciphertext = (plaintext || tag) ⊕ keystream
//
// Therefore, decryptKey converts the ops and mem cost parameters
// to the (N, r, p)-tuple expected by Scrypt.
//
// The plaintext must be a private key ID concatenated with a raw
// Ed25519 private key, and therefore, 72 bytes long.
func encryptKey(password string, salt []byte, ops, mem uint64, plaintext []byte) []byte {
const (
plaintextLen = 72
messageLen = 74
ciphertextLen = 104
)
N, r, p := convertScryptParameters(ops, mem)
keystream, err := scrypt.Key([]byte(password), salt, N, r, p, ciphertextLen)
if err != nil {
panic(err)
}
var message [messageLen]byte
binary.LittleEndian.PutUint16(message[:2], EdDSA)
copy(message[2:], plaintext)
checksum := blake2b.Sum256(message[:])
var ciphertext [ciphertextLen]byte
copy(ciphertext[:plaintextLen], plaintext)
copy(ciphertext[plaintextLen:], checksum[:])
for i, k := range keystream {
ciphertext[i] ^= k
}
return ciphertext[:]
}
// decryptKey decrypts the ciphertext and returns a plaintext by:
// 1. keystream = Scrypt(password, salt, convert(ops, mem))
// 2. plaintext || tag = ciphertext ⊕ keystream
// 3. Check that: tag == BLAKE2b-256(EdDSA-const || plaintext)
//
// Therefore, decryptKey converts the ops and mem cost parameters to
// the (N, r, p)-tuple expected by Scrypt.
//
// It returns an error if the ciphertext is not valid - i.e. if the
// tag does not match the BLAKE2b-256 hash value.
func decryptKey(password string, salt []byte, ops, mem uint64, ciphertext []byte) ([]byte, error) {
const (
plaintextLen = 72
messageLen = 74
ciphertextLen = 104
)
if len(ciphertext) != ciphertextLen {
return nil, errDecrypt
}
N, r, p := convertScryptParameters(ops, mem)
keystream, err := scrypt.Key([]byte(password), salt, N, r, p, ciphertextLen)
if err != nil {
return nil, err
}
var plaintext [ciphertextLen]byte
for i, k := range keystream {
plaintext[i] = ciphertext[i] ^ k
}
var (
privateKeyBytes = plaintext[:plaintextLen]
checksum = plaintext[plaintextLen:]
)
var message [messageLen]byte
binary.LittleEndian.PutUint16(message[:2], EdDSA)
copy(message[2:], privateKeyBytes)
if sum := blake2b.Sum256(message[:]); subtle.ConstantTimeCompare(sum[:], checksum[:]) != 1 {
return nil, errDecrypt
}
return privateKeyBytes, nil
}
// convertScryptParameters converts the operational and memory cost
// to the Scrypt parameters N, r and p.
//
// N is the overall memory / CPU cost and r * p has to be lower then
// 2³⁰. Refer to the scrypt.Key docs for more information.
func convertScryptParameters(ops, mem uint64) (N, r, p int) {
const (
minOps = 1 << 15
maxRP = 0x3fffffff
)
if ops < minOps {
ops = minOps
}
if ops < mem/32 {
r, p = 8, 1
for n := 1; n < 63; n++ {
if N = 1 << n; uint64(N) > (ops / (8 * uint64(r))) {
break
}
}
} else {
r = 8
for n := 1; n < 63; n++ {
if N = 1 << n; uint64(N) > (mem / (256 * uint64(r))) {
break
}
}
if rp := (ops / 4) / uint64(N); rp < maxRP {
p = int(rp) / r
} else {
p = maxRP / r
}
}
return N, r, p
}
-93
View File
@@ -1,93 +0,0 @@
package minisign
import (
"crypto"
"crypto/ed25519"
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"os"
"strconv"
"strings"
)
// PublicKeyFromFile reads a new PublicKey from the
// given file.
func PublicKeyFromFile(path string) (PublicKey, error) {
bytes, err := os.ReadFile(path)
if err != nil {
return PublicKey{}, err
}
var key PublicKey
if err = key.UnmarshalText(bytes); err != nil {
return PublicKey{}, err
}
return key, nil
}
// PublicKey is a minisign public key.
//
// A public key is used to verify whether messages
// have been signed with the corresponding private
// key.
type PublicKey struct {
_ [0]func() // prevent direct comparison: p1 == p2.
id uint64
bytes [ed25519.PublicKeySize]byte
}
// ID returns the 64 bit key ID.
func (p PublicKey) ID() uint64 { return p.id }
// Equal returns true if and only if p and x have equivalent values.
func (p PublicKey) Equal(x crypto.PublicKey) bool {
xx, ok := x.(PublicKey)
if !ok {
return false
}
return p.id == xx.id && p.bytes == xx.bytes
}
// String returns a base64 string representation of the PublicKey p.
func (p PublicKey) String() string {
var bytes [2 + 8 + ed25519.PublicKeySize]byte
binary.LittleEndian.PutUint16(bytes[:2], EdDSA)
binary.LittleEndian.PutUint64(bytes[2:10], p.ID())
copy(bytes[10:], p.bytes[:])
return base64.StdEncoding.EncodeToString(bytes[:])
}
// MarshalText returns a textual representation of the PublicKey p.
//
// It never returns an error.
func (p PublicKey) MarshalText() ([]byte, error) {
var comment = "untrusted comment: minisign public key: " + strings.ToUpper(strconv.FormatUint(p.ID(), 16)) + "\n"
return []byte(comment + p.String()), nil
}
// UnmarshalText parses text as textual-encoded public key.
// It returns an error if text is not a well-formed public key.
func (p *PublicKey) UnmarshalText(text []byte) error {
text = trimUntrustedComment(text)
bytes := make([]byte, base64.StdEncoding.DecodedLen(len(text)))
n, err := base64.StdEncoding.Decode(bytes, text)
if err != nil {
return fmt.Errorf("minisign: invalid public key: %v", err)
}
bytes = bytes[:n] // Adjust b/c text may contain '\r' or '\n' which would have been ignored during decoding.
if n = len(bytes); n != 2+8+ed25519.PublicKeySize {
return errors.New("minisign: invalid public key length " + strconv.Itoa(n))
}
if a := binary.LittleEndian.Uint16(bytes[:2]); a != EdDSA {
return errors.New("minisign: invalid public key algorithm " + strconv.Itoa(int(a)))
}
p.id = binary.LittleEndian.Uint64(bytes[2:10])
copy(p.bytes[:], bytes[10:])
return nil
}
@@ -1,94 +0,0 @@
// Copyright (c) 2021 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
package minisign
import "testing"
var marshalPublicKeyTests = []struct {
PublicKey PublicKey
Text string
}{
{
PublicKey: PublicKey{
id: 0xe7620f1842b4e81f,
bytes: [32]byte{121, 165, 97, 231, 14, 224, 140, 211, 231, 84, 198, 62, 155, 214, 185, 195, 82, 10, 29, 66, 4, 205, 16, 77, 162, 231, 239, 118, 59, 24, 83, 183},
},
Text: "untrusted comment: minisign public key: E7620F1842B4E81F" + "\n" + "RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3",
},
{
PublicKey: PublicKey{
id: 0x6f7add142cdc7edb,
bytes: [32]byte{121, 165, 97, 231, 14, 224, 140, 211, 231, 84, 198, 62, 155, 214, 185, 195, 82, 10, 29, 66, 4, 205, 16, 77, 162, 231, 239, 118, 59, 24, 83, 183},
},
Text: "untrusted comment: minisign public key: 6F7ADD142CDC7EDB" + "\n" + "RWTbftwsFN16b3mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3",
},
}
func TestMarshalPublicKey(t *testing.T) {
for i, test := range marshalPublicKeyTests {
text, err := test.PublicKey.MarshalText()
if err != nil {
t.Fatalf("Test %d: failed to marshal public key: %v", i, err)
}
if string(text) != test.Text {
t.Fatalf("Test %d: got '%s' - want '%s'", i, string(text), test.Text)
}
}
}
var unmarshalPublicKeyTests = []struct {
Text string
PublicKey PublicKey
ShouldFail bool
}{
{
Text: "RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3",
PublicKey: PublicKey{
id: 0xe7620f1842b4e81f,
bytes: [32]byte{121, 165, 97, 231, 14, 224, 140, 211, 231, 84, 198, 62, 155, 214, 185, 195, 82, 10, 29, 66, 4, 205, 16, 77, 162, 231, 239, 118, 59, 24, 83, 183},
},
},
{
Text: "RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3\r\n\n",
PublicKey: PublicKey{
id: 0xe7620f1842b4e81f,
bytes: [32]byte{121, 165, 97, 231, 14, 224, 140, 211, 231, 84, 198, 62, 155, 214, 185, 195, 82, 10, 29, 66, 4, 205, 16, 77, 162, 231, 239, 118, 59, 24, 83, 183},
},
},
{ // Invalid algorithm
Text: "RmQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3",
ShouldFail: true,
},
{ // Invalid public key b/c too long
Text: "RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3bhQ=",
ShouldFail: true,
},
}
func TestUnmarshalPublicKey(t *testing.T) {
for i, test := range unmarshalPublicKeyTests {
var key PublicKey
err := key.UnmarshalText([]byte(test.Text))
if err == nil && test.ShouldFail {
t.Fatalf("Test %d: should have failed but passed", i)
}
if err != nil && !test.ShouldFail {
t.Fatalf("Test %d: failed to unmarshal public key: %v", i, err)
}
if err == nil {
if key.ID() != test.PublicKey.ID() {
t.Fatalf("Test %d: key ID mismatch: got '%x' - want '%x'", i, key.ID(), test.PublicKey.ID())
}
if key.bytes != test.PublicKey.bytes {
t.Fatalf("Test %d: raw public key mismatch: got '%v' - want '%v'", i, key.bytes, test.PublicKey.bytes)
}
if !key.Equal(test.PublicKey) {
t.Fatalf("Test %d: public keys are not equal", i)
}
}
}
}
@@ -1,67 +0,0 @@
package minisign
import (
"crypto/rand"
insecureRand "math/rand"
"testing"
)
func TestRawSigValid(t *testing.T) {
publicKey, privateKey, err := GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("Failed to generate key: %v", err)
}
for i := 0; i < 100; i++ {
message := randomBuf()
signature := SignRawBuf(privateKey, message)
rawMsg := append(signature[:], message...)
if !VerifyRawBuf(publicKey, rawMsg) {
t.Fatalf("Verification failed: signature %q - public key %q", signature, publicKey)
}
}
}
func TestRawSigInvalidKey(t *testing.T) {
_, privateKeyA, err := GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("Failed to generate key: %v", err)
}
publicKeyB, _, err := GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("Failed to generate key: %v", err)
}
for i := 0; i < 100; i++ {
message := randomBuf()
signature := SignRawBuf(privateKeyA, message)
rawMsg := append(signature[:], message...)
if VerifyRawBuf(publicKeyB, rawMsg) {
t.Fatalf("Verification expected to fail, but didn't: signature %q - public key %q", signature, publicKeyB)
}
}
}
func TestRawSigInvalidTamper(t *testing.T) {
publicKey, privateKey, err := GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("Failed to generate key: %v", err)
}
for i := 0; i < 100; i++ {
message := randomBuf()
signature := SignRawBuf(privateKey, message)
message[insecureRand.Intn(len(message))] ^= 0xFF
message[insecureRand.Intn(len(message))] ^= 0xFF
message[insecureRand.Intn(len(message))] ^= 0xFF
message[insecureRand.Intn(len(message))] ^= 0xFF
message[insecureRand.Intn(len(message))] ^= 0xFF
rawMsg := append(signature[:], message...)
if VerifyRawBuf(publicKey, rawMsg) {
t.Fatalf("Verification expected to fail, but didn't: signature %q - public key %q", signature, publicKey)
}
}
}
func randomBuf() []byte {
buf := make([]byte, insecureRand.Intn(4096)+1)
rand.Read(buf)
return buf
}
-186
View File
@@ -1,186 +0,0 @@
// Copyright (c) 2021 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
package minisign
import (
"crypto/ed25519"
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"os"
"strconv"
"strings"
)
// SignatureFromFile reads a new Signature from the
// given file.
func SignatureFromFile(file string) (Signature, error) {
bytes, err := os.ReadFile(file)
if err != nil {
return Signature{}, err
}
var signature Signature
if err = signature.UnmarshalText(bytes); err != nil {
return Signature{}, err
}
return signature, nil
}
// Signature is a structured representation of a minisign
// signature.
//
// A signature is generated when signing a message with
// a private key:
// signature = Sign(privateKey, message)
//
// The signature of a message can then be verified with the
// corresponding public key:
// if Verify(publicKey, message, signature) {
// // => signature is valid
// // => message has been signed with correspoding private key
// }
//
type Signature struct {
_ [0]func() // enforce named assignment and prevent direct comparison
// Algorithm is the signature algorithm. It is either
// EdDSA or HashEdDSA.
Algorithm uint16
// KeyID may be the 64 bit ID of the private key that was used
// to produce this signature. It can be used to identify the
// corresponding public key that can verify the signature.
//
// However, key IDs are random identifiers and not protected at all.
// A key ID is just a hint to quickly identify a public key candidate.
KeyID uint64
// TrustedComment is a comment that has been signed and is
// verified during signature verification.
TrustedComment string
// UntrustedComment is a comment that has not been signed
// and is not verified during signature verification.
//
// It must not be considered authentic - in contrast to the
// TrustedComment.
UntrustedComment string
// Signature is the Ed25519 signature of the message that
// has been signed.
Signature [ed25519.SignatureSize]byte
// CommentSignature is the Ed25519 signature of Signature
// concatenated with the TrustedComment:
//
// CommentSignature = ed25519.Sign(PrivateKey, Signature || TrustedComment)
//
// It is used to verify that the TrustedComment is authentic.
CommentSignature [ed25519.SignatureSize]byte
}
// String returns a string representation of the Signature s.
//
// In contrast to MarshalText, String does not fail if s is
// not a valid minisign signature.
func (s Signature) String() string {
var buffer strings.Builder
buffer.WriteString("untrusted comment: ")
buffer.WriteString(s.UntrustedComment)
buffer.WriteByte('\n')
var signature [2 + 8 + ed25519.SignatureSize]byte
binary.LittleEndian.PutUint16(signature[:2], s.Algorithm)
binary.LittleEndian.PutUint64(signature[2:10], s.KeyID)
copy(signature[10:], s.Signature[:])
buffer.WriteString(base64.StdEncoding.EncodeToString(signature[:]))
buffer.WriteByte('\n')
buffer.WriteString("trusted comment: ")
buffer.WriteString(s.TrustedComment)
buffer.WriteByte('\n')
buffer.WriteString(base64.StdEncoding.EncodeToString(s.CommentSignature[:]))
return buffer.String()
}
// Equal reports whether s and x have equivalent values.
//
// The untrusted comments of two equivalent signatures may differ.
func (s Signature) Equal(x Signature) bool {
return s.Algorithm == x.Algorithm &&
s.KeyID == x.KeyID &&
s.Signature == x.Signature &&
s.CommentSignature == x.CommentSignature &&
s.TrustedComment == x.TrustedComment
}
// MarshalText returns a textual representation of the Signature s.
//
// It returns an error if s cannot be a valid signature - e.g.
// because the signature algorithm is neither EdDSA nor HashEdDSA.
func (s Signature) MarshalText() ([]byte, error) {
if s.Algorithm != EdDSA && s.Algorithm != HashEdDSA {
return nil, errors.New("minisign: invalid signature algorithm " + strconv.Itoa(int(s.Algorithm)))
}
return []byte(s.String()), nil
}
// UnmarshalText parses text as textual-encoded signature.
// It returns an error if text is not a well-formed minisign
// signature.
func (s *Signature) UnmarshalText(text []byte) error {
segments := strings.SplitN(string(text), "\n", 4)
if len(segments) != 4 {
return errors.New("minisign: invalid signature")
}
var (
untrustedComment = strings.TrimRight(segments[0], "\r")
encodedSignature = segments[1]
trustedComment = strings.TrimRight(segments[2], "\r")
encodedCommentSignature = segments[3]
)
if !strings.HasPrefix(untrustedComment, "untrusted comment: ") {
return errors.New("minisign: invalid signature: invalid untrusted comment")
}
if !strings.HasPrefix(trustedComment, "trusted comment: ") {
return errors.New("minisign: invalid signature: invalid trusted comment")
}
rawSignature, err := base64.StdEncoding.DecodeString(encodedSignature)
if err != nil {
return fmt.Errorf("minisign: invalid signature: %v", err)
}
if n := len(rawSignature); n != 2+8+ed25519.SignatureSize {
return errors.New("minisign: invalid signature length " + strconv.Itoa(n))
}
commentSignature, err := base64.StdEncoding.DecodeString(encodedCommentSignature)
if err != nil {
return fmt.Errorf("minisign: invalid signature: %v", err)
}
if n := len(commentSignature); n != ed25519.SignatureSize {
return errors.New("minisign: invalid comment signature length " + strconv.Itoa(n))
}
var (
algorithm = binary.LittleEndian.Uint16(rawSignature[:2])
keyID = binary.LittleEndian.Uint64(rawSignature[2:10])
)
if algorithm != EdDSA && algorithm != HashEdDSA {
return errors.New("minisign: invalid signature: invalid algorithm " + strconv.Itoa(int(algorithm)))
}
s.Algorithm = algorithm
s.KeyID = keyID
s.TrustedComment = strings.TrimPrefix(trustedComment, "trusted comment: ")
s.UntrustedComment = strings.TrimPrefix(untrustedComment, "untrusted comment: ")
copy(s.Signature[:], rawSignature[10:])
copy(s.CommentSignature[:], commentSignature)
return nil
}
@@ -1,296 +0,0 @@
// Copyright (c) 2021 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
package minisign
import (
"strings"
"testing"
)
func TestEqualSignature(t *testing.T) {
for i, test := range equalSignatureTests {
equal := test.A.Equal(test.B)
if equal != test.Equal {
t.Fatalf("Test %d: got 'equal=%v' - want 'equal=%v", i, equal, test.Equal)
}
if revEqual := test.B.Equal(test.A); equal != revEqual {
t.Fatalf("Test %d: A == B is %v but B == A is %v", i, equal, revEqual)
}
}
}
func TestMarshalInvalidSignature(t *testing.T) {
var signature Signature
if _, err := signature.MarshalText(); err == nil {
t.Fatal("Marshaling invalid signature succeeded")
}
}
func TestMarshalSignatureRoundtrip(t *testing.T) {
for i, test := range marshalSignatureTests {
text, err := test.Signature.MarshalText()
if err != nil {
t.Fatalf("Test %d: failed to marshal signature: %v", i, err)
}
var signature Signature
if err = signature.UnmarshalText(text); err != nil {
t.Fatalf("Test %d: failed to unmarshal signature: %v", i, err)
}
if !signature.Equal(test.Signature) {
t.Fatalf("Test %d: signature mismatch: got '%v' - want '%v'", i, signature, test.Signature)
}
}
}
func TestUnmarshalSignature(t *testing.T) {
for i, test := range unmarshalSignatureTests {
var signature Signature
err := signature.UnmarshalText([]byte(test.Text))
if err == nil && test.ShouldFail {
t.Fatalf("Test %d: should have failed but passed", i)
}
if err != nil && !test.ShouldFail {
t.Fatalf("Test %d: failed to unmarshal signature: %v", i, err)
}
if err == nil {
if !signature.Equal(test.Signature) {
t.Fatalf("Test %d: signatures are not equal: got '%s' - want '%s'", i, signature, test.Signature)
}
if signature.UntrustedComment != test.Signature.UntrustedComment {
t.Fatalf("Test %d: untrusted comment mismatch: got '%s' - want '%s'", i, signature.UntrustedComment, test.Signature.UntrustedComment)
}
}
}
}
func TestSignatureCarriageReturn(t *testing.T) {
signature, err := SignatureFromFile("./internal/testdata/robtest.ps1.minisig")
if err != nil {
t.Fatalf("Failed to read signature from file: %v", err)
}
if strings.HasSuffix(signature.UntrustedComment, "\r") {
t.Fatal("Untrusted comment ends with a carriage return")
}
if strings.HasSuffix(signature.TrustedComment, "\r") {
t.Fatal("Trusted comment ends with a carriage return")
}
}
var equalSignatureTests = []struct {
A, B Signature
Equal bool
}{
{
A: Signature{}, B: Signature{}, Equal: true,
},
{
A: Signature{
Algorithm: EdDSA,
KeyID: 0xe7620f1842b4e81f,
UntrustedComment: `signature from minisign secret key`,
TrustedComment: `timestamp:1591521248 file:minisign-0.9.tar.gz`,
Signature: [64]byte{20, 99, 118, 100, 132, 21, 202, 44, 47, 123, 240, 66, 228, 28, 175, 132, 143, 49, 11, 188, 252, 49, 53, 73, 106, 154, 66, 249, 67, 203, 35, 77, 156, 24, 226, 182, 244, 241, 252, 5, 244, 97, 127, 41, 191, 156, 128, 14, 117, 64, 157, 164, 36, 146, 238, 203, 151, 33, 174, 82, 239, 66, 73, 10},
CommentSignature: [64]byte{148, 178, 205, 92, 217, 151, 10, 78, 112, 147, 154, 17, 47, 24, 233, 136, 141, 16, 37, 217, 29, 77, 64, 75, 217, 55, 69, 178, 114, 188, 40, 93, 6, 130, 93, 121, 211, 7, 19, 198, 190, 160, 33, 49, 136, 129, 80, 249, 121, 170, 165, 216, 105, 97, 230, 151, 208, 109, 244, 227, 46, 121, 241, 15},
},
B: Signature{
Algorithm: EdDSA,
KeyID: 0xe7620f1842b4e81f,
UntrustedComment: `signature from minisign secret key`,
TrustedComment: `timestamp:1591521248 file:minisign-0.9.tar.gz`,
Signature: [64]byte{20, 99, 118, 100, 132, 21, 202, 44, 47, 123, 240, 66, 228, 28, 175, 132, 143, 49, 11, 188, 252, 49, 53, 73, 106, 154, 66, 249, 67, 203, 35, 77, 156, 24, 226, 182, 244, 241, 252, 5, 244, 97, 127, 41, 191, 156, 128, 14, 117, 64, 157, 164, 36, 146, 238, 203, 151, 33, 174, 82, 239, 66, 73, 10},
CommentSignature: [64]byte{148, 178, 205, 92, 217, 151, 10, 78, 112, 147, 154, 17, 47, 24, 233, 136, 141, 16, 37, 217, 29, 77, 64, 75, 217, 55, 69, 178, 114, 188, 40, 93, 6, 130, 93, 121, 211, 7, 19, 198, 190, 160, 33, 49, 136, 129, 80, 249, 121, 170, 165, 216, 105, 97, 230, 151, 208, 109, 244, 227, 46, 121, 241, 15},
},
Equal: true,
},
{
A: Signature{UntrustedComment: "signature A"},
B: Signature{UntrustedComment: "signature B"},
Equal: true,
},
{
A: Signature{Algorithm: EdDSA},
B: Signature{Algorithm: HashEdDSA},
Equal: false, // Algorithm differs
},
{
A: Signature{KeyID: 0xe7620f1842b4e81f},
B: Signature{KeyID: 0x1fe8b442180f62e7},
Equal: false, // KeyID differs
},
{
A: Signature{TrustedComment: `timestamp:1591521248 file:minisign-0.9.tar.gz`},
B: Signature{TrustedComment: `timestamp:1591521249 file:minisign-0.9.tar.gz`},
Equal: false, // TrustedComment differs
},
{
A: Signature{Signature: [64]byte{20, 99, 118, 100, 132, 21, 202, 44, 47, 123, 240, 66, 228, 28, 175, 132, 143, 49, 11, 188, 252, 49, 53, 73, 106, 154, 66, 249, 67, 203, 35, 77, 156, 24, 226, 182, 244, 241, 252, 5, 244, 97, 127, 41, 191, 156, 128, 14, 117, 64, 157, 164, 36, 146, 238, 203, 151, 33, 174, 82, 239, 66, 73, 10}},
B: Signature{Signature: [64]byte{148, 178, 205, 92, 217, 151, 10, 78, 112, 147, 154, 17, 47, 24, 233, 136, 141, 16, 37, 217, 29, 77, 64, 75, 217, 55, 69, 178, 114, 188, 40, 93, 6, 130, 93, 121, 211, 7, 19, 198, 190, 160, 33, 49, 136, 129, 80, 249, 121, 170, 165, 216, 105, 97, 230, 151, 208, 109, 244, 227, 46, 121, 241, 15}},
Equal: false, // Signature differs
},
{
A: Signature{CommentSignature: [64]byte{20, 99, 118, 100, 132, 21, 202, 44, 47, 123, 240, 66, 228, 28, 175, 132, 143, 49, 11, 188, 252, 49, 53, 73, 106, 154, 66, 249, 67, 203, 35, 77, 156, 24, 226, 182, 244, 241, 252, 5, 244, 97, 127, 41, 191, 156, 128, 14, 117, 64, 157, 164, 36, 146, 238, 203, 151, 33, 174, 82, 239, 66, 73, 10}},
B: Signature{CommentSignature: [64]byte{148, 178, 205, 92, 217, 151, 10, 78, 112, 147, 154, 17, 47, 24, 233, 136, 141, 16, 37, 217, 29, 77, 64, 75, 217, 55, 69, 178, 114, 188, 40, 93, 6, 130, 93, 121, 211, 7, 19, 198, 190, 160, 33, 49, 136, 129, 80, 249, 121, 170, 165, 216, 105, 97, 230, 151, 208, 109, 244, 227, 46, 121, 241, 15}},
Equal: false, // CommentSignature differs
},
}
var marshalSignatureTests = []struct {
Signature Signature
}{
{
Signature: Signature{
Algorithm: EdDSA,
},
},
{
Signature: Signature{
Algorithm: EdDSA,
KeyID: 0xe7620f1842b4e81f,
},
},
{
Signature: Signature{
Algorithm: EdDSA,
KeyID: 0xe7620f1842b4e81f,
UntrustedComment: `signature from minisign secret key`,
},
},
{
Signature: Signature{
Algorithm: EdDSA,
KeyID: 0xe7620f1842b4e81f,
UntrustedComment: `signature from minisign secret key`,
TrustedComment: `timestamp:1591521248 file:minisign-0.9.tar.gz`,
},
},
{
Signature: Signature{
Algorithm: EdDSA,
KeyID: 0xe7620f1842b4e81f,
UntrustedComment: `signature from minisign secret key`,
TrustedComment: `timestamp:1591521248 file:minisign-0.9.tar.gz`,
Signature: [64]byte{20, 99, 118, 100, 132, 21, 202, 44, 47, 123, 240, 66, 228, 28, 175, 132, 143, 49, 11, 188, 252, 49, 53, 73, 106, 154, 66, 249, 67, 203, 35, 77, 156, 24, 226, 182, 244, 241, 252, 5, 244, 97, 127, 41, 191, 156, 128, 14, 117, 64, 157, 164, 36, 146, 238, 203, 151, 33, 174, 82, 239, 66, 73, 10},
},
},
{
Signature: Signature{
Algorithm: EdDSA,
KeyID: 0xe7620f1842b4e81f,
UntrustedComment: `signature from minisign secret key`,
TrustedComment: `timestamp:1591521248 file:minisign-0.9.tar.gz`,
Signature: [64]byte{20, 99, 118, 100, 132, 21, 202, 44, 47, 123, 240, 66, 228, 28, 175, 132, 143, 49, 11, 188, 252, 49, 53, 73, 106, 154, 66, 249, 67, 203, 35, 77, 156, 24, 226, 182, 244, 241, 252, 5, 244, 97, 127, 41, 191, 156, 128, 14, 117, 64, 157, 164, 36, 146, 238, 203, 151, 33, 174, 82, 239, 66, 73, 10},
CommentSignature: [64]byte{148, 178, 205, 92, 217, 151, 10, 78, 112, 147, 154, 17, 47, 24, 233, 136, 141, 16, 37, 217, 29, 77, 64, 75, 217, 55, 69, 178, 114, 188, 40, 93, 6, 130, 93, 121, 211, 7, 19, 198, 190, 160, 33, 49, 136, 129, 80, 249, 121, 170, 165, 216, 105, 97, 230, 151, 208, 109, 244, 227, 46, 121, 241, 15},
},
},
}
var unmarshalSignatureTests = []struct {
Text string
Signature Signature
ShouldFail bool
}{
{
Text: `untrusted comment: signature from minisign secret key
RWQf6LRCGA9i5xRjdmSEFcosL3vwQuQcr4SPMQu8/DE1SWqaQvlDyyNNnBjitvTx/AX0YX8pv5yADnVAnaQkku7LlyGuUu9CSQo=
trusted comment: timestamp:1591521248 file:minisign-0.9.tar.gz
lLLNXNmXCk5wk5oRLxjpiI0QJdkdTUBL2TdFsnK8KF0Ggl150wcTxr6gITGIgVD5eaql2Glh5pfQbfTjLnnxDw==`,
Signature: Signature{
Algorithm: EdDSA,
KeyID: 0xe7620f1842b4e81f,
UntrustedComment: `signature from minisign secret key`,
TrustedComment: `timestamp:1591521248 file:minisign-0.9.tar.gz`,
Signature: [64]byte{20, 99, 118, 100, 132, 21, 202, 44, 47, 123, 240, 66, 228, 28, 175, 132, 143, 49, 11, 188, 252, 49, 53, 73, 106, 154, 66, 249, 67, 203, 35, 77, 156, 24, 226, 182, 244, 241, 252, 5, 244, 97, 127, 41, 191, 156, 128, 14, 117, 64, 157, 164, 36, 146, 238, 203, 151, 33, 174, 82, 239, 66, 73, 10},
CommentSignature: [64]byte{148, 178, 205, 92, 217, 151, 10, 78, 112, 147, 154, 17, 47, 24, 233, 136, 141, 16, 37, 217, 29, 77, 64, 75, 217, 55, 69, 178, 114, 188, 40, 93, 6, 130, 93, 121, 211, 7, 19, 198, 190, 160, 33, 49, 136, 129, 80, 249, 121, 170, 165, 216, 105, 97, 230, 151, 208, 109, 244, 227, 46, 121, 241, 15},
},
},
{
Text: `untrusted comment: signature from minisign secret key
RWQf6LRCGA9i5xRjdmSEFcosL3vwQuQcr4SPMQu8/DE1SWqaQvlDyyNNnBjitvTx/AX0YX8pv5yADnVAnaQkku7LlyGuUu9CSQo=
trusted comment: timestamp:1591521248 file:minisign-0.9.tar.gz
lLLNXNmXCk5wk5oRLxjpiI0QJdkdTUBL2TdFsnK8KF0Ggl150wcTxr6gITGIgVD5eaql2Glh5pfQbfTjLnnxDw==` + "\n\r\n",
Signature: Signature{
Algorithm: EdDSA,
KeyID: 0xe7620f1842b4e81f,
UntrustedComment: `signature from minisign secret key`,
TrustedComment: `timestamp:1591521248 file:minisign-0.9.tar.gz`,
Signature: [64]byte{20, 99, 118, 100, 132, 21, 202, 44, 47, 123, 240, 66, 228, 28, 175, 132, 143, 49, 11, 188, 252, 49, 53, 73, 106, 154, 66, 249, 67, 203, 35, 77, 156, 24, 226, 182, 244, 241, 252, 5, 244, 97, 127, 41, 191, 156, 128, 14, 117, 64, 157, 164, 36, 146, 238, 203, 151, 33, 174, 82, 239, 66, 73, 10},
CommentSignature: [64]byte{148, 178, 205, 92, 217, 151, 10, 78, 112, 147, 154, 17, 47, 24, 233, 136, 141, 16, 37, 217, 29, 77, 64, 75, 217, 55, 69, 178, 114, 188, 40, 93, 6, 130, 93, 121, 211, 7, 19, 198, 190, 160, 33, 49, 136, 129, 80, 249, 121, 170, 165, 216, 105, 97, 230, 151, 208, 109, 244, 227, 46, 121, 241, 15},
},
},
// Invalid signatures
{
Text: `RWQf6LRCGA9i5xRjdmSEFcosL3vwQuQcr4SPMQu8/DE1SWqaQvlDyyNNnBjitvTx/AX0YX8pv5yADnVAnaQkku7LlyGuUu9CSQo=
trusted comment: timestamp:1591521248 file:minisign-0.9.tar.gz
lLLNXNmXCk5wk5oRLxjpiI0QJdkdTUBL2TdFsnK8KF0Ggl150wcTxr6gITGIgVD5eaql2Glh5pfQbfTjLnnxDw==`,
ShouldFail: true, // Missing untrusted comment
},
{
Text: `untrusted: signature from minisign secret key
RWQf6LRCGA9i5xRjdmSEFcosL3vwQuQcr4SPMQu8/DE1SWqaQvlDyyNNnBjitvTx/AX0YX8pv5yADnVAnaQkku7LlyGuUu9CSQo=
trusted comment: timestamp:1591521248 file:minisign-0.9.tar.gz
lLLNXNmXCk5wk5oRLxjpiI0QJdkdTUBL2TdFsnK8KF0Ggl150wcTxr6gITGIgVD5eaql2Glh5pfQbfTjLnnxDw==`,
ShouldFail: true, // Invalid untrusted comment - wrong prefix
},
{
Text: `untrusted comment: signature from minisign secret key
trusted comment: timestamp:1591521248 file:minisign-0.9.tar.gz
lLLNXNmXCk5wk5oRLxjpiI0QJdkdTUBL2TdFsnK8KF0Ggl150wcTxr6gITGIgVD5eaql2Glh5pfQbfTjLnnxDw==`,
ShouldFail: true, // Missing signature value
},
{
Text: `untrusted comment: signature from minisign secret key
31TR+QBxE86BOJz1U46pc1lM1zEvMLBDTE255CHxFFLFcn4qPd3Q77xJTF2Y2IkDNqrTOCaZ43PQjSv9kIrnHXXwW0dwKnj
trusted comment: timestamp:1591521248 file:minisign-0.9.tar.gz
lLLNXNmXCk5wk5oRLxjpiI0QJdkdTUBL2TdFsnK8KF0Ggl150wcTxr6gITGIgVD5eaql2Glh5pfQbfTjLnnxDw==`,
ShouldFail: true, // Invalid signature value - invalid base64
},
{
Text: `untrusted comment: signature from minisign secret key
f4IYNY3p6K5CYtfB+dhN6Y+Fi+F6wWI0r+VjLwDE0q23wB1Opso6w/MJd9YGIU/HBs04flXnak37x/s2QhWAZlSCdbQYX7Q=
trusted comment: timestamp:1591521248 file:minisign-0.9.tar.gz
lLLNXNmXCk5wk5oRLxjpiI0QJdkdTUBL2TdFsnK8KF0Ggl150wcTxr6gITGIgVD5eaql2Glh5pfQbfTjLnnxDw==`,
ShouldFail: true, // Invalid signature value - invalid size
},
{
Text: `untrusted comment: signature from minisign secret key
RWQf6LRCGA9i5xRjdmSEFcosL3vwQuQcr4SPMQu8/DE1SWqaQvlDyyNNnBjitvTx/AX0YX8pv5yADnVAnaQkku7LlyGuUu9CSQo=
lLLNXNmXCk5wk5oRLxjpiI0QJdkdTUBL2TdFsnK8KF0Ggl150wcTxr6gITGIgVD5eaql2Glh5pfQbfTjLnnxDw==` + "\n\r\n",
ShouldFail: true, // Missing trusted comment
},
{
Text: `untrusted comment: signature from minisign secret key
RWQf6LRCGA9i5xRjdmSEFcosL3vwQuQcr4SPMQu8/DE1SWqaQvlDyyNNnBjitvTx/AX0YX8pv5yADnVAnaQkku7LlyGuUu9CSQo=
comment: timestamp:1591521248 file:minisign-0.9.tar.gz
lLLNXNmXCk5wk5oRLxjpiI0QJdkdTUBL2TdFsnK8KF0Ggl150wcTxr6gITGIgVD5eaql2Glh5pfQbfTjLnnxDw==` + "\n\r\n",
ShouldFail: true, // Invalid trusted comment - wrong prefix
},
{
Text: `untrusted comment: signature from minisign secret key
RWQf6LRCGA9i5xRjdmSEFcosL3vwQuQcr4SPMQu8/DE1SWqaQvlDyyNNnBjitvTx/AX0YX8pv5yADnVAnaQkku7LlyGuUu9CSQo=
trusted comment: timestamp:1591521248 file:minisign-0.9.tar.gz`,
ShouldFail: true, // Missing comment signature
},
{
Text: `untrusted comment: signature from minisign secret key
RWQf6LRCGA9i5xRjdmSEFcosL3vwQuQcr4SPMQu8/DE1SWqaQvlDyyNNnBjitvTx/AX0YX8pv5yADnVAnaQkku7LlyGuUu9CSQo=
trusted comment: timestamp:1591521248 file:minisign-0.9.tar.gz
Bqq219+sDloDkxHiCLcR5sTxrbl+qMS4oEnZ+IrZ4JDH5BxAzKehjoWSch3nbyNT96c/jz+XQjj4zd492skB_w==`,
ShouldFail: true, // Invalid comment signature - invalid base64
},
{
Text: `untrusted comment: signature from minisign secret key
RWQf6LRCGA9i5xRjdmSEFcosL3vwQuQcr4SPMQu8/DE1SWqaQvlDyyNNnBjitvTx/AX0YX8pv5yADnVAnaQkku7LlyGuUu9CSQo=
trusted comment: timestamp:1591521248 file:minisign-0.9.tar.gz
nqGtUS55Xhx/VzvCGtWjtsnlcItcsp0hzl/40j3oRkyJAISXHTakVQKK2VBBMyjBfhZTRRlEputvn/dNdC/Dh6Y=`,
ShouldFail: true, // Invalid comment signature - invalid size
},
}