pkcs7: provide session interface #276

This commit is contained in:
Sun Yimin 2024-11-21 17:35:23 +08:00 committed by GitHub
parent 8c6297d00f
commit bc0e11e9b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 140 additions and 71 deletions

View File

@ -3,13 +3,11 @@ package pkcs7
import ( import (
"bytes" "bytes"
"crypto" "crypto"
"crypto/rand"
"encoding/asn1" "encoding/asn1"
"errors" "errors"
"math/big" "math/big"
"github.com/emmansun/gmsm/pkcs" "github.com/emmansun/gmsm/pkcs"
"github.com/emmansun/gmsm/sm2"
"github.com/emmansun/gmsm/smx509" "github.com/emmansun/gmsm/smx509"
) )
@ -93,26 +91,11 @@ func (p7 *PKCS7) decrypt(cert *smx509.Certificate, pkey crypto.PrivateKey, isCFC
if recipient == nil { if recipient == nil {
return nil, errors.New("pkcs7: no enveloped recipient for provided certificate") return nil, errors.New("pkcs7: no enveloped recipient for provided certificate")
} }
contentKey, err := p7.session.DecryptDataKey(recipient.EncryptedKey, pkey, cert, isCFCA)
switch pkey := pkey.(type) { if err != nil {
case crypto.Decrypter: return nil, err
// Generic case to handle anything that provides the crypto.Decrypter interface.
encryptedKey := recipient.EncryptedKey
var decrypterOpts crypto.DecrypterOpts
if _, ok := pkey.(*sm2.PrivateKey); ok && isCFCA {
encryptedKey = make([]byte, len(recipient.EncryptedKey)+1)
encryptedKey[0] = 0x04
copy(encryptedKey[1:], recipient.EncryptedKey)
decrypterOpts = sm2.NewPlainDecrypterOpts(sm2.C1C2C3)
}
contentKey, err := pkey.Decrypt(rand.Reader, encryptedKey, decrypterOpts)
if err != nil {
return nil, err
}
return decryptableData.GetEncryptedContentInfo().decrypt(contentKey)
} }
return nil, ErrUnsupportedAlgorithm return decryptableData.GetEncryptedContentInfo().decrypt(contentKey)
} }
// DecryptUsingPSK decrypts encrypted data using caller provided // DecryptUsingPSK decrypts encrypted data using caller provided

View File

@ -2,16 +2,13 @@ package pkcs7
import ( import (
"bytes" "bytes"
"crypto/ecdsa"
"crypto/rand" "crypto/rand"
"crypto/rsa"
"crypto/sha1" "crypto/sha1"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/asn1" "encoding/asn1"
"errors" "errors"
"github.com/emmansun/gmsm/pkcs" "github.com/emmansun/gmsm/pkcs"
"github.com/emmansun/gmsm/sm2"
"github.com/emmansun/gmsm/smx509" "github.com/emmansun/gmsm/smx509"
"golang.org/x/crypto/cryptobyte" "golang.org/x/crypto/cryptobyte"
cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1" cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
@ -22,6 +19,7 @@ type EnvelopedData struct {
key []byte key []byte
contentType asn1.ObjectIdentifier contentType asn1.ObjectIdentifier
encryptedContentType asn1.ObjectIdentifier encryptedContentType asn1.ObjectIdentifier
session Session
} }
type envelopedData struct { type envelopedData struct {
@ -106,7 +104,7 @@ func Encrypt(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificat
} }
for _, recipient := range recipients { for _, recipient := range recipients {
if err := ed.AddRecipient(recipient, 0, func(cert *smx509.Certificate, key []byte) ([]byte, error) { if err := ed.AddRecipient(recipient, 0, func(cert *smx509.Certificate, key []byte) ([]byte, error) {
return encryptKey(key, cert, false) return ed.session.EncryptdDataKey(key, cert, nil)
}); err != nil { }); err != nil {
return nil, err return nil, err
} }
@ -148,7 +146,7 @@ func encryptSM(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certific
} }
for _, recipient := range recipients { for _, recipient := range recipients {
if err := ed.AddRecipient(recipient, version, func(cert *smx509.Certificate, key []byte) ([]byte, error) { if err := ed.AddRecipient(recipient, version, func(cert *smx509.Certificate, key []byte) ([]byte, error) {
return encryptKey(key, cert, isLegacyCFCA) return ed.session.EncryptdDataKey(key, cert, isLegacyCFCA)
}); err != nil { }); err != nil {
return nil, err return nil, err
} }
@ -158,22 +156,35 @@ func encryptSM(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certific
// NewEnvelopedData creates a new EnvelopedData structure with the provided cipher and content. // NewEnvelopedData creates a new EnvelopedData structure with the provided cipher and content.
func NewEnvelopedData(cipher pkcs.Cipher, content []byte) (*EnvelopedData, error) { func NewEnvelopedData(cipher pkcs.Cipher, content []byte) (*EnvelopedData, error) {
return newEnvelopedData(cipher, content, OIDEnvelopedData) return newEnvelopedData(cipher, content, OIDEnvelopedData, nil)
} }
// NewSM2EnvelopedData creates a new EnvelopedData structure with the provided cipher and content. // NewSM2EnvelopedData creates a new EnvelopedData structure with the provided cipher and content.
// The OIDs use GM/T 0010 - 2012 set. // The OIDs use GM/T 0010 - 2012 set.
func NewSM2EnvelopedData(cipher pkcs.Cipher, content []byte) (*EnvelopedData, error) { func NewSM2EnvelopedData(cipher pkcs.Cipher, content []byte) (*EnvelopedData, error) {
return newEnvelopedData(cipher, content, SM2OIDEnvelopedData) return newEnvelopedData(cipher, content, SM2OIDEnvelopedData, nil)
} }
func newEnvelopedData(cipher pkcs.Cipher, content []byte, contentType asn1.ObjectIdentifier) (*EnvelopedData, error) { // NewEnvelopedDataWithSession creates a new EnvelopedData structure with the provided cipher, content and sessionKey.
var key []byte func NewEnvelopedDataWithSession(cipher pkcs.Cipher, content []byte, session Session) (*EnvelopedData, error) {
var err error return newEnvelopedData(cipher, content, OIDEnvelopedData, session)
}
// Create key // NewSM2EnvelopedDataWithSession creates a new EnvelopedData structure with the provided cipher, content and sessionKey.
key = make([]byte, cipher.KeySize()) // The OIDs use GM/T 0010 - 2012 set.
if _, err = rand.Read(key); err != nil { func NewSM2EnvelopedDataWithSession(cipher pkcs.Cipher, content []byte, session Session) (*EnvelopedData, error) {
return newEnvelopedData(cipher, content, SM2OIDEnvelopedData, session)
}
func newEnvelopedData(cipher pkcs.Cipher, content []byte, contentType asn1.ObjectIdentifier, session Session) (*EnvelopedData, error) {
ed := &EnvelopedData{}
ed.session = session
if ed.session == nil {
ed.session = DefaultSession{}
}
key, err := ed.session.GenerateDataKey(cipher.KeySize())
if err != nil {
return nil, err return nil, err
} }
@ -181,7 +192,7 @@ func newEnvelopedData(cipher pkcs.Cipher, content []byte, contentType asn1.Objec
if err != nil { if err != nil {
return nil, err return nil, err
} }
ed := &EnvelopedData{}
ed.contentType = contentType ed.contentType = contentType
ed.encryptedContentType = OIDData ed.encryptedContentType = OIDData
version := 0 version := 0
@ -274,21 +285,3 @@ func newEncryptedContent(contentType asn1.ObjectIdentifier, alg *pkix.AlgorithmI
func marshalEncryptedContent(content []byte) asn1.RawValue { func marshalEncryptedContent(content []byte) asn1.RawValue {
return asn1.RawValue{Tag: 0, Class: asn1.ClassContextSpecific, Bytes: content} return asn1.RawValue{Tag: 0, Class: asn1.ClassContextSpecific, Bytes: content}
} }
func encryptKey(key []byte, recipient *smx509.Certificate, isCFCA bool) ([]byte, error) {
if pub, ok := recipient.PublicKey.(*rsa.PublicKey); ok {
return rsa.EncryptPKCS1v15(rand.Reader, pub, key)
}
if pub, ok := recipient.PublicKey.(*ecdsa.PublicKey); ok && pub.Curve == sm2.P256() {
if isCFCA {
encryptedKey, err := sm2.Encrypt(rand.Reader, pub, key, sm2.NewPlainEncrypterOpts(sm2.MarshalUncompressed, sm2.C1C2C3))
if err != nil {
return nil, err
}
return encryptedKey[1:], nil
} else {
return sm2.EncryptASN1(rand.Reader, pub, key)
}
}
return nil, errors.New("pkcs7: only supports RSA/SM2 key")
}

View File

@ -27,6 +27,7 @@ type PKCS7 struct {
CRLs []pkix.CertificateList CRLs []pkix.CertificateList
Signers []signerInfo Signers []signerInfo
raw any raw any
session Session
} }
type contentInfo struct { type contentInfo struct {
@ -195,8 +196,13 @@ func getOIDForEncryptionAlgorithm(pkey any, OIDDigestAlg asn1.ObjectIdentifier)
} }
// Parse decodes a DER encoded PKCS7 package // Parse decodes a DER encoded PKCS7 package and assign the default session to the PKCS7 object
func Parse(data []byte) (p7 *PKCS7, err error) { func Parse(data []byte) (p7 *PKCS7, err error) {
return ParseWithSession(DefaultSession{}, data)
}
// ParseWithSession decodes a DER encoded PKCS7 package and assign the session to the PKCS7 object
func ParseWithSession(session Session, data []byte) (p7 *PKCS7, err error) {
if len(data) == 0 { if len(data) == 0 {
return nil, errors.New("pkcs7: input data is empty") return nil, errors.New("pkcs7: input data is empty")
} }
@ -218,33 +224,35 @@ func Parse(data []byte) (p7 *PKCS7, err error) {
case info.ContentType.Equal(OIDSignedData) || info.ContentType.Equal(SM2OIDSignedData): case info.ContentType.Equal(OIDSignedData) || info.ContentType.Equal(SM2OIDSignedData):
return parseSignedData(info.Content.Bytes) return parseSignedData(info.Content.Bytes)
case info.ContentType.Equal(OIDEnvelopedData) || info.ContentType.Equal(SM2OIDEnvelopedData): case info.ContentType.Equal(OIDEnvelopedData) || info.ContentType.Equal(SM2OIDEnvelopedData):
return parseEnvelopedData(info.Content.Bytes) return parseEnvelopedData(session, info.Content.Bytes)
case info.ContentType.Equal(OIDEncryptedData) || info.ContentType.Equal(SM2OIDEncryptedData): case info.ContentType.Equal(OIDEncryptedData) || info.ContentType.Equal(SM2OIDEncryptedData):
return parseEncryptedData(info.Content.Bytes) return parseEncryptedData(session, info.Content.Bytes)
case info.ContentType.Equal(OIDSignedEnvelopedData) || info.ContentType.Equal(SM2OIDSignedEnvelopedData): case info.ContentType.Equal(OIDSignedEnvelopedData) || info.ContentType.Equal(SM2OIDSignedEnvelopedData):
return parseSignedEnvelopedData(info.Content.Bytes) return parseSignedEnvelopedData(session, info.Content.Bytes)
default: default:
return nil, ErrUnsupportedContentType return nil, ErrUnsupportedContentType
} }
} }
func parseEnvelopedData(data []byte) (*PKCS7, error) { func parseEnvelopedData(session Session, data []byte) (*PKCS7, error) {
var ed envelopedData var ed envelopedData
if _, err := asn1.Unmarshal(data, &ed); err != nil { if _, err := asn1.Unmarshal(data, &ed); err != nil {
return nil, err return nil, err
} }
return &PKCS7{ return &PKCS7{
raw: ed, raw: ed,
session: session,
}, nil }, nil
} }
func parseEncryptedData(data []byte) (*PKCS7, error) { func parseEncryptedData(session Session, data []byte) (*PKCS7, error) {
var ed encryptedData var ed encryptedData
if _, err := asn1.Unmarshal(data, &ed); err != nil { if _, err := asn1.Unmarshal(data, &ed); err != nil {
return nil, err return nil, err
} }
return &PKCS7{ return &PKCS7{
raw: ed, raw: ed,
session: session,
}, nil }, nil
} }

78
pkcs7/session.go Normal file
View File

@ -0,0 +1,78 @@
package pkcs7
import (
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"errors"
"github.com/emmansun/gmsm/sm2"
"github.com/emmansun/gmsm/smx509"
)
type Session interface {
// GenerateDataKey returns the data key to be used for encryption
GenerateDataKey(size int) ([]byte, error)
// EncryptdDataKey encrypts the key with the provided certificate public key
EncryptdDataKey(key []byte, cert *smx509.Certificate, opts any) ([]byte, error)
// DecryptDataKey decrypts the key with the provided certificate private key
DecryptDataKey(key []byte, priv crypto.PrivateKey, cert *smx509.Certificate, opts any) ([]byte, error)
}
// DefaultSession is the default implementation of Session without any special handling
// Custom implementations can be provided to handle key reuse, cache, etc.
type DefaultSession struct{}
func (d DefaultSession) GenerateDataKey(size int) ([]byte, error) {
key := make([]byte, size)
if _, err := rand.Read(key); err != nil {
return nil, err
}
return key, nil
}
func (d DefaultSession) EncryptdDataKey(key []byte, cert *smx509.Certificate, opts any) ([]byte, error) {
switch pub := cert.PublicKey.(type) {
case *rsa.PublicKey:
return rsa.EncryptPKCS1v15(rand.Reader, pub, key)
case *ecdsa.PublicKey:
if pub.Curve == sm2.P256() {
if isLegacyCFCA, ok := opts.(bool); ok && isLegacyCFCA {
encryptedKey, err := sm2.Encrypt(rand.Reader, pub, key, sm2.NewPlainEncrypterOpts(sm2.MarshalUncompressed, sm2.C1C2C3))
if err != nil {
return nil, err
}
return encryptedKey[1:], nil
} else {
return sm2.EncryptASN1(rand.Reader, pub, key)
}
}
}
return nil, errors.New("pkcs7: only supports RSA/SM2 key")
}
func (d DefaultSession) DecryptDataKey(key []byte, priv crypto.PrivateKey, cert *smx509.Certificate, opts any) ([]byte, error) {
switch pkey := priv.(type) {
case crypto.Decrypter:
// Generic case to handle anything that provides the crypto.Decrypter interface.
encryptedKey := key
var decrypterOpts crypto.DecrypterOpts
if _, ok := pkey.(*sm2.PrivateKey); ok {
if isLegacyCFCA, ok := opts.(bool); ok && isLegacyCFCA {
encryptedKey = make([]byte, len(key)+1)
encryptedKey[0] = 0x04
copy(encryptedKey[1:], key)
decrypterOpts = sm2.NewPlainDecrypterOpts(sm2.C1C2C3)
}
}
contentKey, err := pkey.Decrypt(rand.Reader, encryptedKey, decrypterOpts)
if err != nil {
return nil, err
}
return contentKey, nil
}
return nil, ErrUnsupportedAlgorithm
}

View File

@ -47,7 +47,7 @@ func (data signedEnvelopedData) GetEncryptedContentInfo() *encryptedContentInfo
return &data.EncryptedContentInfo return &data.EncryptedContentInfo
} }
func parseSignedEnvelopedData(data []byte) (*PKCS7, error) { func parseSignedEnvelopedData(session Session, data []byte) (*PKCS7, error) {
var sed signedEnvelopedData var sed signedEnvelopedData
if _, err := asn1.Unmarshal(data, &sed); err != nil { if _, err := asn1.Unmarshal(data, &sed); err != nil {
return nil, err return nil, err
@ -61,7 +61,8 @@ func parseSignedEnvelopedData(data []byte) (*PKCS7, error) {
Certificates: certs, Certificates: certs,
CRLs: sed.CRLs, CRLs: sed.CRLs,
Signers: sed.SignerInfos, Signers: sed.SignerInfos,
raw: sed}, nil raw: sed,
session: session}, nil
} }
type VerifyFunc func() error type VerifyFunc func() error
@ -140,6 +141,7 @@ type SignedAndEnvelopedData struct {
data, cek []byte data, cek []byte
contentTypeOid asn1.ObjectIdentifier contentTypeOid asn1.ObjectIdentifier
digestOid asn1.ObjectIdentifier digestOid asn1.ObjectIdentifier
session Session
} }
// NewSignedAndEnvelopedData takes data and cipher and initializes a new PKCS7 SignedAndEnvelopedData structure // NewSignedAndEnvelopedData takes data and cipher and initializes a new PKCS7 SignedAndEnvelopedData structure
@ -149,9 +151,11 @@ func NewSignedAndEnvelopedData(data []byte, cipher pkcs.Cipher) (*SignedAndEnvel
var key []byte var key []byte
var err error var err error
// Create key result := &SignedAndEnvelopedData{
key = make([]byte, cipher.KeySize()) session: DefaultSession{},
_, err = rand.Read(key) }
key, err = result.session.GenerateDataKey(cipher.KeySize())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -160,8 +164,11 @@ func NewSignedAndEnvelopedData(data []byte, cipher pkcs.Cipher) (*SignedAndEnvel
if err != nil { if err != nil {
return nil, err return nil, err
} }
result.cek = key
sed := signedEnvelopedData{ result.contentTypeOid = OIDSignedEnvelopedData
result.data = data
result.digestOid = OIDDigestAlgorithmSHA1
result.sed = signedEnvelopedData{
Version: 1, // 0 or 1? Version: 1, // 0 or 1?
EncryptedContentInfo: encryptedContentInfo{ EncryptedContentInfo: encryptedContentInfo{
ContentType: OIDData, ContentType: OIDData,
@ -169,7 +176,7 @@ func NewSignedAndEnvelopedData(data []byte, cipher pkcs.Cipher) (*SignedAndEnvel
EncryptedContent: marshalEncryptedContent(ciphertext), EncryptedContent: marshalEncryptedContent(ciphertext),
}, },
} }
return &SignedAndEnvelopedData{sed: sed, data: data, cek: key, digestOid: OIDDigestAlgorithmSHA1, contentTypeOid: OIDSignedEnvelopedData}, nil return result, nil
} }
// NewSMSignedAndEnvelopedData takes data and cipher and initializes a new PKCS7(SM) SignedAndEnvelopedData structure // NewSMSignedAndEnvelopedData takes data and cipher and initializes a new PKCS7(SM) SignedAndEnvelopedData structure
@ -271,7 +278,7 @@ func (saed *SignedAndEnvelopedData) AddCertificate(cert *smx509.Certificate) {
// AddRecipient adds a recipient to the payload // AddRecipient adds a recipient to the payload
func (saed *SignedAndEnvelopedData) AddRecipient(recipient *smx509.Certificate) error { func (saed *SignedAndEnvelopedData) AddRecipient(recipient *smx509.Certificate) error {
encryptedKey, err := encryptKey(saed.cek, recipient, false) //TODO: check if CFCA has such function encryptedKey, err := saed.session.EncryptdDataKey(saed.cek, recipient, nil)
if err != nil { if err != nil {
return err return err
} }