Skip to content

Commit

Permalink
pkcs7: support GetRecipients #252
Browse files Browse the repository at this point in the history
  • Loading branch information
emmansun authored Sep 30, 2024
1 parent 3968b9d commit ff59b79
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 0 deletions.
32 changes: 32 additions & 0 deletions pkcs7/decrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,55 @@ import (
"crypto/rand"
"encoding/asn1"
"errors"
"math/big"

"github.com/emmansun/gmsm/pkcs"
"github.com/emmansun/gmsm/sm2"
"github.com/emmansun/gmsm/smx509"
)

// IssuerAndSerial is a structure that holds the issuer name and serial number
type IssuerAndSerial struct {
RawIssuer []byte
SerialNumber *big.Int
}

func newIssuerAndSerial(issuerAndSerial issuerAndSerial) IssuerAndSerial {
is := IssuerAndSerial{}
if len(issuerAndSerial.IssuerName.FullBytes) > 0 {
is.RawIssuer = make([]byte, len(issuerAndSerial.IssuerName.FullBytes))
copy(is.RawIssuer, issuerAndSerial.IssuerName.FullBytes)
}
if issuerAndSerial.SerialNumber != nil {
is.SerialNumber = new(big.Int).Set(issuerAndSerial.SerialNumber)
}
return is
}

// ErrUnsupportedAlgorithm tells you when our quick dev assumptions have failed
var ErrUnsupportedAlgorithm = errors.New("pkcs7: cannot decrypt data: only RSA, SM2, DES, DES-EDE3, AES and SM4 supported")

// ErrNotEncryptedContent is returned when attempting to Decrypt data that is not encrypted data
var ErrNotEncryptedContent = errors.New("pkcs7: content data is NOT a decryptable data type")

// ErrNotEnvelopedData is returned when attempting to Decrypt data that is not enveloped data
var ErrNotEnvelopedData = errors.New("pkcs7: content data is NOT an enveloped data type")

type decryptable interface {
GetRecipient(cert *smx509.Certificate) *recipientInfo
GetRecipients() ([]IssuerAndSerial, error)
GetEncryptedContentInfo() *encryptedContentInfo
}

// GetRecipients returns the list of recipients for the enveloped data
func (p7 *PKCS7) GetRecipients() ([]IssuerAndSerial, error) {
decryptableData, ok := p7.raw.(decryptable)
if !ok {
return nil, ErrNotEnvelopedData
}
return decryptableData.GetRecipients()
}

// Decrypt decrypts encrypted content info for recipient cert and private key
func (p7 *PKCS7) Decrypt(cert *smx509.Certificate, pkey crypto.PrivateKey) ([]byte, error) {
return p7.decrypt(cert, pkey, false)
Expand Down
13 changes: 13 additions & 0 deletions pkcs7/decrypt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@ func TestDecrypt(t *testing.T) {
if err != nil {
t.Fatal(err)
}
recipents, err := p7.GetRecipients()
if err != nil {
t.Fatal(err)
}
if len(recipents) != 1 {
t.Errorf("Expected 1 recipient, got %d", len(recipents))
}
if recipents[0].SerialNumber.Cmp(fixture.Certificate.SerialNumber) != 0 {
t.Errorf("Recipient serial number does not match.\n\tExpected:%s\n\tActual:%s", fixture.Certificate.SerialNumber, recipents[0].SerialNumber)
}
if !bytes.Equal(recipents[0].RawIssuer, fixture.Certificate.RawIssuer) {
t.Errorf("Recipient issuer name does not match.\n\tExpected:%x\n\tActual:%x", fixture.Certificate.RawIssuer, recipents[0].RawIssuer)
}
content, err := p7.Decrypt(fixture.Certificate, fixture.PrivateKey)
if err != nil {
t.Errorf("Cannot Decrypt with error: %v", err)
Expand Down
4 changes: 4 additions & 0 deletions pkcs7/encrypt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ func TestEncryptUsingPSK(t *testing.T) {
}

p7, _ := Parse(ciphertext)
_, err = p7.GetRecipients()
if err != ErrNotEnvelopedData {
t.Errorf("expected ErrNotEnvelopedData, got %v", err)
}
result, err := p7.DecryptUsingPSK(key)
if err != nil {
t.Fatalf("cannot Decrypt encrypted result: %s", err)
Expand Down
9 changes: 9 additions & 0 deletions pkcs7/envelope.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ func (data envelopedData) GetRecipient(cert *smx509.Certificate) *recipientInfo
return nil
}

// GetRecipients returns the list of recipients (READONLY) for the enveloped data
func (data envelopedData) GetRecipients() ([]IssuerAndSerial, error) {
var recipients []IssuerAndSerial
for _, recp := range data.RecipientInfos {
recipients = append(recipients, newIssuerAndSerial(recp.IssuerAndSerialNumber))
}
return recipients, nil
}

func (data envelopedData) GetEncryptedContentInfo() *encryptedContentInfo {
return &data.EncryptedContentInfo
}
Expand Down
9 changes: 9 additions & 0 deletions pkcs7/sign_enveloped.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ func (data signedEnvelopedData) GetRecipient(cert *smx509.Certificate) *recipien
return nil
}

// GetRecipients returns the list of recipients (READONLY) for the enveloped data
func (data signedEnvelopedData) GetRecipients() ([]IssuerAndSerial, error) {
var recipients []IssuerAndSerial
for _, recp := range data.RecipientInfos {
recipients = append(recipients, newIssuerAndSerial(recp.IssuerAndSerialNumber))
}
return recipients, nil
}

func (data signedEnvelopedData) GetEncryptedContentInfo() *encryptedContentInfo {
return &data.EncryptedContentInfo
}
Expand Down
25 changes: 25 additions & 0 deletions pkcs7/sign_enveloped_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,14 @@ func TestParseSignedEvnvelopedData(t *testing.T) {
t.Fatal("should only one certificate")
}

recipients, err := p7Data.GetRecipients()
if err != nil {
t.Fatal(err)
}
if len(recipients) != 1 {
t.Fatal("should only one recipient")
}

block, rest = pem.Decode([]byte(signKey))
if len(rest) != 0 {
t.Fatal("unexpected remaining PEM block during decode")
Expand Down Expand Up @@ -263,6 +271,23 @@ func TestCreateSignedEvnvelopedData(t *testing.T) {
if err != nil {
t.Fatal(err)
}

recipients, err := p7Data.GetRecipients()
if err != nil {
t.Fatal(err)
}
if len(recipients) != 1 {
t.Fatal("should only one recipient")
}

if recipients[0].SerialNumber.Cmp(recipient.Certificate.SerialNumber) != 0 {
t.Errorf("Recipient serial number does not match.\n\tExpected:%s\n\tActual:%s", recipient.Certificate.SerialNumber, recipients[0].SerialNumber)
}

if !bytes.Equal(recipients[0].RawIssuer, recipient.Certificate.RawIssuer) {
t.Errorf("Recipient issuer name does not match.\n\tExpected:%x\n\tActual:%x", recipient.Certificate.RawIssuer, recipients[0].RawIssuer)
}

encKeyBytes, err := p7Data.DecryptAndVerify(recipient.Certificate, *recipient.PrivateKey, func() error {
return p7Data.Verify()
})
Expand Down

0 comments on commit ff59b79

Please sign in to comment.