5
5
"crypto/x509"
6
6
"encoding/base64"
7
7
"encoding/json"
8
+ "fmt"
8
9
"net"
9
10
"net/http"
10
11
"strings"
@@ -16,6 +17,7 @@ import (
16
17
"go.step.sm/crypto/x509util"
17
18
18
19
"github.com/smallstep/certificates/acme"
20
+ "github.com/smallstep/certificates/acme/wire"
19
21
"github.com/smallstep/certificates/api/render"
20
22
"github.com/smallstep/certificates/authority/policy"
21
23
"github.com/smallstep/certificates/authority/provisioner"
@@ -48,16 +50,86 @@ func (n *NewOrderRequest) Validate() error {
48
50
if id .Value == "" {
49
51
return acme .NewError (acme .ErrorMalformedType , "permanent identifier cannot be empty" )
50
52
}
53
+ case acme .WireUser , acme .WireDevice :
54
+ // validation of Wire identifiers is performed in `validateWireIdentifiers`, but
55
+ // marked here as known and supported types.
56
+ continue
51
57
default :
52
58
return acme .NewError (acme .ErrorMalformedType , "identifier type unsupported: %s" , id .Type )
53
59
}
60
+ }
54
61
55
- // TODO(hs): add some validations for DNS domains?
56
- // TODO(hs): combine the errors from this with allow/deny policy, like example error in https://datatracker.ietf.org/doc/html/rfc8555#section-6.7.1
62
+ if err := n . validateWireIdentifiers (); err != nil {
63
+ return acme . WrapError ( acme . ErrorMalformedType , err , "failed validating Wire identifiers" )
57
64
}
65
+
66
+ // TODO(hs): add some validations for DNS domains?
67
+ // TODO(hs): combine the errors from this with allow/deny policy, like example error in https://datatracker.ietf.org/doc/html/rfc8555#section-6.7.1
68
+
58
69
return nil
59
70
}
60
71
72
+ func (n * NewOrderRequest ) validateWireIdentifiers () error {
73
+ if ! n .hasWireIdentifiers () {
74
+ return nil
75
+ }
76
+
77
+ userIdentifiers := identifiersOfType (acme .WireUser , n .Identifiers )
78
+ deviceIdentifiers := identifiersOfType (acme .WireDevice , n .Identifiers )
79
+
80
+ if len (userIdentifiers ) != 1 {
81
+ return fmt .Errorf ("expected exactly one Wire UserID identifier; got %d" , len (userIdentifiers ))
82
+ }
83
+ if len (deviceIdentifiers ) != 1 {
84
+ return fmt .Errorf ("expected exactly one Wire DeviceID identifier, got %d" , len (deviceIdentifiers ))
85
+ }
86
+
87
+ wireUserID , err := wire .ParseUserID (userIdentifiers [0 ].Value )
88
+ if err != nil {
89
+ return fmt .Errorf ("failed parsing Wire UserID: %w" , err )
90
+ }
91
+
92
+ wireDeviceID , err := wire .ParseDeviceID (deviceIdentifiers [0 ].Value )
93
+ if err != nil {
94
+ return fmt .Errorf ("failed parsing Wire DeviceID: %w" , err )
95
+ }
96
+ if _ , err := wire .ParseClientID (wireDeviceID .ClientID ); err != nil {
97
+ return fmt .Errorf ("invalid Wire client ID %q: %w" , wireDeviceID .ClientID , err )
98
+ }
99
+
100
+ switch {
101
+ case wireUserID .Domain != wireDeviceID .Domain :
102
+ return fmt .Errorf ("UserID domain %q does not match DeviceID domain %q" , wireUserID .Domain , wireDeviceID .Domain )
103
+ case wireUserID .Name != wireDeviceID .Name :
104
+ return fmt .Errorf ("UserID name %q does not match DeviceID name %q" , wireUserID .Name , wireDeviceID .Name )
105
+ case wireUserID .Handle != wireDeviceID .Handle :
106
+ return fmt .Errorf ("UserID handle %q does not match DeviceID handle %q" , wireUserID .Handle , wireDeviceID .Handle )
107
+ }
108
+
109
+ return nil
110
+ }
111
+
112
+ // hasWireIdentifiers returns whether the [NewOrderRequest] contains
113
+ // Wire identifiers.
114
+ func (n * NewOrderRequest ) hasWireIdentifiers () bool {
115
+ for _ , i := range n .Identifiers {
116
+ if i .Type == acme .WireUser || i .Type == acme .WireDevice {
117
+ return true
118
+ }
119
+ }
120
+ return false
121
+ }
122
+
123
+ // identifiersOfType returns the Identifiers that are of type typ.
124
+ func identifiersOfType (typ acme.IdentifierType , ids []acme.Identifier ) (result []acme.Identifier ) {
125
+ for _ , id := range ids {
126
+ if id .Type == typ {
127
+ result = append (result , id )
128
+ }
129
+ }
130
+ return
131
+ }
132
+
61
133
// FinalizeRequest captures the body for a Finalize order request.
62
134
type FinalizeRequest struct {
63
135
CSR string `json:"csr"`
@@ -263,12 +335,43 @@ func newAuthorization(ctx context.Context, az *acme.Authorization) error {
263
335
continue
264
336
}
265
337
338
+ var target string
339
+ switch az .Identifier .Type {
340
+ case acme .WireUser :
341
+ wireOptions , err := prov .GetOptions ().GetWireOptions ()
342
+ if err != nil {
343
+ return acme .WrapErrorISE (err , "failed getting Wire options" )
344
+ }
345
+ target , err = wireOptions .GetOIDCOptions ().EvaluateTarget ("" ) // TODO(hs): determine if required by Wire
346
+ if err != nil {
347
+ return acme .WrapError (acme .ErrorMalformedType , err , "invalid Go template registered for 'target'" )
348
+ }
349
+ case acme .WireDevice :
350
+ wireID , err := wire .ParseDeviceID (az .Identifier .Value )
351
+ if err != nil {
352
+ return acme .WrapError (acme .ErrorMalformedType , err , "failed parsing WireDevice" )
353
+ }
354
+ clientID , err := wire .ParseClientID (wireID .ClientID )
355
+ if err != nil {
356
+ return acme .WrapError (acme .ErrorMalformedType , err , "failed parsing ClientID" )
357
+ }
358
+ wireOptions , err := prov .GetOptions ().GetWireOptions ()
359
+ if err != nil {
360
+ return acme .WrapErrorISE (err , "failed getting Wire options" )
361
+ }
362
+ target , err = wireOptions .GetDPOPOptions ().EvaluateTarget (clientID .DeviceID )
363
+ if err != nil {
364
+ return acme .WrapError (acme .ErrorMalformedType , err , "invalid Go template registered for 'target'" )
365
+ }
366
+ }
367
+
266
368
ch := & acme.Challenge {
267
369
AccountID : az .AccountID ,
268
370
Value : az .Identifier .Value ,
269
371
Type : typ ,
270
372
Token : az .Token ,
271
373
Status : acme .StatusPending ,
374
+ Target : target ,
272
375
}
273
376
if err := db .CreateChallenge (ctx , ch ); err != nil {
274
377
return acme .WrapErrorISE (err , "error creating challenge" )
@@ -400,6 +503,10 @@ func challengeTypes(az *acme.Authorization) []acme.ChallengeType {
400
503
}
401
504
case acme .PermanentIdentifier :
402
505
chTypes = []acme.ChallengeType {acme .DEVICEATTEST01 }
506
+ case acme .WireUser :
507
+ chTypes = []acme.ChallengeType {acme .WIREOIDC01 }
508
+ case acme .WireDevice :
509
+ chTypes = []acme.ChallengeType {acme .WIREDPOP01 }
403
510
default :
404
511
chTypes = []acme.ChallengeType {}
405
512
}
0 commit comments