refactor: Split keys and keyext packages, make compression, signature and encryption packages public

This commit is contained in:
Felicitas Pojtinger
2022-01-03 14:56:46 +01:00
parent b0a1b61297
commit 5d5b6ebb96
27 changed files with 65 additions and 55 deletions

159
pkg/signature/sign.go Normal file
View File

@@ -0,0 +1,159 @@
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/records"
"github.com/pojntfx/stfs/pkg/config"
)
func Sign(
src io.Reader,
isRegular bool,
signatureFormat string,
identity interface{},
) (io.Reader, func() (string, error), error) {
switch signatureFormat {
case config.SignatureFormatMinisignKey:
if !isRegular {
return nil, nil, config.ErrSignatureFormatRegularOnly
}
identity, ok := identity.(minisign.PrivateKey)
if !ok {
return nil, nil, config.ErrIdentityUnparsable
}
signer := minisign.NewReader(src)
return signer, func() (string, error) {
return base64.StdEncoding.EncodeToString(signer.Sign(identity)), nil
}, nil
case config.SignatureFormatPGPKey:
identities, ok := identity.(openpgp.EntityList)
if !ok {
return nil, nil, config.ErrIdentityUnparsable
}
if len(identities) < 1 {
return nil, nil, config.ErrIdentityUnparsable
}
// See openpgp.DetachSign
var c *packet.Config
signingKey, ok := identities[0].SigningKeyById(c.Now(), c.SigningKey())
if !ok || signingKey.PrivateKey == nil || signingKey.PublicKey == nil {
return nil, nil, config.ErrIdentityUnparsable
}
sig := new(packet.Signature)
sig.SigType = packet.SigTypeBinary
sig.PubKeyAlgo = signingKey.PrivateKey.PubKeyAlgo
sig.Hash = c.Hash()
sig.CreationTime = c.Now()
sigLifetimeSecs := c.SigLifetime()
sig.SigLifetimeSecs = &sigLifetimeSecs
sig.IssuerKeyId = &signingKey.PrivateKey.KeyId
hash := sig.Hash.New()
return io.TeeReader(src, hash), func() (string, error) {
if err := sig.Sign(hash, signingKey.PrivateKey, c); err != nil {
return "", err
}
out := &bytes.Buffer{}
if err := sig.Serialize(out); err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(out.Bytes()), nil
}, nil
case config.NoneKey:
return src, func() (string, error) {
return "", nil
}, nil
default:
return nil, nil, config.ErrSignatureFormatUnsupported
}
}
func SignHeader(
hdr *tar.Header,
isRegular bool,
signatureFormat string,
identity interface{},
) error {
if signatureFormat == config.NoneKey {
return nil
}
newHdr := &tar.Header{
Format: tar.FormatPAX,
Size: hdr.Size,
PAXRecords: map[string]string{},
}
wrappedHeader, err := json.Marshal(hdr)
if err != nil {
return err
}
newHdr.PAXRecords[records.STFSRecordEmbeddedHeader] = string(wrappedHeader)
newHdr.PAXRecords[records.STFSRecordSignature], err = SignString(newHdr.PAXRecords[records.STFSRecordEmbeddedHeader], isRegular, signatureFormat, identity)
if err != nil {
return err
}
*hdr = *newHdr
return nil
}
func SignString(
src string,
isRegular bool,
signatureFormat string,
identity interface{},
) (string, error) {
switch signatureFormat {
case config.SignatureFormatMinisignKey:
if !isRegular {
return "", config.ErrSignatureFormatRegularOnly
}
identity, ok := identity.(minisign.PrivateKey)
if !ok {
return "", config.ErrIdentityUnparsable
}
return base64.StdEncoding.EncodeToString(minisign.Sign(identity, []byte(src))), nil
case config.SignatureFormatPGPKey:
identities, ok := identity.(openpgp.EntityList)
if !ok {
return "", config.ErrIdentityUnparsable
}
if len(identities) < 1 {
return "", config.ErrIdentityUnparsable
}
out := &bytes.Buffer{}
if err := openpgp.DetachSign(out, identities[0], bytes.NewBufferString(src), nil); err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(out.Bytes()), nil
case config.NoneKey:
return src, nil
default:
return "", config.ErrSignatureFormatUnsupported
}
}

195
pkg/signature/verify.go Normal file
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/records"
"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.ErrSignatureFormatRegularOnly
}
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.ErrSignatureFormatUnsupported
}
}
func VerifyHeader(
hdr *tar.Header,
isRegular bool,
signatureFormat string,
recipient interface{},
) error {
if signatureFormat == config.NoneKey {
return nil
}
if hdr.PAXRecords == nil {
return config.ErrTarHeaderEmbeddedMissing
}
embeddedHeader, ok := hdr.PAXRecords[records.STFSRecordEmbeddedHeader]
if !ok {
return config.ErrTarHeaderEmbeddedMissing
}
signature, ok := hdr.PAXRecords[records.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.ErrSignatureFormatRegularOnly
}
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.ErrSignatureFormatUnsupported
}
}