refactor: Split keys and keyext packages, make compression, signature and encryption packages public
This commit is contained in:
159
pkg/signature/sign.go
Normal file
159
pkg/signature/sign.go
Normal 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
195
pkg/signature/verify.go
Normal 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user