Skip to content

Commit cf8313d

Browse files
committed
signature/pkcs7: address review feedback
- Restrict to RSA-2048 + SHA-256 + RSASSA-PKCS#1 v1.5; reject other keys with UnsupportedSigningKeyError and remove the unreachable EC branches in getEncryptionOID. - Reject SignRequest fields the dm-verity profile cannot honor (SigningTime, Expiry, SigningScheme, SigningAgent, ExtendedSignedAttributes) and nil Signer with InvalidSignRequestError. - Verify upstream signer output is RSASSA-PKCS#1 v1.5 over SHA-256 of the payload before wrapping it. - Verify() returns exported sentinel ErrDetachedNotVerifiable; detached PKCS#7 cannot be verified through signature.Envelope.Verify. - Content() leaves SignerInfo.SignatureAlgorithm zero rather than mislabeling PKCS#1 v1.5 as PSS (enum lacks PKCS#1 v1.5 constants). - Surface the post-Sign Parse error instead of swallowing it. - Recover from gopkcs7 v0.9.0 panics on malformed BER. - signerAdapter panics on a second Sign call (single-use invariant). - Add compile-time signature.Envelope assertion; go mod tidy moves gopkcs7 to direct require. - Add negative tests, seeded fuzz target, and conformance test (OIDs, no signed attrs, detached, chain ordering, independent rsa.VerifyPKCS1v15). Signed-off-by: Dallas Delaney <dadelan@microsoft.com>
1 parent 7dc8aca commit cf8313d

5 files changed

Lines changed: 402 additions & 149 deletions

File tree

go.mod

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@ require (
77
github.com/golang-jwt/jwt/v4 v4.5.2
88
github.com/notaryproject/tspclient-go v1.0.0
99
github.com/veraison/go-cose v1.3.0
10+
go.mozilla.org/pkcs7 v0.9.0
1011
golang.org/x/crypto v0.37.0
1112
)
1213

13-
require (
14-
github.com/x448/float16 v0.8.4 // indirect
15-
go.mozilla.org/pkcs7 v0.9.0 // indirect
16-
)
14+
require github.com/x448/float16 v0.8.4 // indirect
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright The Notary Project Authors.
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package pkcs7
15+
16+
import (
17+
"crypto"
18+
"crypto/rsa"
19+
"crypto/sha256"
20+
"testing"
21+
22+
gopkcs7 "go.mozilla.org/pkcs7"
23+
24+
"github.com/notaryproject/notation-core-go/signature"
25+
)
26+
27+
// TestConformance asserts that Sign() produces a PKCS#7 SignedData that meets
28+
// the kernel dm-verity profile: RSASSA-PKCS#1 v1.5 over SHA-256, no signed
29+
// attributes, detached content, single signer.
30+
func TestConformance(t *testing.T) {
31+
signer := newRSATestSigner()
32+
encoded, err := NewEnvelope().Sign(&signature.SignRequest{
33+
Payload: signature.Payload{ContentType: MediaTypeEnvelope, Content: []byte(testPayload)},
34+
Signer: signer,
35+
})
36+
if err != nil {
37+
t.Fatalf("Sign() error: %v", err)
38+
}
39+
40+
p7, err := gopkcs7.Parse(encoded)
41+
if err != nil {
42+
t.Fatalf("Parse() error: %v", err)
43+
}
44+
45+
if got, want := len(p7.Signers), 1; got != want {
46+
t.Fatalf("Signers = %d, want %d", got, want)
47+
}
48+
49+
// Cert chain must be leaf + root. dm-verity has no intermediates.
50+
if got, want := len(p7.Certificates), 2; got != want {
51+
t.Fatalf("Certificates = %d, want %d (leaf + root)", got, want)
52+
}
53+
if !p7.Certificates[0].Equal(signer.certs[0]) {
54+
t.Errorf("Certificates[0] is not the leaf certificate")
55+
}
56+
if !p7.Certificates[1].Equal(signer.certs[1]) {
57+
t.Errorf("Certificates[1] is not the root certificate")
58+
}
59+
60+
si := p7.Signers[0]
61+
if !si.DigestAlgorithm.Algorithm.Equal(gopkcs7.OIDDigestAlgorithmSHA256) {
62+
t.Errorf("DigestAlgorithm = %v, want SHA-256", si.DigestAlgorithm.Algorithm)
63+
}
64+
if !si.DigestEncryptionAlgorithm.Algorithm.Equal(gopkcs7.OIDEncryptionAlgorithmRSA) {
65+
t.Errorf("DigestEncryptionAlgorithm = %v, want rsaEncryption", si.DigestEncryptionAlgorithm.Algorithm)
66+
}
67+
if len(si.AuthenticatedAttributes) != 0 {
68+
t.Errorf("AuthenticatedAttributes len = %d, want 0", len(si.AuthenticatedAttributes))
69+
}
70+
if len(p7.Content) != 0 {
71+
t.Errorf("Content len = %d, want 0 (must be detached)", len(p7.Content))
72+
}
73+
74+
// Independently verify the bytes are RSASSA-PKCS#1 v1.5 over SHA-256
75+
// of the payload.
76+
digest := sha256.Sum256([]byte(testPayload))
77+
pub, ok := signer.certs[0].PublicKey.(*rsa.PublicKey)
78+
if !ok {
79+
t.Fatalf("leaf public key is %T, want *rsa.PublicKey", signer.certs[0].PublicKey)
80+
}
81+
if err := rsa.VerifyPKCS1v15(pub, crypto.SHA256, digest[:], si.EncryptedDigest); err != nil {
82+
t.Fatalf("rsa.VerifyPKCS1v15 over SHA-256(payload) failed: %v", err)
83+
}
84+
}

0 commit comments

Comments
 (0)