feat: Add basic signature support to archive cmd based on Minisign
This commit is contained in:
@@ -16,6 +16,7 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"aead.dev/minisign"
|
||||
"filippo.io/age"
|
||||
"github.com/ProtonMail/go-crypto/openpgp"
|
||||
"github.com/andybalholm/brotli"
|
||||
@@ -37,7 +38,7 @@ import (
|
||||
|
||||
const (
|
||||
recordSizeFlag = "record-size"
|
||||
srcFlag = "src"
|
||||
fromFlag = "from"
|
||||
overwriteFlag = "overwrite"
|
||||
compressionLevelFlag = "compression-level"
|
||||
|
||||
@@ -76,7 +77,11 @@ var archiveCmd = &cobra.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
return checkKeyAccessible(viper.GetString(encryptionFlag), viper.GetString(recipientFlag))
|
||||
if err := checkKeyAccessible(viper.GetString(encryptionFlag), viper.GetString(recipientFlag)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkKeyAccessible(viper.GetString(signatureFlag), viper.GetString(identityFlag))
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if viper.GetBool(verboseFlag) {
|
||||
@@ -110,22 +115,34 @@ var archiveCmd = &cobra.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
privkey, err := readKey(viper.GetString(signatureFlag), viper.GetString(identityFlag))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
identity, err := parseSignerIdentity(viper.GetString(signatureFlag), privkey, viper.GetString(passwordFlag))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hdrs, err := archive(
|
||||
viper.GetString(tapeFlag),
|
||||
viper.GetString(driveFlag),
|
||||
viper.GetInt(recordSizeFlag),
|
||||
viper.GetString(srcFlag),
|
||||
viper.GetString(fromFlag),
|
||||
viper.GetBool(overwriteFlag),
|
||||
viper.GetString(compressionFlag),
|
||||
viper.GetString(compressionLevelFlag),
|
||||
viper.GetString(encryptionFlag),
|
||||
recipient,
|
||||
viper.GetString(signatureFlag),
|
||||
identity,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return index(
|
||||
viper.GetString(tapeFlag),
|
||||
viper.GetString(driveFlag),
|
||||
viper.GetString(metadataFlag),
|
||||
viper.GetInt(recordSizeFlag),
|
||||
int(lastIndexedRecord),
|
||||
@@ -156,6 +173,8 @@ func archive(
|
||||
compressionLevel string,
|
||||
encryptionFormat string,
|
||||
recipient interface{},
|
||||
signatureFormat string,
|
||||
identity interface{},
|
||||
) ([]*tar.Header, error) {
|
||||
dirty := false
|
||||
tw, isRegular, cleanup, err := openTapeWriter(tape)
|
||||
@@ -268,13 +287,18 @@ func archive(
|
||||
return err
|
||||
}
|
||||
|
||||
signer, sign, err := sign(file, signatureFormat, identity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isRegular {
|
||||
if _, err := io.Copy(compressor, file); err != nil {
|
||||
if _, err := io.Copy(compressor, signer); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
buf := make([]byte, controllers.BlockSize*recordSize)
|
||||
if _, err := io.CopyBuffer(compressor, file, buf); err != nil {
|
||||
if _, err := io.CopyBuffer(compressor, signer, buf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -299,6 +323,9 @@ func archive(
|
||||
hdr.PAXRecords = map[string]string{}
|
||||
}
|
||||
hdr.PAXRecords[pax.STFSRecordUncompressedSize] = strconv.Itoa(int(hdr.Size))
|
||||
if signature := sign(); signature != "" {
|
||||
hdr.PAXRecords[pax.STFSRecordSignature] = signature
|
||||
}
|
||||
hdr.Size = int64(fileSizeCounter.BytesRead)
|
||||
|
||||
hdr.Name, err = addSuffix(hdr.Name, compressionFormat, encryptionFormat)
|
||||
@@ -390,7 +417,7 @@ func archive(
|
||||
}
|
||||
|
||||
func checkKeyAccessible(encryptionFormat string, pathToKey string) error {
|
||||
if encryptionFormat == encryptionFormatNoneKey {
|
||||
if encryptionFormat == noneKey {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -402,7 +429,7 @@ func checkKeyAccessible(encryptionFormat string, pathToKey string) error {
|
||||
}
|
||||
|
||||
func readKey(encryptionFormat string, pathToKey string) ([]byte, error) {
|
||||
if encryptionFormat == encryptionFormatNoneKey {
|
||||
if encryptionFormat == noneKey {
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
@@ -430,7 +457,7 @@ func encryptHeader(
|
||||
encryptionFormat string,
|
||||
recipient interface{},
|
||||
) error {
|
||||
if encryptionFormat == encryptionFormatNoneKey {
|
||||
if encryptionFormat == noneKey {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -471,7 +498,7 @@ func addSuffix(name string, compressionFormat string, encryptionFormat string) (
|
||||
fallthrough
|
||||
case compressionFormatBzip2ParallelKey:
|
||||
name += compressionFormatBzip2Suffix
|
||||
case compressionFormatNoneKey:
|
||||
case noneKey:
|
||||
default:
|
||||
return "", errUnsupportedCompressionFormat
|
||||
}
|
||||
@@ -481,7 +508,7 @@ func addSuffix(name string, compressionFormat string, encryptionFormat string) (
|
||||
name += encryptionFormatAgeSuffix
|
||||
case encryptionFormatPGPKey:
|
||||
name += encryptionFormatPGPSuffix
|
||||
case compressionFormatNoneKey:
|
||||
case noneKey:
|
||||
default:
|
||||
return "", errUnsupportedEncryptionFormat
|
||||
}
|
||||
@@ -498,7 +525,7 @@ func parseRecipient(
|
||||
return age.ParseX25519Recipient(string(pubkey))
|
||||
case encryptionFormatPGPKey:
|
||||
return openpgp.ReadKeyRing(bytes.NewBuffer(pubkey))
|
||||
case encryptionFormatNoneKey:
|
||||
case noneKey:
|
||||
return pubkey, nil
|
||||
default:
|
||||
return nil, errUnsupportedEncryptionFormat
|
||||
@@ -525,13 +552,54 @@ func encrypt(
|
||||
}
|
||||
|
||||
return openpgp.Encrypt(dst, recipient, nil, nil, nil)
|
||||
case encryptionFormatNoneKey:
|
||||
case noneKey:
|
||||
return noop.AddClose(dst), nil
|
||||
default:
|
||||
return nil, errUnsupportedEncryptionFormat
|
||||
}
|
||||
}
|
||||
|
||||
func parseSignerIdentity(
|
||||
signatureFormat string,
|
||||
privkey []byte,
|
||||
password string,
|
||||
) (interface{}, error) {
|
||||
switch signatureFormat {
|
||||
case signatureFormatMinisignKey:
|
||||
return minisign.DecryptKey(password, privkey)
|
||||
case noneKey:
|
||||
return privkey, nil
|
||||
default:
|
||||
return nil, errUnsupportedSignatureFormat
|
||||
}
|
||||
}
|
||||
|
||||
func sign(
|
||||
src io.Reader,
|
||||
signatureFormat string,
|
||||
identity interface{},
|
||||
) (io.Reader, func() string, error) {
|
||||
switch signatureFormat {
|
||||
case signatureFormatMinisignKey:
|
||||
identity, ok := identity.(minisign.PrivateKey)
|
||||
if !ok {
|
||||
return nil, nil, errIdentityUnparsable
|
||||
}
|
||||
|
||||
signer := minisign.NewReader(src)
|
||||
|
||||
return signer, func() string {
|
||||
return base64.StdEncoding.EncodeToString(signer.Sign(identity))
|
||||
}, nil
|
||||
case noneKey:
|
||||
return io.NopCloser(src), func() string {
|
||||
return ""
|
||||
}, nil
|
||||
default:
|
||||
return nil, nil, errUnsupportedSignatureFormat
|
||||
}
|
||||
}
|
||||
|
||||
func encryptString(
|
||||
src string,
|
||||
encryptionFormat string,
|
||||
@@ -580,7 +648,7 @@ func encryptString(
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString(out.Bytes()), nil
|
||||
case encryptionFormatNoneKey:
|
||||
case noneKey:
|
||||
return src, nil
|
||||
default:
|
||||
return "", errUnsupportedEncryptionFormat
|
||||
@@ -704,7 +772,7 @@ func compress(
|
||||
}
|
||||
|
||||
return noop.AddFlush(bz), nil
|
||||
case compressionFormatNoneKey:
|
||||
case noneKey:
|
||||
return noop.AddFlush(noop.AddClose(dst)), nil
|
||||
default:
|
||||
return nil, errUnsupportedCompressionFormat
|
||||
@@ -713,10 +781,12 @@ func compress(
|
||||
|
||||
func init() {
|
||||
archiveCmd.PersistentFlags().IntP(recordSizeFlag, "z", 20, "Amount of 512-bit blocks per record")
|
||||
archiveCmd.PersistentFlags().StringP(srcFlag, "s", ".", "File or directory to archive")
|
||||
archiveCmd.PersistentFlags().StringP(fromFlag, "f", ".", "File or directory to archive")
|
||||
archiveCmd.PersistentFlags().BoolP(overwriteFlag, "o", false, "Start writing from the start instead of from the end of the tape or tar file")
|
||||
archiveCmd.PersistentFlags().StringP(compressionLevelFlag, "l", compressionLevelBalanced, fmt.Sprintf("Compression level to use (default %v, available are %v)", compressionLevelBalanced, knownCompressionLevels))
|
||||
archiveCmd.PersistentFlags().StringP(recipientFlag, "r", "", "Path to public key of recipient to encrypt for")
|
||||
archiveCmd.PersistentFlags().StringP(identityFlag, "i", "", "Path to private key to sign with")
|
||||
archiveCmd.PersistentFlags().StringP(passwordFlag, "p", "", "Password for the private key")
|
||||
|
||||
viper.AutomaticEnv()
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ var deleteCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
return delete(
|
||||
viper.GetString(tapeFlag),
|
||||
viper.GetString(driveFlag),
|
||||
viper.GetString(metadataFlag),
|
||||
viper.GetString(nameFlag),
|
||||
viper.GetString(encryptionFlag),
|
||||
|
||||
@@ -21,7 +21,7 @@ var driveEjectCmd = &cobra.Command{
|
||||
boil.DebugMode = true
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(viper.GetString(tapeFlag), os.O_RDONLY, os.ModeCharDevice)
|
||||
f, err := os.OpenFile(viper.GetString(driveFlag), os.O_RDONLY, os.ModeCharDevice)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ var driveTellCmd = &cobra.Command{
|
||||
boil.DebugMode = true
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(viper.GetString(tapeFlag), os.O_RDONLY, os.ModeCharDevice)
|
||||
f, err := os.OpenFile(viper.GetString(driveFlag), os.O_RDONLY, os.ModeCharDevice)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -46,10 +46,10 @@ var moveCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
return move(
|
||||
viper.GetString(tapeFlag),
|
||||
viper.GetString(driveFlag),
|
||||
viper.GetString(metadataFlag),
|
||||
viper.GetString(srcFlag),
|
||||
viper.GetString(dstFlag),
|
||||
viper.GetString(fromFlag),
|
||||
viper.GetString(toFlag),
|
||||
viper.GetString(encryptionFlag),
|
||||
recipient,
|
||||
)
|
||||
@@ -135,8 +135,8 @@ func move(
|
||||
|
||||
func init() {
|
||||
moveCmd.PersistentFlags().IntP(recordSizeFlag, "z", 20, "Amount of 512-bit blocks per record")
|
||||
moveCmd.PersistentFlags().StringP(srcFlag, "s", "", "Current path of the file or directory to move")
|
||||
moveCmd.PersistentFlags().StringP(dstFlag, "d", "", "Path to move the file or directory to")
|
||||
moveCmd.PersistentFlags().StringP(fromFlag, "f", "", "Current path of the file or directory to move")
|
||||
moveCmd.PersistentFlags().StringP(toFlag, "t", "", "Path to move the file or directory to")
|
||||
moveCmd.PersistentFlags().StringP(recipientFlag, "r", "", "Path to public key of recipient to encrypt for")
|
||||
|
||||
viper.AutomaticEnv()
|
||||
|
||||
@@ -32,7 +32,7 @@ import (
|
||||
const (
|
||||
recordFlag = "record"
|
||||
blockFlag = "block"
|
||||
dstFlag = "dst"
|
||||
toFlag = "to"
|
||||
previewFlag = "preview"
|
||||
)
|
||||
|
||||
@@ -72,11 +72,11 @@ var recoveryFetchCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
return restoreFromRecordAndBlock(
|
||||
viper.GetString(tapeFlag),
|
||||
viper.GetString(driveFlag),
|
||||
viper.GetInt(recordSizeFlag),
|
||||
viper.GetInt(recordFlag),
|
||||
viper.GetInt(blockFlag),
|
||||
viper.GetString(dstFlag),
|
||||
viper.GetString(toFlag),
|
||||
viper.GetBool(previewFlag),
|
||||
true,
|
||||
viper.GetString(compressionFlag),
|
||||
@@ -240,7 +240,7 @@ func decompress(
|
||||
bz := pbzip2.NewReader(context.Background(), src)
|
||||
|
||||
return io.NopCloser(bz), nil
|
||||
case compressionFormatNoneKey:
|
||||
case noneKey:
|
||||
return io.NopCloser(src), nil
|
||||
default:
|
||||
return nil, errUnsupportedCompressionFormat
|
||||
@@ -252,7 +252,7 @@ func decryptHeader(
|
||||
encryptionFormat string,
|
||||
identity interface{},
|
||||
) error {
|
||||
if encryptionFormat == encryptionFormatNoneKey {
|
||||
if encryptionFormat == noneKey {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -328,7 +328,7 @@ func parseIdentity(
|
||||
}
|
||||
|
||||
return identities, nil
|
||||
case encryptionFormatNoneKey:
|
||||
case noneKey:
|
||||
return privkey, nil
|
||||
default:
|
||||
return nil, errUnsupportedEncryptionFormat
|
||||
@@ -385,7 +385,7 @@ func decryptString(
|
||||
}
|
||||
|
||||
return out.String(), nil
|
||||
case encryptionFormatNoneKey:
|
||||
case noneKey:
|
||||
return src, nil
|
||||
default:
|
||||
return "", errUnsupportedEncryptionFormat
|
||||
@@ -422,7 +422,7 @@ func decrypt(
|
||||
}
|
||||
|
||||
return io.NopCloser(r.UnverifiedBody), nil
|
||||
case encryptionFormatNoneKey:
|
||||
case noneKey:
|
||||
return io.NopCloser(src), nil
|
||||
default:
|
||||
return nil, errUnsupportedEncryptionFormat
|
||||
@@ -433,7 +433,7 @@ 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")
|
||||
recoveryFetchCmd.PersistentFlags().IntP(blockFlag, "b", 0, "Block in record to seek too")
|
||||
recoveryFetchCmd.PersistentFlags().StringP(dstFlag, "d", "", "File to restore to (archived name by default)")
|
||||
recoveryFetchCmd.PersistentFlags().StringP(toFlag, "t", "", "File to restore to (archived name by default)")
|
||||
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")
|
||||
|
||||
@@ -53,7 +53,7 @@ var recoveryIndexCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
return index(
|
||||
viper.GetString(tapeFlag),
|
||||
viper.GetString(driveFlag),
|
||||
viper.GetString(metadataFlag),
|
||||
viper.GetInt(recordSizeFlag),
|
||||
viper.GetInt(recordFlag),
|
||||
@@ -421,7 +421,7 @@ func removeSuffix(name string, compressionFormat string, encryptionFormat string
|
||||
name = strings.TrimSuffix(name, encryptionFormatAgeSuffix)
|
||||
case encryptionFormatPGPKey:
|
||||
name = strings.TrimSuffix(name, encryptionFormatPGPSuffix)
|
||||
case encryptionFormatNoneKey:
|
||||
case noneKey:
|
||||
default:
|
||||
return "", errUnsupportedEncryptionFormat
|
||||
}
|
||||
@@ -441,7 +441,7 @@ func removeSuffix(name string, compressionFormat string, encryptionFormat string
|
||||
fallthrough
|
||||
case compressionFormatBzip2ParallelKey:
|
||||
name = strings.TrimSuffix(name, compressionFormatBzip2Suffix)
|
||||
case compressionFormatNoneKey:
|
||||
case noneKey:
|
||||
default:
|
||||
return "", errUnsupportedCompressionFormat
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ var recoveryQueryCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
return query(
|
||||
viper.GetString(tapeFlag),
|
||||
viper.GetString(driveFlag),
|
||||
viper.GetInt(recordFlag),
|
||||
viper.GetInt(blockFlag),
|
||||
viper.GetInt(recordSizeFlag),
|
||||
|
||||
@@ -57,7 +57,7 @@ var restoreCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
headersToRestore := []*models.Header{}
|
||||
src := strings.TrimSuffix(viper.GetString(srcFlag), "/")
|
||||
src := strings.TrimSuffix(viper.GetString(fromFlag), "/")
|
||||
dbhdr, err := metadataPersister.GetHeader(context.Background(), src)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
@@ -100,20 +100,20 @@ var restoreCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
dst := dbhdr.Name
|
||||
if viper.GetString(dstFlag) != "" {
|
||||
if viper.GetString(toFlag) != "" {
|
||||
if viper.GetBool(flattenFlag) {
|
||||
dst = viper.GetString(dstFlag)
|
||||
dst = viper.GetString(toFlag)
|
||||
} else {
|
||||
dst = filepath.Join(viper.GetString(dstFlag), strings.TrimPrefix(dst, viper.GetString(srcFlag)))
|
||||
dst = filepath.Join(viper.GetString(toFlag), strings.TrimPrefix(dst, viper.GetString(fromFlag)))
|
||||
|
||||
if strings.TrimSuffix(dst, "/") == strings.TrimSuffix(viper.GetString(dstFlag), "/") {
|
||||
if strings.TrimSuffix(dst, "/") == strings.TrimSuffix(viper.GetString(toFlag), "/") {
|
||||
dst = filepath.Join(dst, path.Base(dbhdr.Name)) // Append the name so we don't overwrite
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := restoreFromRecordAndBlock(
|
||||
viper.GetString(tapeFlag),
|
||||
viper.GetString(driveFlag),
|
||||
viper.GetInt(recordSizeFlag),
|
||||
int(dbhdr.Record),
|
||||
int(dbhdr.Block),
|
||||
@@ -134,9 +134,9 @@ var restoreCmd = &cobra.Command{
|
||||
|
||||
func init() {
|
||||
restoreCmd.PersistentFlags().IntP(recordSizeFlag, "z", 20, "Amount of 512-bit blocks per record")
|
||||
restoreCmd.PersistentFlags().StringP(srcFlag, "s", "", "File or directory to restore")
|
||||
restoreCmd.PersistentFlags().StringP(dstFlag, "d", "", "File or directory restore to (archived name by default)")
|
||||
restoreCmd.PersistentFlags().BoolP(flattenFlag, "f", false, "Ignore the folder hierarchy on the tape or tar file")
|
||||
restoreCmd.PersistentFlags().StringP(fromFlag, "f", "", "File or directory to restore")
|
||||
restoreCmd.PersistentFlags().StringP(toFlag, "t", "", "File or directory restore to (archived name by default)")
|
||||
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")
|
||||
|
||||
|
||||
@@ -12,12 +12,13 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
tapeFlag = "tape"
|
||||
metadataFlag = "metadata"
|
||||
verboseFlag = "verbose"
|
||||
driveFlag = "drive"
|
||||
metadataFlag = "metadata"
|
||||
verboseFlag = "verbose"
|
||||
|
||||
compressionFlag = "compression"
|
||||
|
||||
compressionFormatNoneKey = "none"
|
||||
noneKey = "none"
|
||||
|
||||
compressionFormatGZipKey = "gzip"
|
||||
compressionFormatGZipSuffix = ".gz"
|
||||
@@ -40,26 +41,33 @@ const (
|
||||
|
||||
encryptionFlag = "encryption"
|
||||
|
||||
encryptionFormatNoneKey = "none"
|
||||
|
||||
encryptionFormatAgeKey = "age"
|
||||
encryptionFormatAgeSuffix = ".age"
|
||||
|
||||
encryptionFormatPGPKey = "pgp"
|
||||
encryptionFormatPGPSuffix = ".pgp"
|
||||
|
||||
signatureFlag = "signature"
|
||||
|
||||
signatureFormatMinisignKey = "minisign"
|
||||
)
|
||||
|
||||
var (
|
||||
knownCompressionFormats = []string{compressionFormatNoneKey, compressionFormatGZipKey, compressionFormatParallelGZipKey, compressionFormatLZ4Key, compressionFormatZStandardKey, compressionFormatBrotliKey, compressionFormatBzip2Key, compressionFormatBzip2ParallelKey}
|
||||
knownCompressionFormats = []string{noneKey, compressionFormatGZipKey, compressionFormatParallelGZipKey, compressionFormatLZ4Key, compressionFormatZStandardKey, compressionFormatBrotliKey, compressionFormatBzip2Key, compressionFormatBzip2ParallelKey}
|
||||
|
||||
errUnknownCompressionFormat = errors.New("unknown compression format")
|
||||
errUnsupportedCompressionFormat = errors.New("unsupported compression format")
|
||||
|
||||
knownEncryptionFormats = []string{encryptionFormatNoneKey, encryptionFormatAgeKey, encryptionFormatPGPKey}
|
||||
knownEncryptionFormats = []string{noneKey, encryptionFormatAgeKey, encryptionFormatPGPKey}
|
||||
|
||||
errUnknownEncryptionFormat = errors.New("unknown encryption format")
|
||||
errUnsupportedEncryptionFormat = errors.New("unsupported encryption format")
|
||||
errKeygenForEncryptionFormatUnsupported = errors.New("can not generate keys for this encryption format")
|
||||
|
||||
knownSignatureFormats = []string{noneKey, signatureFormatMinisignKey}
|
||||
|
||||
errUnknownSignatureFormat = errors.New("unknown signature format")
|
||||
errUnsupportedSignatureFormat = errors.New("unsupported signature format")
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
@@ -99,6 +107,19 @@ https://github.com/pojntfx/stfs`,
|
||||
return errUnknownEncryptionFormat
|
||||
}
|
||||
|
||||
signatureFormatIsKnown := false
|
||||
signatureFormat := viper.GetString(signatureFlag)
|
||||
|
||||
for _, candidate := range knownSignatureFormats {
|
||||
if signatureFormat == candidate {
|
||||
signatureFormatIsKnown = true
|
||||
}
|
||||
}
|
||||
|
||||
if !signatureFormatIsKnown {
|
||||
return errUnknownSignatureFormat
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -111,11 +132,12 @@ func Execute() {
|
||||
}
|
||||
metadataPath := filepath.Join(home, ".local", "share", "stbak", "var", "lib", "stbak", "metadata.sqlite")
|
||||
|
||||
rootCmd.PersistentFlags().StringP(tapeFlag, "t", "/dev/nst0", "Tape or tar file to use")
|
||||
rootCmd.PersistentFlags().StringP(driveFlag, "d", "/dev/nst0", "Tape or tar file to use")
|
||||
rootCmd.PersistentFlags().StringP(metadataFlag, "m", metadataPath, "Metadata database to use")
|
||||
rootCmd.PersistentFlags().BoolP(verboseFlag, "v", false, "Enable verbose logging")
|
||||
rootCmd.PersistentFlags().StringP(compressionFlag, "c", compressionFormatNoneKey, fmt.Sprintf("Compression format to use (default %v, available are %v)", compressionFormatNoneKey, knownCompressionFormats))
|
||||
rootCmd.PersistentFlags().StringP(encryptionFlag, "e", encryptionFormatNoneKey, fmt.Sprintf("Encryption format to use (default %v, available are %v)", encryptionFormatNoneKey, knownEncryptionFormats))
|
||||
rootCmd.PersistentFlags().StringP(compressionFlag, "c", noneKey, fmt.Sprintf("Compression format to use (default %v, available are %v)", noneKey, knownCompressionFormats))
|
||||
rootCmd.PersistentFlags().StringP(encryptionFlag, "e", noneKey, fmt.Sprintf("Encryption format to use (default %v, available are %v)", noneKey, knownEncryptionFormats))
|
||||
rootCmd.PersistentFlags().StringP(signatureFlag, "s", noneKey, fmt.Sprintf("Signature format to use (default %v, available are %v)", noneKey, knownSignatureFormats))
|
||||
|
||||
if err := viper.BindPFlags(rootCmd.PersistentFlags()); err != nil {
|
||||
panic(err)
|
||||
|
||||
@@ -66,9 +66,9 @@ var updateCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
hdrs, err := update(
|
||||
viper.GetString(tapeFlag),
|
||||
viper.GetString(driveFlag),
|
||||
viper.GetInt(recordSizeFlag),
|
||||
viper.GetString(srcFlag),
|
||||
viper.GetString(fromFlag),
|
||||
viper.GetBool(overwriteFlag),
|
||||
viper.GetString(compressionFlag),
|
||||
viper.GetString(compressionLevelFlag),
|
||||
@@ -80,7 +80,7 @@ var updateCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
return index(
|
||||
viper.GetString(tapeFlag),
|
||||
viper.GetString(driveFlag),
|
||||
viper.GetString(metadataFlag),
|
||||
viper.GetInt(recordSizeFlag),
|
||||
int(lastIndexedRecord),
|
||||
@@ -321,7 +321,7 @@ func update(
|
||||
|
||||
func init() {
|
||||
updateCmd.PersistentFlags().IntP(recordSizeFlag, "z", 20, "Amount of 512-bit blocks per record")
|
||||
updateCmd.PersistentFlags().StringP(srcFlag, "s", "", "Path of the file or directory to update")
|
||||
updateCmd.PersistentFlags().StringP(fromFlag, "f", "", "Path of the file or directory to update")
|
||||
updateCmd.PersistentFlags().BoolP(overwriteFlag, "o", false, "Replace the content on the tape or tar file")
|
||||
updateCmd.PersistentFlags().StringP(compressionLevelFlag, "l", compressionLevelBalanced, fmt.Sprintf("Compression level to use (default %v, available are %v)", compressionLevelBalanced, knownCompressionLevels))
|
||||
updateCmd.PersistentFlags().StringP(recipientFlag, "r", "", "Path to public key of recipient to encrypt for")
|
||||
|
||||
1
go.mod
1
go.mod
@@ -3,6 +3,7 @@ module github.com/pojntfx/stfs
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
aead.dev/minisign v0.2.0
|
||||
filippo.io/age v1.0.0
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.3.0
|
||||
|
||||
5
go.sum
5
go.sum
@@ -1,3 +1,5 @@
|
||||
aead.dev/minisign v0.2.0 h1:kAWrq/hBRu4AARY6AlciO83xhNnW9UaC8YipS2uhLPk=
|
||||
aead.dev/minisign v0.2.0/go.mod h1:zdq6LdSd9TbuSxchxwhpA9zEb9YXcVGoE8JakuiGaIQ=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
@@ -468,6 +470,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
@@ -623,6 +626,7 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -640,6 +644,7 @@ golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1 h1:kwrAHlwJ0DUBZwQ238v+Uod/3eZ8B2K5rYsUHBQvzmI=
|
||||
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
|
||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
|
||||
@@ -21,6 +21,8 @@ const (
|
||||
|
||||
STFSRecordUncompressedSize = STFSPrefix + "UncompressedSize"
|
||||
|
||||
STFSRecordSignature = STFSPrefix + "Signature"
|
||||
|
||||
STFSEmbeddedHeader = STFSPrefix + "EmbeddedHeader"
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user