release/dist/cli: add verify-package-signature command (#9110)

Helper command to verify package signatures, mainly for debugging.
Also fix a copy-paste mistake in error message in distsign.

Updates #8760

Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
pull/9111/head
Andrew Lytvynov 1 year ago committed by GitHub
parent c86a610eb3
commit 18d9c92342
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -224,7 +224,7 @@ func (c *Client) Download(srcPath, dstPath string) error {
if !VerifyAny(sigPub, msg, sig) { if !VerifyAny(sigPub, msg, sig) {
// Best-effort clean up of downloaded package. // Best-effort clean up of downloaded package.
os.Remove(dstPathUnverified) os.Remove(dstPathUnverified)
return fmt.Errorf("signature %q for key %q does not validate with the current release signing key; either you are under attack, or attempting to download an old version of Tailscale which was signed with an older signing key", sigURL, srcURL) return fmt.Errorf("signature %q for file %q does not validate with the current release signing key; either you are under attack, or attempting to download an old version of Tailscale which was signed with an older signing key", sigURL, srcURL)
} }
if err := os.Rename(dstPathUnverified, dstPath); err != nil { if err := os.Rename(dstPathUnverified, dstPath); err != nil {

@ -8,10 +8,12 @@ import (
"context" "context"
"crypto" "crypto"
"crypto/x509" "crypto/x509"
"encoding/binary"
"encoding/pem" "encoding/pem"
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
"io"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -119,6 +121,21 @@ func CLI(getTargets func() ([]dist.Target, error)) *ffcli.Command {
return fs return fs
})(), })(),
}, },
{
Name: "verify-package-signature",
Exec: func(ctx context.Context, args []string) error {
return runVerifyPackageSignature(ctx)
},
ShortUsage: "dist verify-package-signature",
ShortHelp: "Verify a package signture using a signing key",
FlagSet: (func() *flag.FlagSet {
fs := flag.NewFlagSet("verify-package-signature", flag.ExitOnError)
fs.StringVar(&verifyPackageSignatureArgs.signPubPath, "sign-pub-path", "signing-public-key.pem", "path to the signing public key; this can be a bundle of multiple keys")
fs.StringVar(&verifyPackageSignatureArgs.packagePath, "package-path", "", "path to the package that was signed")
fs.StringVar(&verifyPackageSignatureArgs.sigPath, "sig-path", "signature.bin", "path to the signature file")
return fs
})(),
},
}, },
Exec: func(context.Context, []string) error { return flag.ErrHelp }, Exec: func(context.Context, []string) error { return flag.ErrHelp },
} }
@ -287,19 +304,20 @@ var verifyKeySignatureArgs struct {
} }
func runVerifyKeySignature(ctx context.Context) error { func runVerifyKeySignature(ctx context.Context) error {
rootPubBundle, err := os.ReadFile(verifyKeySignatureArgs.rootPubPath) args := verifyKeySignatureArgs
rootPubBundle, err := os.ReadFile(args.rootPubPath)
if err != nil { if err != nil {
return err return err
} }
rootPubs, err := distsign.ParseRootKeyBundle(rootPubBundle) rootPubs, err := distsign.ParseRootKeyBundle(rootPubBundle)
if err != nil { if err != nil {
return fmt.Errorf("parsing %q: %w", verifyKeySignatureArgs.rootPubPath, err) return fmt.Errorf("parsing %q: %w", args.rootPubPath, err)
} }
signPubBundle, err := os.ReadFile(verifyKeySignatureArgs.signPubPath) signPubBundle, err := os.ReadFile(args.signPubPath)
if err != nil { if err != nil {
return err return err
} }
sig, err := os.ReadFile(verifyKeySignatureArgs.sigPath) sig, err := os.ReadFile(args.sigPath)
if err != nil { if err != nil {
return err return err
} }
@ -309,3 +327,40 @@ func runVerifyKeySignature(ctx context.Context) error {
fmt.Println("signature ok") fmt.Println("signature ok")
return nil return nil
} }
var verifyPackageSignatureArgs struct {
signPubPath string
packagePath string
sigPath string
}
func runVerifyPackageSignature(ctx context.Context) error {
args := verifyPackageSignatureArgs
signPubBundle, err := os.ReadFile(args.signPubPath)
if err != nil {
return err
}
signPubs, err := distsign.ParseSigningKeyBundle(signPubBundle)
if err != nil {
return fmt.Errorf("parsing %q: %w", args.signPubPath, err)
}
pkg, err := os.Open(args.packagePath)
if err != nil {
return err
}
defer pkg.Close()
pkgHash := distsign.NewPackageHash()
if _, err := io.Copy(pkgHash, pkg); err != nil {
return fmt.Errorf("reading %q: %w", args.packagePath, err)
}
hash := binary.LittleEndian.AppendUint64(pkgHash.Sum(nil), uint64(pkgHash.Len()))
sig, err := os.ReadFile(args.sigPath)
if err != nil {
return err
}
if !distsign.VerifyAny(signPubs, hash, sig) {
return errors.New("signature not valid")
}
fmt.Println("signature ok")
return nil
}

Loading…
Cancel
Save