From eddfa206849b240133d40d36d6623c2c95b68b24 Mon Sep 17 00:00:00 2001 From: moloch-- <875022+moloch--@users.noreply.github.com> Date: Fri, 10 Nov 2023 15:31:35 -0700 Subject: [PATCH] Move minisign to util/ --- client/command/armory/armory.go | 2 +- client/command/armory/install.go | 2 +- client/command/armory/parsers.go | 2 +- go-tests.sh | 16 +- server/cryptography/cryptography.go | 2 +- server/cryptography/cryptography_test.go | 2 +- server/cryptography/minisign/LICENSE | 21 -- .../minisign/internal/testdata/message.txt | 1 - .../internal/testdata/message.txt.minisig | 4 - .../minisign/internal/testdata/minisign.key | 2 - .../minisign/internal/testdata/minisign.pub | 2 - .../internal/testdata/robtest.ps1.minisig | 4 - server/cryptography/minisign/minisign.go | 267 --------------- server/cryptography/minisign/minisign_test.go | 63 ---- server/cryptography/minisign/private.go | 319 ------------------ server/cryptography/minisign/public.go | 93 ----- server/cryptography/minisign/public_test.go | 94 ------ server/cryptography/minisign/rawsig_test.go | 67 ---- server/cryptography/minisign/signature.go | 186 ---------- .../cryptography/minisign/signature_test.go | 296 ---------------- {client => util}/minisign/LICENSE | 0 .../minisign/internal/testdata/message.txt | 0 .../internal/testdata/message.txt.minisig | 0 .../minisign/internal/testdata/minisign.key | 0 .../minisign/internal/testdata/minisign.pub | 0 .../internal/testdata/robtest.ps1.minisig | 0 {client => util}/minisign/minisign.go | 0 {client => util}/minisign/minisign_test.go | 0 {client => util}/minisign/private.go | 0 {client => util}/minisign/public.go | 0 {client => util}/minisign/public_test.go | 0 {client => util}/minisign/rawsig_test.go | 0 {client => util}/minisign/signature.go | 0 {client => util}/minisign/signature_test.go | 0 34 files changed, 13 insertions(+), 1432 deletions(-) delete mode 100644 server/cryptography/minisign/LICENSE delete mode 100644 server/cryptography/minisign/internal/testdata/message.txt delete mode 100644 server/cryptography/minisign/internal/testdata/message.txt.minisig delete mode 100644 server/cryptography/minisign/internal/testdata/minisign.key delete mode 100644 server/cryptography/minisign/internal/testdata/minisign.pub delete mode 100644 server/cryptography/minisign/internal/testdata/robtest.ps1.minisig delete mode 100644 server/cryptography/minisign/minisign.go delete mode 100644 server/cryptography/minisign/minisign_test.go delete mode 100644 server/cryptography/minisign/private.go delete mode 100644 server/cryptography/minisign/public.go delete mode 100644 server/cryptography/minisign/public_test.go delete mode 100644 server/cryptography/minisign/rawsig_test.go delete mode 100644 server/cryptography/minisign/signature.go delete mode 100644 server/cryptography/minisign/signature_test.go rename {client => util}/minisign/LICENSE (100%) rename {client => util}/minisign/internal/testdata/message.txt (100%) rename {client => util}/minisign/internal/testdata/message.txt.minisig (100%) rename {client => util}/minisign/internal/testdata/minisign.key (100%) rename {client => util}/minisign/internal/testdata/minisign.pub (100%) rename {client => util}/minisign/internal/testdata/robtest.ps1.minisig (100%) rename {client => util}/minisign/minisign.go (100%) rename {client => util}/minisign/minisign_test.go (100%) rename {client => util}/minisign/private.go (100%) rename {client => util}/minisign/public.go (100%) rename {client => util}/minisign/public_test.go (100%) rename {client => util}/minisign/rawsig_test.go (100%) rename {client => util}/minisign/signature.go (100%) rename {client => util}/minisign/signature_test.go (100%) diff --git a/client/command/armory/armory.go b/client/command/armory/armory.go index 14d55069f..e575475bb 100644 --- a/client/command/armory/armory.go +++ b/client/command/armory/armory.go @@ -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 diff --git a/client/command/armory/install.go b/client/command/armory/install.go index 730b40ed3..8674a4aba 100644 --- a/client/command/armory/install.go +++ b/client/command/armory/install.go @@ -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 diff --git a/client/command/armory/parsers.go b/client/command/armory/parsers.go index d4ca08f92..4e26e7cc6 100644 --- a/client/command/armory/parsers.go +++ b/client/command/armory/parsers.go @@ -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 diff --git a/go-tests.sh b/go-tests.sh index b98fd6236..aaf6b94dc 100755 --- a/go-tests.sh +++ b/go-tests.sh @@ -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 : diff --git a/server/cryptography/cryptography.go b/server/cryptography/cryptography.go index cc23d67e9..8770d909b 100644 --- a/server/cryptography/cryptography.go +++ b/server/cryptography/cryptography.go @@ -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" ) diff --git a/server/cryptography/cryptography_test.go b/server/cryptography/cryptography_test.go index 336a580e4..ba25d44b5 100644 --- a/server/cryptography/cryptography_test.go +++ b/server/cryptography/cryptography_test.go @@ -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 ( diff --git a/server/cryptography/minisign/LICENSE b/server/cryptography/minisign/LICENSE deleted file mode 100644 index 694902ae0..000000000 --- a/server/cryptography/minisign/LICENSE +++ /dev/null @@ -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. diff --git a/server/cryptography/minisign/internal/testdata/message.txt b/server/cryptography/minisign/internal/testdata/message.txt deleted file mode 100644 index 980a0d5f1..000000000 --- a/server/cryptography/minisign/internal/testdata/message.txt +++ /dev/null @@ -1 +0,0 @@ -Hello World! diff --git a/server/cryptography/minisign/internal/testdata/message.txt.minisig b/server/cryptography/minisign/internal/testdata/message.txt.minisig deleted file mode 100644 index 84f1b3507..000000000 --- a/server/cryptography/minisign/internal/testdata/message.txt.minisig +++ /dev/null @@ -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== diff --git a/server/cryptography/minisign/internal/testdata/minisign.key b/server/cryptography/minisign/internal/testdata/minisign.key deleted file mode 100644 index 225d97435..000000000 --- a/server/cryptography/minisign/internal/testdata/minisign.key +++ /dev/null @@ -1,2 +0,0 @@ -untrusted comment: minisign encrypted secret key -RWRTY0Iytaz5znJmUO5kBt5xVkvpBl+29A7pZH86phD4h8vD3V8AAAACAAAAAAAAAEAAAAAA9vH9EcS6NdXNIEGhYGoqG1CiL4aptyJreJ4IfuT4+1h+OgVaY/vi0HsbCP0Y6n/wcy0AN0wOXmVDPP33jZqv82YCj2fH+/6MRuAfzNQYoLvc3sH/8bIwqdfpKIjDRZhvqRf063RFYoI= diff --git a/server/cryptography/minisign/internal/testdata/minisign.pub b/server/cryptography/minisign/internal/testdata/minisign.pub deleted file mode 100644 index 9ec68e8e8..000000000 --- a/server/cryptography/minisign/internal/testdata/minisign.pub +++ /dev/null @@ -1,2 +0,0 @@ -untrusted comment: minisign public key C373193807678450 -RWRQhGcHOBlzw4CoKyugkk4ioDfoxlXxC9LBx+VNhJ3w9w+cAxgvPsuo diff --git a/server/cryptography/minisign/internal/testdata/robtest.ps1.minisig b/server/cryptography/minisign/internal/testdata/robtest.ps1.minisig deleted file mode 100644 index 7d09aa4c4..000000000 --- a/server/cryptography/minisign/internal/testdata/robtest.ps1.minisig +++ /dev/null @@ -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== diff --git a/server/cryptography/minisign/minisign.go b/server/cryptography/minisign/minisign.go deleted file mode 100644 index a365b8950..000000000 --- a/server/cryptography/minisign/minisign.go +++ /dev/null @@ -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] -} diff --git a/server/cryptography/minisign/minisign_test.go b/server/cryptography/minisign/minisign_test.go deleted file mode 100644 index a5423cafd..000000000 --- a/server/cryptography/minisign/minisign_test.go +++ /dev/null @@ -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) - } - -} diff --git a/server/cryptography/minisign/private.go b/server/cryptography/minisign/private.go deleted file mode 100644 index e8ff2b6ad..000000000 --- a/server/cryptography/minisign/private.go +++ /dev/null @@ -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 -} diff --git a/server/cryptography/minisign/public.go b/server/cryptography/minisign/public.go deleted file mode 100644 index b11cad08d..000000000 --- a/server/cryptography/minisign/public.go +++ /dev/null @@ -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 -} diff --git a/server/cryptography/minisign/public_test.go b/server/cryptography/minisign/public_test.go deleted file mode 100644 index 9dc18a97a..000000000 --- a/server/cryptography/minisign/public_test.go +++ /dev/null @@ -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) - } - } - } -} diff --git a/server/cryptography/minisign/rawsig_test.go b/server/cryptography/minisign/rawsig_test.go deleted file mode 100644 index 8a11e26b9..000000000 --- a/server/cryptography/minisign/rawsig_test.go +++ /dev/null @@ -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 -} diff --git a/server/cryptography/minisign/signature.go b/server/cryptography/minisign/signature.go deleted file mode 100644 index 3a4e6ecf3..000000000 --- a/server/cryptography/minisign/signature.go +++ /dev/null @@ -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 -} diff --git a/server/cryptography/minisign/signature_test.go b/server/cryptography/minisign/signature_test.go deleted file mode 100644 index aa1f3e3c1..000000000 --- a/server/cryptography/minisign/signature_test.go +++ /dev/null @@ -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 - }, -} diff --git a/client/minisign/LICENSE b/util/minisign/LICENSE similarity index 100% rename from client/minisign/LICENSE rename to util/minisign/LICENSE diff --git a/client/minisign/internal/testdata/message.txt b/util/minisign/internal/testdata/message.txt similarity index 100% rename from client/minisign/internal/testdata/message.txt rename to util/minisign/internal/testdata/message.txt diff --git a/client/minisign/internal/testdata/message.txt.minisig b/util/minisign/internal/testdata/message.txt.minisig similarity index 100% rename from client/minisign/internal/testdata/message.txt.minisig rename to util/minisign/internal/testdata/message.txt.minisig diff --git a/client/minisign/internal/testdata/minisign.key b/util/minisign/internal/testdata/minisign.key similarity index 100% rename from client/minisign/internal/testdata/minisign.key rename to util/minisign/internal/testdata/minisign.key diff --git a/client/minisign/internal/testdata/minisign.pub b/util/minisign/internal/testdata/minisign.pub similarity index 100% rename from client/minisign/internal/testdata/minisign.pub rename to util/minisign/internal/testdata/minisign.pub diff --git a/client/minisign/internal/testdata/robtest.ps1.minisig b/util/minisign/internal/testdata/robtest.ps1.minisig similarity index 100% rename from client/minisign/internal/testdata/robtest.ps1.minisig rename to util/minisign/internal/testdata/robtest.ps1.minisig diff --git a/client/minisign/minisign.go b/util/minisign/minisign.go similarity index 100% rename from client/minisign/minisign.go rename to util/minisign/minisign.go diff --git a/client/minisign/minisign_test.go b/util/minisign/minisign_test.go similarity index 100% rename from client/minisign/minisign_test.go rename to util/minisign/minisign_test.go diff --git a/client/minisign/private.go b/util/minisign/private.go similarity index 100% rename from client/minisign/private.go rename to util/minisign/private.go diff --git a/client/minisign/public.go b/util/minisign/public.go similarity index 100% rename from client/minisign/public.go rename to util/minisign/public.go diff --git a/client/minisign/public_test.go b/util/minisign/public_test.go similarity index 100% rename from client/minisign/public_test.go rename to util/minisign/public_test.go diff --git a/client/minisign/rawsig_test.go b/util/minisign/rawsig_test.go similarity index 100% rename from client/minisign/rawsig_test.go rename to util/minisign/rawsig_test.go diff --git a/client/minisign/signature.go b/util/minisign/signature.go similarity index 100% rename from client/minisign/signature.go rename to util/minisign/signature.go diff --git a/client/minisign/signature_test.go b/util/minisign/signature_test.go similarity index 100% rename from client/minisign/signature_test.go rename to util/minisign/signature_test.go