Skip to content

Commit

Permalink
feat: support copying PKCS7 structs
Browse files Browse the repository at this point in the history
  • Loading branch information
secDre4mer committed May 3, 2024
1 parent a82ada2 commit bf12d97
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 0 deletions.
26 changes: 26 additions & 0 deletions sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,32 @@ type SignedData struct {
encryptionOid asn1.ObjectIdentifier
}

// Copy creates a new SignedData struct based on existing PKCS7 signed data.
func Copy(data *PKCS7, unauthenticatedAttrs ...Attribute) ([]byte, error) {
rawData, isSignedData := data.raw.(signedData)
if !isSignedData {
return nil, ErrUnsupportedContentType
}

// Make a copy of the signer infos since we change them
rawData.SignerInfos = append([]signerInfo{}, rawData.SignerInfos...)
for i := range rawData.SignerInfos {
err := rawData.SignerInfos[i].SetUnauthenticatedAttributes(unauthenticatedAttrs)
if err != nil {
return nil, err
}
}
inner, err := asn1.Marshal(rawData)
if err != nil {
return nil, err
}
outer := contentInfo{
ContentType: OIDSignedData,
Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: inner, IsCompound: true},
}
return asn1.Marshal(outer)
}

// NewSignedData takes data and initializes a PKCS7 SignedData struct that is
// ready to be signed via AddSigner. The digest algorithm is set to SHA1 by default
// and can be changed by calling SetDigestAlgorithm.
Expand Down
64 changes: 64 additions & 0 deletions sign_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,67 @@ func fromHex(s string) *big.Int {
}
return result
}

func TestCopy(t *testing.T) {
content := []byte("Hello World")
rootCert, err := createTestCertificateByIssuer("PKCS7 Test Root CA", nil, x509.SHA256WithRSA, true)
if err != nil {
t.Fatalf("cannot generate root cert: %s", err)
}
truststore := x509.NewCertPool()
truststore.AddCert(rootCert.Certificate)
signerCert, err := createTestCertificateByIssuer("PKCS7 Test Signer Cert", rootCert, x509.SHA256WithRSA, false)
if err != nil {
t.Fatalf("cannot generate signer cert: %s", err)
}
toBeSigned, err := NewSignedData(content)
if err != nil {
t.Fatalf("cannot initialize signed data: %s", err)
}

// Set the digest to match the end entity cert
signerDigest, _ := getDigestOIDForSignatureAlgorithm(signerCert.Certificate.SignatureAlgorithm)
toBeSigned.SetDigestAlgorithm(signerDigest)

if err := toBeSigned.AddSignerChain(signerCert.Certificate, *signerCert.PrivateKey, nil, SignerInfoConfig{}); err != nil {
t.Fatalf("cannot add signer: %s", err)
}
signed, err := toBeSigned.Finish()
if err != nil {
t.Fatalf("cannot finish signing data: %s", err)
}
pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: signed})

p7, err := Parse(signed)
if err != nil {
t.Fatalf("cannot parse signed data: %s", err)
}
testOid := asn1.ObjectIdentifier{2, 3, 4, 5, 6, 7}
testValue := "TestValue"
copied, err := Copy(p7, Attribute{
Type: testOid,
Value: testValue,
})
if err != nil {
t.Fatalf("cannot copy signed data: %s", err)
}
p7, err = Parse(copied)
if err != nil {
t.Fatalf("cannot parse copied data: %s", err)
}

if !bytes.Equal(content, p7.Content) {
t.Errorf("content was not found in the copied data:\n\tExpected: %s\n\tActual: %s", content, p7.Content)
}
if err := p7.VerifyWithChain(truststore); err != nil {
t.Errorf("cannot verify copied data: %s", err)
}

var copiedValue string
if err := p7.UnmarshalUnsignedAttribute(testOid, &copiedValue); err != nil {
t.Fatalf("could not unmarshal attribute: %s", err)
}
if copiedValue != testValue {
t.Errorf("incorrect attribute value: %s", copiedValue)
}
}
13 changes: 13 additions & 0 deletions verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,19 @@ func (p7 *PKCS7) UnmarshalSignedAttribute(attributeType asn1.ObjectIdentifier, o
return unmarshalAttribute(attributes, attributeType, out)
}

// UnmarshalUnsignedAttribute decodes a single attribute from the signer info
func (p7 *PKCS7) UnmarshalUnsignedAttribute(attributeType asn1.ObjectIdentifier, out interface{}) error {
sd, ok := p7.raw.(signedData)
if !ok {
return errors.New("pkcs7: payload is not signedData content")
}
if len(sd.SignerInfos) < 1 {
return errors.New("pkcs7: payload has no signers")
}
attributes := sd.SignerInfos[0].UnauthenticatedAttributes
return unmarshalAttribute(attributes, attributeType, out)
}

func parseSignedData(data []byte) (*PKCS7, error) {
var sd signedData
asn1.Unmarshal(data, &sd)
Expand Down

0 comments on commit bf12d97

Please sign in to comment.