160 lines
3.7 KiB
Go
160 lines
3.7 KiB
Go
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 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.ErrSignatureFormatOnlyRegularSupport
|
|
}
|
|
|
|
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[pax.STFSRecordEmbeddedHeader] = string(wrappedHeader)
|
|
newHdr.PAXRecords[pax.STFSRecordSignature], err = SignString(newHdr.PAXRecords[pax.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.ErrSignatureFormatOnlyRegularSupport
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|