feat: Add signature verification support when restoring

This commit is contained in:
Felicitas Pojtinger
2021-12-04 19:17:59 +01:00
parent ce7c9df3ac
commit 75ee70dc4c
5 changed files with 132 additions and 23 deletions

View File

@@ -150,7 +150,7 @@ var archiveCmd = &cobra.Command{
viper.GetBool(overwriteFlag),
viper.GetString(compressionFlag),
viper.GetString(encryptionFlag),
func(hdr *tar.Header, encryptionFormat string, i int) error {
func(hdr *tar.Header, i int) error {
if len(hdrs) <= i {
return errMissingTarHeader
}

View File

@@ -13,6 +13,7 @@ import (
"os"
"path/filepath"
"aead.dev/minisign"
"filippo.io/age"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/andybalholm/brotli"
@@ -40,6 +41,8 @@ var (
errEmbeddedHeaderMissing = errors.New("embedded header is missing")
errIdentityUnparsable = errors.New("recipient could not be parsed")
errInvalidSignature = errors.New("invalid signature")
)
var recoveryFetchCmd = &cobra.Command{
@@ -50,7 +53,11 @@ var recoveryFetchCmd = &cobra.Command{
return err
}
return checkKeyAccessible(viper.GetString(encryptionFlag), viper.GetString(identityFlag))
if err := checkKeyAccessible(viper.GetString(encryptionFlag), viper.GetString(identityFlag)); err != nil {
return err
}
return checkKeyAccessible(viper.GetString(signatureFlag), viper.GetString(recipientFlag))
},
RunE: func(cmd *cobra.Command, args []string) error {
if err := viper.BindPFlags(cmd.PersistentFlags()); err != nil {
@@ -61,6 +68,16 @@ var recoveryFetchCmd = &cobra.Command{
boil.DebugMode = true
}
pubkey, err := readKey(viper.GetString(signatureFlag), viper.GetString(recipientFlag))
if err != nil {
return err
}
recipient, err := parseSignerRecipient(viper.GetString(signatureFlag), pubkey)
if err != nil {
return err
}
privkey, err := readKey(viper.GetString(encryptionFlag), viper.GetString(identityFlag))
if err != nil {
return err
@@ -82,6 +99,8 @@ var recoveryFetchCmd = &cobra.Command{
viper.GetString(compressionFlag),
viper.GetString(encryptionFlag),
identity,
viper.GetString(signatureFlag),
recipient,
)
},
}
@@ -97,6 +116,8 @@ func restoreFromRecordAndBlock(
compressionFormat string,
encryptionFormat string,
identity interface{},
signatureFormat string,
recipient interface{},
) error {
f, isRegular, err := openTapeReadOnly(tape)
if err != nil {
@@ -183,7 +204,23 @@ func restoreFromRecordAndBlock(
return err
}
if _, err := io.Copy(dstFile, decompressor); err != nil {
signature := ""
if hdr.PAXRecords != nil {
if s, ok := hdr.PAXRecords[pax.STFSRecordSignature]; ok {
signature = s
}
}
verifier, verify, err := verify(decompressor, signatureFormat, recipient, signature)
if err != nil {
return err
}
if _, err := io.Copy(dstFile, verifier); err != nil {
return err
}
if err := verify(); err != nil {
return err
}
@@ -429,6 +466,61 @@ func decrypt(
}
}
func parseSignerRecipient(
signatureFormat string,
pubkey []byte,
) (interface{}, error) {
switch signatureFormat {
case signatureFormatMinisignKey:
var recipient minisign.PublicKey
if err := recipient.UnmarshalText(pubkey); err != nil {
return nil, err
}
return recipient, nil
case noneKey:
return pubkey, nil
default:
return nil, errUnsupportedSignatureFormat
}
}
func verify(
src io.Reader,
signatureFormat string,
recipient interface{},
signature string,
) (io.Reader, func() error, error) {
switch signatureFormat {
case signatureFormatMinisignKey:
recipient, ok := recipient.(minisign.PublicKey)
if !ok {
return nil, nil, errRecipientUnparsable
}
verifier := minisign.NewReader(src)
return verifier, func() error {
decodedSignature, err := base64.StdEncoding.DecodeString(signature)
if err != nil {
return err
}
if verifier.Verify(recipient, decodedSignature) {
return nil
}
return errInvalidSignature
}, nil
case noneKey:
return io.NopCloser(src), func() error {
return nil
}, nil
default:
return nil, nil, errUnsupportedSignatureFormat
}
}
func init() {
recoveryFetchCmd.PersistentFlags().IntP(recordSizeFlag, "z", 20, "Amount of 512-bit blocks per record")
recoveryFetchCmd.PersistentFlags().IntP(recordFlag, "k", 0, "Record to seek too")
@@ -437,6 +529,7 @@ func init() {
recoveryFetchCmd.PersistentFlags().BoolP(previewFlag, "w", false, "Only read the header")
recoveryFetchCmd.PersistentFlags().StringP(identityFlag, "i", "", "Path to private key of recipient that has been encrypted for")
recoveryFetchCmd.PersistentFlags().StringP(passwordFlag, "p", "", "Password for the private key")
recoveryFetchCmd.PersistentFlags().StringP(recipientFlag, "r", "", "Path to the public key to verify with")
viper.AutomaticEnv()

View File

@@ -61,8 +61,8 @@ var recoveryIndexCmd = &cobra.Command{
viper.GetBool(overwriteFlag),
viper.GetString(compressionFlag),
viper.GetString(encryptionFlag),
func(hdr *tar.Header, encryptionFormat string, i int) error {
return decryptHeader(hdr, encryptionFormat, identity)
func(hdr *tar.Header, i int) error {
return decryptHeader(hdr, viper.GetString(encryptionFlag), identity)
},
0,
)
@@ -80,7 +80,6 @@ func index(
encryptionFormat string,
decryptHeader func(
hdr *tar.Header,
encryptionFormat string,
i int,
) error,
offset int,
@@ -173,7 +172,7 @@ func index(
}
if i >= offset {
if err := decryptHeader(hdr, encryptionFormat, i-offset); err != nil {
if err := decryptHeader(hdr, i-offset); err != nil {
return err
}
@@ -255,7 +254,7 @@ func index(
}
if i >= offset {
if err := decryptHeader(hdr, encryptionFormat, i-offset); err != nil {
if err := decryptHeader(hdr, i-offset); err != nil {
return err
}
@@ -288,19 +287,6 @@ func index(
return nil
}
func init() {
recoveryIndexCmd.PersistentFlags().IntP(recordSizeFlag, "z", 20, "Amount of 512-bit blocks per record")
recoveryIndexCmd.PersistentFlags().IntP(recordFlag, "k", 0, "Record to seek too before counting")
recoveryIndexCmd.PersistentFlags().IntP(blockFlag, "b", 0, "Block in record to seek too before counting")
recoveryIndexCmd.PersistentFlags().BoolP(overwriteFlag, "o", false, "Remove the old index before starting to index")
recoveryIndexCmd.PersistentFlags().StringP(identityFlag, "i", "", "Path to private key of recipient that has been encrypted for")
recoveryIndexCmd.PersistentFlags().StringP(passwordFlag, "p", "", "Password for the private key")
viper.AutomaticEnv()
recoveryCmd.AddCommand(recoveryIndexCmd)
}
func indexHeader(
record, block int64,
hdr *tar.Header,
@@ -472,3 +458,16 @@ func openTapeReadOnly(tape string) (f *os.File, isRegular bool, err error) {
return f, isRegular, nil
}
func init() {
recoveryIndexCmd.PersistentFlags().IntP(recordSizeFlag, "z", 20, "Amount of 512-bit blocks per record")
recoveryIndexCmd.PersistentFlags().IntP(recordFlag, "k", 0, "Record to seek too before counting")
recoveryIndexCmd.PersistentFlags().IntP(blockFlag, "b", 0, "Block in record to seek too before counting")
recoveryIndexCmd.PersistentFlags().BoolP(overwriteFlag, "o", false, "Remove the old index before starting to index")
recoveryIndexCmd.PersistentFlags().StringP(identityFlag, "i", "", "Path to private key of recipient that has been encrypted for")
recoveryIndexCmd.PersistentFlags().StringP(passwordFlag, "p", "", "Password for the private key")
viper.AutomaticEnv()
recoveryCmd.AddCommand(recoveryIndexCmd)
}

View File

@@ -30,7 +30,11 @@ var restoreCmd = &cobra.Command{
return err
}
return checkKeyAccessible(viper.GetString(encryptionFlag), viper.GetString(identityFlag))
if err := checkKeyAccessible(viper.GetString(encryptionFlag), viper.GetString(identityFlag)); err != nil {
return err
}
return checkKeyAccessible(viper.GetString(signatureFlag), viper.GetString(recipientFlag))
},
RunE: func(cmd *cobra.Command, args []string) error {
if err := viper.BindPFlags(cmd.PersistentFlags()); err != nil {
@@ -46,6 +50,16 @@ var restoreCmd = &cobra.Command{
return err
}
pubkey, err := readKey(viper.GetString(signatureFlag), viper.GetString(recipientFlag))
if err != nil {
return err
}
recipient, err := parseSignerRecipient(viper.GetString(signatureFlag), pubkey)
if err != nil {
return err
}
privkey, err := readKey(viper.GetString(encryptionFlag), viper.GetString(identityFlag))
if err != nil {
return err
@@ -123,6 +137,8 @@ var restoreCmd = &cobra.Command{
viper.GetString(compressionFlag),
viper.GetString(encryptionFlag),
identity,
viper.GetString(signatureFlag),
recipient,
); err != nil {
return err
}
@@ -139,6 +155,7 @@ func init() {
restoreCmd.PersistentFlags().BoolP(flattenFlag, "a", false, "Ignore the folder hierarchy on the tape or tar file")
restoreCmd.PersistentFlags().StringP(identityFlag, "i", "", "Path to private key of recipient that has been encrypted for")
restoreCmd.PersistentFlags().StringP(passwordFlag, "p", "", "Password for the private key")
restoreCmd.PersistentFlags().StringP(recipientFlag, "r", "", "Path to the public key to verify with")
viper.AutomaticEnv()

View File

@@ -88,7 +88,7 @@ var updateCmd = &cobra.Command{
false,
viper.GetString(compressionFlag),
viper.GetString(encryptionFlag),
func(hdr *tar.Header, encryptionFormat string, i int) error {
func(hdr *tar.Header, i int) error {
if len(hdrs) <= i {
return errMissingTarHeader
}