Skip to content

Commit bf685f0

Browse files
committed
fix: address Copilot review comments
PR SUNET#263 Code Quality Fixes: - jws.go: Use %v instead of %s for non-string types in error messages - attachment.go: Use RawURLEncoding for base64url (no padding per DIDComm spec) - attachment.go: Fix AttachmentData comment to match implementation (multiple formats allowed) - packer.go: Return error when SignBeforeEncrypt=true but SignerKey=nil (was silent no-op) - route.go: Document BuildRoute fallback behavior and RoutingKey usage - ecdsa/suite.go: Add context parameter for HSM signer support - transport/doc.go: Update example to use SendRequest struct - message/doc.go: Fix WithTo example (variadic, not slice) - signer_test.go: Check rand.Read errors properly
1 parent 491b2e3 commit bf685f0

File tree

12 files changed

+47
-27
lines changed

12 files changed

+47
-27
lines changed

pkg/didcomm/crypto/jws.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,10 +156,10 @@ func determineSigningAlgorithm(key jwk.Key, hint string) (jwa.SignatureAlgorithm
156156
if crv.String() == "secp256k1" {
157157
return jwa.ES256K(), nil
158158
}
159-
return jwa.ES256(), fmt.Errorf("%w: unsupported curve %s", ErrUnsupportedAlgorithm, crv)
159+
return jwa.ES256(), fmt.Errorf("%w: unsupported curve %v", ErrUnsupportedAlgorithm, crv)
160160
}
161161
default:
162-
return jwa.EdDSA(), fmt.Errorf("%w: unsupported key type %s", ErrUnsupportedAlgorithm, kty)
162+
return jwa.EdDSA(), fmt.Errorf("%w: unsupported key type %v", ErrUnsupportedAlgorithm, kty)
163163
}
164164
}
165165

pkg/didcomm/message/attachment.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ type Attachment struct {
3939
}
4040

4141
// AttachmentData represents the actual attachment content.
42-
// Exactly one of JWS, Hash, Links, Base64, or JSON should be set.
42+
// At least one of JWS, Links, Base64, or JSON should be set.
43+
// Multiple formats are allowed per DIDComm spec.
4344
type AttachmentData struct {
4445
// JWS is a JSON Web Signature wrapping the content.
4546
// Provides integrity and optional authentication.
@@ -71,7 +72,7 @@ func (a *Attachment) Validate() error {
7172
// Works for Base64 and JSON encoded content.
7273
func (a *Attachment) GetBytes() ([]byte, error) {
7374
if a.Data.Base64 != "" {
74-
return base64.URLEncoding.DecodeString(a.Data.Base64)
75+
return base64.RawURLEncoding.DecodeString(a.Data.Base64)
7576
}
7677
if a.Data.JSON != nil {
7778
return json.Marshal(a.Data.JSON)
@@ -111,7 +112,7 @@ func (a *Attachment) GetJSON(v any) error {
111112
return json.Unmarshal(data, v)
112113
}
113114
if a.Data.Base64 != "" {
114-
data, err := base64.URLEncoding.DecodeString(a.Data.Base64)
115+
data, err := base64.RawURLEncoding.DecodeString(a.Data.Base64)
115116
if err != nil {
116117
return err
117118
}
@@ -144,13 +145,13 @@ func (d *AttachmentData) Validate() error {
144145
return nil
145146
}
146147

147-
// NewBase64Attachment creates an attachment with base64-encoded data.
148+
// NewBase64Attachment creates an attachment with base64url-encoded data (no padding).
148149
func NewBase64Attachment(id string, mediaType string, data []byte) Attachment {
149150
return Attachment{
150151
ID: id,
151152
MediaType: mediaType,
152153
Data: AttachmentData{
153-
Base64: base64.URLEncoding.EncodeToString(data),
154+
Base64: base64.RawURLEncoding.EncodeToString(data),
154155
},
155156
}
156157
}

pkg/didcomm/message/doc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
//
2121
// msg := message.New(
2222
// message.WithType("https://example.com/protocols/1.0/hello"),
23-
// message.WithTo([]string{"did:example:bob"}),
23+
// message.WithTo("did:example:bob"),
2424
// message.WithFrom("did:example:alice"),
2525
// message.WithBody(map[string]any{"greeting": "Hello, Bob!"}),
2626
// )

pkg/didcomm/packer.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,10 @@ func Pack(ctx context.Context, msg *message.Message, opts PackOptions) (*PackRes
7575
}
7676

7777
// Step 1: Sign if requested
78-
if opts.SignBeforeEncrypt && opts.SignerKey != nil {
78+
if opts.SignBeforeEncrypt {
79+
if opts.SignerKey == nil {
80+
return nil, fmt.Errorf("SignBeforeEncrypt requires SignerKey to be set")
81+
}
7982
signedMsg, err := crypto.Sign(ctx, plaintext, opts.SignerKey, crypto.SignOptions{})
8083
if err != nil {
8184
return nil, fmt.Errorf("failed to sign message: %w", err)

pkg/didcomm/routing/route.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@ const DefaultMaxHops = 10
1313
// Hop represents a single hop in a routing path.
1414
type Hop struct {
1515
// RecipientDID is the DID that should receive the message at this hop.
16+
// This is used for encryption (Encrypter interface requires DIDs).
1617
RecipientDID string
1718

18-
// RoutingKey is the key ID to use for encryption at this hop.
19-
// If empty, the recipient's default key agreement key is used.
19+
// RoutingKey is the original key ID from the service endpoint.
20+
// May be a DID URL (e.g., did:example:mediator#key-1) or a DID.
21+
// The Encrypter uses RecipientDID (extracted from RoutingKey if needed)
22+
// since the encryption interface operates on DIDs, not key IDs.
2023
RoutingKey string
2124

2225
// ServiceEndpoint is the endpoint URL for this hop (if known).
@@ -107,7 +110,10 @@ func (rb *RouteBuilder) buildRouteRecursive(ctx context.Context, did string, dep
107110
// Resolve the DID's service endpoint
108111
service, err := rb.resolver.ResolveDIDCommService(ctx, did)
109112
if err != nil {
110-
// If we can't resolve, assume direct delivery
113+
// Fallback to direct delivery when service resolution fails.
114+
// This allows sending to DIDs that don't publish DIDComm services,
115+
// treating them as direct recipients. Callers should handle this
116+
// by attempting direct delivery or reporting the missing endpoint.
111117
return &Route{
112118
FinalRecipient: did,
113119
Hops: []Hop{

pkg/didcomm/transport/doc.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414
// Messages are sent as POST requests with appropriate content types.
1515
//
1616
// client := transport.NewHTTPClient()
17-
// response, err := client.Send(ctx, "https://example.com/didcomm", message)
17+
// response, err := client.Send(ctx, transport.SendRequest{
18+
// Endpoint: "https://example.com/didcomm",
19+
// Message: packedMessage,
20+
// MediaType: "application/didcomm-encrypted+json",
21+
// })
1822
//
1923
// # Server Handler
2024
//

pkg/openid4vp/vc20_handler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -921,7 +921,7 @@ func (h *VC20Handler) signECDSA2019(cred *credential.RDFCredential) (*credential
921921
}
922922

923923
suite := ecdsaSuite.NewSuite()
924-
return suite.Sign(cred, key, &ecdsaSuite.SignOptions{
924+
return suite.Sign(context.Background(), cred, key, &ecdsaSuite.SignOptions{
925925
VerificationMethod: h.signerConfig.VerificationMethod,
926926
ProofPurpose: "assertionMethod",
927927
Created: time.Now().UTC(),

pkg/openid4vp/vc20_handler_integration_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func TestVC20Handler_VerifyECDSA2019_Integration(t *testing.T) {
5050
cred, err := credential.NewRDFCredentialFromJSON(credBytes, nil)
5151
require.NoError(t, err)
5252

53-
signedCred, err := suite.Sign(cred, key, &ecdsaSuite.SignOptions{
53+
signedCred, err := suite.Sign(t.Context(), cred, key, &ecdsaSuite.SignOptions{
5454
VerificationMethod: "did:example:issuer#key-1",
5555
Created: time.Now(),
5656
ProofPurpose: "assertionMethod",
@@ -218,7 +218,7 @@ func TestVC20Handler_VP_Extraction(t *testing.T) {
218218
cred, err := credential.NewRDFCredentialFromJSON(credBytes, nil)
219219
require.NoError(t, err)
220220

221-
signedCred, err := suite.Sign(cred, key, &ecdsaSuite.SignOptions{
221+
signedCred, err := suite.Sign(t.Context(), cred, key, &ecdsaSuite.SignOptions{
222222
VerificationMethod: "did:example:issuer#key-1",
223223
Created: time.Now(),
224224
ProofPurpose: "assertionMethod",
@@ -283,7 +283,7 @@ func TestVC20Handler_TimeValidation(t *testing.T) {
283283
cred, err := credential.NewRDFCredentialFromJSON(credBytes, nil)
284284
require.NoError(t, err)
285285

286-
signedCred, err := suite.Sign(cred, key, &ecdsaSuite.SignOptions{
286+
signedCred, err := suite.Sign(t.Context(), cred, key, &ecdsaSuite.SignOptions{
287287
VerificationMethod: "did:example:issuer#key-1",
288288
Created: time.Now(),
289289
ProofPurpose: "assertionMethod",
@@ -323,7 +323,7 @@ func TestVC20Handler_TimeValidation(t *testing.T) {
323323
cred, err := credential.NewRDFCredentialFromJSON(credBytes, nil)
324324
require.NoError(t, err)
325325

326-
signedCred, err := suite.Sign(cred, key, &ecdsaSuite.SignOptions{
326+
signedCred, err := suite.Sign(t.Context(), cred, key, &ecdsaSuite.SignOptions{
327327
VerificationMethod: "did:example:issuer#key-1",
328328
Created: time.Now().Add(-time.Hour * 24 * 30),
329329
ProofPurpose: "assertionMethod",

pkg/openid4vp/vc20_vp_builder.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package openid4vp
44

55
import (
6+
"context"
67
"crypto"
78
"crypto/ecdsa"
89
"crypto/ed25519"
@@ -223,7 +224,7 @@ func (b *VPBuilder) signWithECDSA(vpBytes []byte, privateKey crypto.PrivateKey,
223224

224225
// Sign
225226
suite := ecdsaSuite.NewSuite()
226-
signedVP, err := suite.Sign(vpCred, ecKey, signOpts)
227+
signedVP, err := suite.Sign(context.Background(), vpCred, ecKey, signOpts)
227228
if err != nil {
228229
return nil, fmt.Errorf("ECDSA signing failed: %w", err)
229230
}

pkg/vc20/crypto/ecdsa/suite.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,18 @@ type SignOptions struct {
4141
}
4242

4343
// Sign signs a credential using ecdsa-rdfc-2019
44-
func (s *Suite) Sign(cred *credential.RDFCredential, key *ecdsa.PrivateKey, opts *SignOptions) (*credential.RDFCredential, error) {
44+
func (s *Suite) Sign(ctx context.Context, cred *credential.RDFCredential, key *ecdsa.PrivateKey, opts *SignOptions) (*credential.RDFCredential, error) {
4545
if key == nil {
4646
return nil, fmt.Errorf("private key is nil")
4747
}
4848
// Wrap the raw key and delegate to SignWithSigner
4949
wrapper := vccrypto.NewECDSAKeyWrapper(key)
50-
return s.SignWithSigner(cred, wrapper, opts)
50+
return s.SignWithSigner(ctx, cred, wrapper, opts)
5151
}
5252

5353
// SignWithSigner signs a credential using ecdsa-rdfc-2019 with a VCSigner.
54-
// This method supports both raw keys (via wrappers) and HSM-based keys (via pki.RawSigner).
55-
func (s *Suite) SignWithSigner(cred *credential.RDFCredential, signer vccrypto.VCSigner, opts *SignOptions) (*credential.RDFCredential, error) {
54+
// This method supports both raw keys (via wrappers) and HSM-backed keys (via pki.RawSigner).
55+
func (s *Suite) SignWithSigner(ctx context.Context, cred *credential.RDFCredential, signer vccrypto.VCSigner, opts *SignOptions) (*credential.RDFCredential, error) {
5656
if cred == nil {
5757
return nil, fmt.Errorf("credential is nil")
5858
}
@@ -130,7 +130,7 @@ func (s *Suite) SignWithSigner(cred *credential.RDFCredential, signer vccrypto.V
130130
combined := append(proofHashBytes[:], docHashBytes[:]...)
131131

132132
// 5. Sign using the VCSigner
133-
signature, err := signer.SignDigest(context.Background(), combined)
133+
signature, err := signer.SignDigest(ctx, combined)
134134
if err != nil {
135135
return nil, fmt.Errorf("failed to sign: %w", err)
136136
}

0 commit comments

Comments
 (0)