refactor: Decompose fetch func

This commit is contained in:
Felix Pojtinger
2021-12-06 22:15:28 +01:00
parent fdafd1a57a
commit 7ac5346ec0
15 changed files with 746 additions and 645 deletions
+59
View File
@@ -0,0 +1,59 @@
package compression
import (
"compress/gzip"
"context"
"io"
"github.com/andybalholm/brotli"
"github.com/cosnicolaou/pbzip2"
"github.com/dsnet/compress/bzip2"
"github.com/klauspost/compress/zstd"
"github.com/klauspost/pgzip"
"github.com/pierrec/lz4/v4"
"github.com/pojntfx/stfs/pkg/config"
)
func Decompress(
src io.Reader,
compressionFormat string,
) (io.ReadCloser, error) {
switch compressionFormat {
case config.CompressionFormatGZipKey:
fallthrough
case config.CompressionFormatParallelGZipKey:
if compressionFormat == config.CompressionFormatGZipKey {
return gzip.NewReader(src)
}
return pgzip.NewReader(src)
case config.CompressionFormatLZ4Key:
lz := lz4.NewReader(src)
if err := lz.Apply(lz4.ConcurrencyOption(-1)); err != nil {
return nil, err
}
return io.NopCloser(lz), nil
case config.CompressionFormatZStandardKey:
zz, err := zstd.NewReader(src)
if err != nil {
return nil, err
}
return io.NopCloser(zz), nil
case config.CompressionFormatBrotliKey:
br := brotli.NewReader(src)
return io.NopCloser(br), nil
case config.CompressionFormatBzip2Key:
return bzip2.NewReader(src, nil)
case config.CompressionFormatBzip2ParallelKey:
bz := pbzip2.NewReader(context.Background(), src)
return io.NopCloser(bz), nil
case config.NoneKey:
return io.NopCloser(src), nil
default:
return nil, config.ErrUnsupportedCompressionFormat
}
}
+141
View File
@@ -0,0 +1,141 @@
package encryption
import (
"archive/tar"
"bytes"
"encoding/base64"
"encoding/json"
"io"
"filippo.io/age"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/pojntfx/stfs/internal/pax"
"github.com/pojntfx/stfs/pkg/config"
)
func Decrypt(
src io.Reader,
encryptionFormat string,
identity interface{},
) (io.ReadCloser, error) {
switch encryptionFormat {
case config.EncryptionFormatAgeKey:
identity, ok := identity.(*age.X25519Identity)
if !ok {
return nil, config.ErrIdentityUnparsable
}
r, err := age.Decrypt(src, identity)
if err != nil {
return nil, err
}
return io.NopCloser(r), nil
case config.EncryptionFormatPGPKey:
identity, ok := identity.(openpgp.EntityList)
if !ok {
return nil, config.ErrIdentityUnparsable
}
r, err := openpgp.ReadMessage(src, identity, nil, nil)
if err != nil {
return nil, err
}
return io.NopCloser(r.UnverifiedBody), nil
case config.NoneKey:
return io.NopCloser(src), nil
default:
return nil, config.ErrUnsupportedEncryptionFormat
}
}
func DecryptHeader(
hdr *tar.Header,
encryptionFormat string,
identity interface{},
) error {
if encryptionFormat == config.NoneKey {
return nil
}
if hdr.PAXRecords == nil {
return config.ErrEmbeddedHeaderMissing
}
encryptedEmbeddedHeader, ok := hdr.PAXRecords[pax.STFSRecordEmbeddedHeader]
if !ok {
return config.ErrEmbeddedHeaderMissing
}
embeddedHeader, err := DecryptString(encryptedEmbeddedHeader, encryptionFormat, identity)
if err != nil {
return err
}
var newHdr tar.Header
if err := json.Unmarshal([]byte(embeddedHeader), &newHdr); err != nil {
return err
}
*hdr = newHdr
return nil
}
func DecryptString(
src string,
encryptionFormat string,
identity interface{},
) (string, error) {
switch encryptionFormat {
case config.EncryptionFormatAgeKey:
identity, ok := identity.(*age.X25519Identity)
if !ok {
return "", config.ErrIdentityUnparsable
}
decoded, err := base64.StdEncoding.DecodeString(src)
if err != nil {
return "", err
}
r, err := age.Decrypt(bytes.NewBufferString(string(decoded)), identity)
if err != nil {
return "", err
}
out := &bytes.Buffer{}
if _, err := io.Copy(out, r); err != nil {
return "", err
}
return out.String(), nil
case config.EncryptionFormatPGPKey:
identity, ok := identity.(openpgp.EntityList)
if !ok {
return "", config.ErrIdentityUnparsable
}
decoded, err := base64.StdEncoding.DecodeString(src)
if err != nil {
return "", err
}
r, err := openpgp.ReadMessage(bytes.NewBufferString(string(decoded)), identity, nil, nil)
if err != nil {
return "", err
}
out := &bytes.Buffer{}
if _, err := io.Copy(out, r.UnverifiedBody); err != nil {
return "", err
}
return out.String(), nil
case config.NoneKey:
return src, nil
default:
return "", config.ErrUnsupportedEncryptionFormat
}
}
+87
View File
@@ -0,0 +1,87 @@
package keys
import (
"bytes"
"io"
"aead.dev/minisign"
"filippo.io/age"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/pojntfx/stfs/pkg/config"
)
func ParseIdentity(
encryptionFormat string,
privkey []byte,
password string,
) (interface{}, error) {
switch encryptionFormat {
case config.EncryptionFormatAgeKey:
if password != "" {
passwordIdentity, err := age.NewScryptIdentity(password)
if err != nil {
return nil, err
}
r, err := age.Decrypt(bytes.NewBuffer(privkey), passwordIdentity)
if err != nil {
return nil, err
}
out := &bytes.Buffer{}
if _, err := io.Copy(out, r); err != nil {
return nil, err
}
privkey = out.Bytes()
}
return age.ParseX25519Identity(string(privkey))
case config.EncryptionFormatPGPKey:
identities, err := openpgp.ReadKeyRing(bytes.NewBuffer(privkey))
if err != nil {
return nil, err
}
if password != "" {
for _, identity := range identities {
if identity.PrivateKey == nil {
return nil, config.ErrIdentityUnparsable
}
if err := identity.PrivateKey.Decrypt([]byte(password)); err != nil {
return nil, err
}
for _, subkey := range identity.Subkeys {
if err := subkey.PrivateKey.Decrypt([]byte(password)); err != nil {
return nil, err
}
}
}
}
return identities, nil
case config.NoneKey:
return privkey, nil
default:
return nil, config.ErrUnsupportedEncryptionFormat
}
}
func ParseSignerIdentity(
signatureFormat string,
privkey []byte,
password string,
) (interface{}, error) {
switch signatureFormat {
case config.SignatureFormatMinisignKey:
return minisign.DecryptKey(password, privkey)
case config.SignatureFormatPGPKey:
return ParseIdentity(signatureFormat, privkey, password)
case config.NoneKey:
return privkey, nil
default:
return nil, config.ErrUnsupportedSignatureFormat
}
}
+47
View File
@@ -0,0 +1,47 @@
package keys
import (
"bytes"
"aead.dev/minisign"
"filippo.io/age"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/pojntfx/stfs/pkg/config"
)
func ParseRecipient(
encryptionFormat string,
pubkey []byte,
) (interface{}, error) {
switch encryptionFormat {
case config.EncryptionFormatAgeKey:
return age.ParseX25519Recipient(string(pubkey))
case config.EncryptionFormatPGPKey:
return openpgp.ReadKeyRing(bytes.NewBuffer(pubkey))
case config.NoneKey:
return pubkey, nil
default:
return nil, config.ErrUnsupportedEncryptionFormat
}
}
func ParseSignerRecipient(
signatureFormat string,
pubkey []byte,
) (interface{}, error) {
switch signatureFormat {
case config.SignatureFormatMinisignKey:
var recipient minisign.PublicKey
if err := recipient.UnmarshalText(pubkey); err != nil {
return nil, err
}
return recipient, nil
case config.SignatureFormatPGPKey:
return ParseRecipient(signatureFormat, pubkey)
case config.NoneKey:
return pubkey, nil
default:
return nil, config.ErrUnsupportedSignatureFormat
}
}
+195
View File
@@ -0,0 +1,195 @@
package signature
import (
"archive/tar"
"bytes"
"encoding/base64"
"encoding/json"
"io"
"aead.dev/minisign"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/packet"
"github.com/pojntfx/stfs/internal/pax"
"github.com/pojntfx/stfs/pkg/config"
)
func Verify(
src io.Reader,
isRegular bool,
signatureFormat string,
recipient interface{},
signature string,
) (io.Reader, func() error, error) {
switch signatureFormat {
case config.SignatureFormatMinisignKey:
if !isRegular {
return nil, nil, config.ErrSignatureFormatOnlyRegularSupport
}
recipient, ok := recipient.(minisign.PublicKey)
if !ok {
return nil, nil, config.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 config.ErrSignatureInvalid
}, nil
case config.SignatureFormatPGPKey:
recipients, ok := recipient.(openpgp.EntityList)
if !ok {
return nil, nil, config.ErrIdentityUnparsable
}
if len(recipients) < 1 {
return nil, nil, config.ErrIdentityUnparsable
}
decodedSignature, err := base64.StdEncoding.DecodeString(signature)
if err != nil {
return nil, nil, err
}
reader := packet.NewReader(bytes.NewBuffer(decodedSignature))
pkt, err := reader.Next()
if err != nil {
return nil, nil, err
}
sig, ok := pkt.(*packet.Signature)
if !ok {
return nil, nil, config.ErrSignatureInvalid
}
hash := sig.Hash.New()
tee := io.TeeReader(src, hash)
return tee, func() error {
return recipients[0].PrimaryKey.VerifySignature(hash, sig)
}, nil
case config.NoneKey:
return io.NopCloser(src), func() error {
return nil
}, nil
default:
return nil, nil, config.ErrUnsupportedSignatureFormat
}
}
func VerifyHeader(
hdr *tar.Header,
isRegular bool,
signatureFormat string,
recipient interface{},
) error {
if signatureFormat == config.NoneKey {
return nil
}
if hdr.PAXRecords == nil {
return config.ErrEmbeddedHeaderMissing
}
embeddedHeader, ok := hdr.PAXRecords[pax.STFSRecordEmbeddedHeader]
if !ok {
return config.ErrEmbeddedHeaderMissing
}
signature, ok := hdr.PAXRecords[pax.STFSRecordSignature]
if !ok {
return config.ErrSignatureMissing
}
if err := VerifyString(embeddedHeader, isRegular, signatureFormat, recipient, signature); err != nil {
return err
}
var newHdr tar.Header
if err := json.Unmarshal([]byte(embeddedHeader), &newHdr); err != nil {
return err
}
*hdr = newHdr
return nil
}
func VerifyString(
src string,
isRegular bool,
signatureFormat string,
recipient interface{},
signature string,
) error {
switch signatureFormat {
case config.SignatureFormatMinisignKey:
if !isRegular {
return config.ErrSignatureFormatOnlyRegularSupport
}
recipient, ok := recipient.(minisign.PublicKey)
if !ok {
return config.ErrRecipientUnparsable
}
decodedSignature, err := base64.StdEncoding.DecodeString(signature)
if err != nil {
return err
}
if minisign.Verify(recipient, []byte(src), decodedSignature) {
return nil
}
return config.ErrSignatureInvalid
case config.SignatureFormatPGPKey:
recipients, ok := recipient.(openpgp.EntityList)
if !ok {
return nil
}
if len(recipients) < 1 {
return nil
}
decodedSignature, err := base64.StdEncoding.DecodeString(signature)
if err != nil {
return nil
}
reader := packet.NewReader(bytes.NewBuffer(decodedSignature))
pkt, err := reader.Next()
if err != nil {
return nil
}
sig, ok := pkt.(*packet.Signature)
if !ok {
return nil
}
hash := sig.Hash.New()
if _, err := io.Copy(hash, bytes.NewBufferString(src)); err != nil {
return err
}
return recipients[0].PrimaryKey.VerifySignature(hash, sig)
case config.NoneKey:
return nil
default:
return config.ErrUnsupportedSignatureFormat
}
}