Skip to content

Commit 25f674c

Browse files
committed
Move WireDB interface assertion up a level
1 parent bb512e7 commit 25f674c

File tree

3 files changed

+247
-42
lines changed

3 files changed

+247
-42
lines changed

acme/challenge.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,17 @@ func (ch *Challenge) Validate(ctx context.Context, db DB, jwk *jose.JSONWebKey,
117117
case DEVICEATTEST01:
118118
return deviceAttest01Validate(ctx, ch, db, jwk, payload)
119119
case WIREOIDC01:
120-
return wireOIDC01Validate(ctx, ch, db, jwk, payload)
120+
wireDB, ok := db.(WireDB)
121+
if !ok {
122+
return NewErrorISE("db %T is not a WireDB", db)
123+
}
124+
return wireOIDC01Validate(ctx, ch, wireDB, jwk, payload)
121125
case WIREDPOP01:
122-
return wireDPOP01Validate(ctx, ch, db, jwk, payload)
126+
wireDB, ok := db.(WireDB)
127+
if !ok {
128+
return NewErrorISE("db %T is not a WireDB", db)
129+
}
130+
return wireDPOP01Validate(ctx, ch, wireDB, jwk, payload)
123131
default:
124132
return NewErrorISE("unexpected challenge type %q", ch.Type)
125133
}
@@ -392,11 +400,7 @@ type wireOidcPayload struct {
392400
IDToken string `json:"id_token"`
393401
}
394402

395-
func wireOIDC01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, payload []byte) error {
396-
wireDB, ok := db.(WireDB)
397-
if !ok {
398-
return NewErrorISE("db %T is not a WireDB", db)
399-
}
403+
func wireOIDC01Validate(ctx context.Context, ch *Challenge, db WireDB, jwk *jose.JSONWebKey, payload []byte) error {
400404
prov, ok := ProvisionerFromContext(ctx)
401405
if !ok {
402406
return NewErrorISE("missing provisioner")
@@ -476,7 +480,7 @@ func wireOIDC01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSO
476480
return WrapErrorISE(err, "error updating challenge")
477481
}
478482

479-
orders, err := wireDB.GetAllOrdersByAccountID(ctx, ch.AccountID)
483+
orders, err := db.GetAllOrdersByAccountID(ctx, ch.AccountID)
480484
if err != nil {
481485
return WrapErrorISE(err, "could not retrieve current order by account id")
482486
}
@@ -485,7 +489,7 @@ func wireOIDC01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSO
485489
}
486490

487491
order := orders[len(orders)-1]
488-
if err := wireDB.CreateOidcToken(ctx, order, transformedIDToken); err != nil {
492+
if err := db.CreateOidcToken(ctx, order, transformedIDToken); err != nil {
489493
return WrapErrorISE(err, "failed storing OIDC id token")
490494
}
491495

@@ -526,11 +530,7 @@ type wireDpopPayload struct {
526530
AccessToken string `json:"access_token"`
527531
}
528532

529-
func wireDPOP01Validate(ctx context.Context, ch *Challenge, db DB, accountJWK *jose.JSONWebKey, payload []byte) error {
530-
wireDB, ok := db.(WireDB)
531-
if !ok {
532-
return NewErrorISE("db %T is not a WireDB", db)
533-
}
533+
func wireDPOP01Validate(ctx context.Context, ch *Challenge, db WireDB, accountJWK *jose.JSONWebKey, payload []byte) error {
534534
prov, ok := ProvisionerFromContext(ctx)
535535
if !ok {
536536
return NewErrorISE("missing provisioner")
@@ -594,7 +594,7 @@ func wireDPOP01Validate(ctx context.Context, ch *Challenge, db DB, accountJWK *j
594594
return WrapErrorISE(err, "error updating challenge")
595595
}
596596

597-
orders, err := wireDB.GetAllOrdersByAccountID(ctx, ch.AccountID)
597+
orders, err := db.GetAllOrdersByAccountID(ctx, ch.AccountID)
598598
if err != nil {
599599
return WrapErrorISE(err, "could not find current order by account id")
600600
}
@@ -603,7 +603,7 @@ func wireDPOP01Validate(ctx context.Context, ch *Challenge, db DB, accountJWK *j
603603
}
604604

605605
order := orders[len(orders)-1]
606-
if err := wireDB.CreateDpopToken(ctx, order, map[string]any(*dpop)); err != nil {
606+
if err := db.CreateDpopToken(ctx, order, map[string]any(*dpop)); err != nil {
607607
return WrapErrorISE(err, "failed storing DPoP token")
608608
}
609609

acme/challenge_test.go

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,100 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
986986
},
987987
}
988988
},
989+
"fail/wire-oidc-01-no-wire-db": func(t *testing.T) test {
990+
jwk, keyAuth := mustAccountAndKeyAuthorization(t, "token")
991+
signerJWK, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
992+
require.NoError(t, err)
993+
signer, err := jose.NewSigner(jose.SigningKey{
994+
Algorithm: jose.SignatureAlgorithm(signerJWK.Algorithm),
995+
Key: signerJWK,
996+
}, new(jose.SignerOptions))
997+
require.NoError(t, err)
998+
srv := mustJWKServer(t, signerJWK.Public())
999+
tokenBytes, err := json.Marshal(struct {
1000+
jose.Claims
1001+
Name string `json:"name,omitempty"`
1002+
PreferredUsername string `json:"preferred_username,omitempty"`
1003+
KeyAuth string `json:"keyauth"`
1004+
ACMEAudience string `json:"acme_aud"`
1005+
}{
1006+
Claims: jose.Claims{
1007+
Issuer: srv.URL,
1008+
Audience: []string{"test"},
1009+
Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)),
1010+
},
1011+
Name: "Alice Smith",
1012+
PreferredUsername: "wireapp://%[email protected]",
1013+
KeyAuth: keyAuth,
1014+
ACMEAudience: "https://ca.example.com/acme/wire/challenge/azID/chID",
1015+
})
1016+
require.NoError(t, err)
1017+
signed, err := signer.Sign(tokenBytes)
1018+
require.NoError(t, err)
1019+
idToken, err := signed.CompactSerialize()
1020+
require.NoError(t, err)
1021+
payload, err := json.Marshal(struct {
1022+
IDToken string `json:"id_token"`
1023+
}{
1024+
IDToken: idToken,
1025+
})
1026+
require.NoError(t, err)
1027+
valueBytes, err := json.Marshal(struct {
1028+
Name string `json:"name,omitempty"`
1029+
Domain string `json:"domain,omitempty"`
1030+
ClientID string `json:"client-id,omitempty"`
1031+
Handle string `json:"handle,omitempty"`
1032+
}{
1033+
Name: "Alice Smith",
1034+
Domain: "wire.com",
1035+
ClientID: "wireapp://[email protected]",
1036+
Handle: "wireapp://%[email protected]",
1037+
})
1038+
require.NoError(t, err)
1039+
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{
1040+
Wire: &wireprovisioner.Options{
1041+
OIDC: &wireprovisioner.OIDCOptions{
1042+
Provider: &wireprovisioner.Provider{
1043+
IssuerURL: srv.URL,
1044+
JWKSURL: srv.URL + "/keys",
1045+
Algorithms: []string{"ES256"},
1046+
},
1047+
Config: &wireprovisioner.Config{
1048+
ClientID: "test",
1049+
SignatureAlgorithms: []string{"ES256"},
1050+
Now: time.Now,
1051+
},
1052+
TransformTemplate: "",
1053+
},
1054+
DPOP: &wireprovisioner.DPOPOptions{
1055+
SigningKey: []byte(fakeKey),
1056+
},
1057+
},
1058+
}))
1059+
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
1060+
return test{
1061+
ch: &Challenge{
1062+
ID: "chID",
1063+
AuthorizationID: "azID",
1064+
AccountID: "accID",
1065+
Token: "token",
1066+
Type: "wire-oidc-01",
1067+
Status: StatusPending,
1068+
Value: string(valueBytes),
1069+
},
1070+
srv: srv,
1071+
payload: payload,
1072+
ctx: ctx,
1073+
jwk: jwk,
1074+
db: &MockDB{},
1075+
err: &Error{
1076+
Type: "urn:ietf:params:acme:error:serverInternal",
1077+
Detail: "The server experienced an internal error",
1078+
Status: 500,
1079+
Err: errors.New("db *acme.MockDB is not a WireDB"),
1080+
},
1081+
}
1082+
},
9891083
"ok/wire-dpop-01": func(t *testing.T) test {
9901084
jwk, keyAuth := mustAccountAndKeyAuthorization(t, "token")
9911085
_ = keyAuth // TODO(hs): keyAuth (not) required for DPoP? Or needs to be added to validation?
@@ -1138,6 +1232,141 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
11381232
},
11391233
}
11401234
},
1235+
"fail/wire-dpop-01-no-wire-db": func(t *testing.T) test {
1236+
jwk, _ := mustAccountAndKeyAuthorization(t, "token")
1237+
dpopSigner, err := jose.NewSigner(jose.SigningKey{
1238+
Algorithm: jose.SignatureAlgorithm(jwk.Algorithm),
1239+
Key: jwk,
1240+
}, new(jose.SignerOptions))
1241+
require.NoError(t, err)
1242+
signerJWK, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
1243+
require.NoError(t, err)
1244+
signer, err := jose.NewSigner(jose.SigningKey{
1245+
Algorithm: jose.SignatureAlgorithm(signerJWK.Algorithm),
1246+
Key: signerJWK,
1247+
}, new(jose.SignerOptions))
1248+
require.NoError(t, err)
1249+
signerPEMBlock, err := pemutil.Serialize(signerJWK.Public().Key)
1250+
require.NoError(t, err)
1251+
signerPEMBytes := pem.EncodeToMemory(signerPEMBlock)
1252+
dpopBytes, err := json.Marshal(struct {
1253+
jose.Claims
1254+
Challenge string `json:"chal,omitempty"`
1255+
Handle string `json:"handle,omitempty"`
1256+
Nonce string `json:"nonce,omitempty"`
1257+
HTU string `json:"htu,omitempty"`
1258+
Name string `json:"name,omitempty"`
1259+
}{
1260+
Claims: jose.Claims{
1261+
Subject: "wireapp://[email protected]",
1262+
Audience: jose.Audience{"https://ca.example.com/acme/wire/challenge/azID/chID"},
1263+
},
1264+
Challenge: "token",
1265+
Handle: "wireapp://%[email protected]",
1266+
Nonce: "nonce",
1267+
HTU: "http://issuer.example.com",
1268+
Name: "Alice Smith",
1269+
})
1270+
require.NoError(t, err)
1271+
dpop, err := dpopSigner.Sign(dpopBytes)
1272+
require.NoError(t, err)
1273+
proof, err := dpop.CompactSerialize()
1274+
require.NoError(t, err)
1275+
tokenBytes, err := json.Marshal(struct {
1276+
jose.Claims
1277+
Challenge string `json:"chal,omitempty"`
1278+
Nonce string `json:"nonce,omitempty"`
1279+
Cnf struct {
1280+
Kid string `json:"kid,omitempty"`
1281+
} `json:"cnf"`
1282+
Proof string `json:"proof,omitempty"`
1283+
ClientID string `json:"client_id"`
1284+
APIVersion int `json:"api_version"`
1285+
Scope string `json:"scope"`
1286+
}{
1287+
Claims: jose.Claims{
1288+
Issuer: "http://issuer.example.com",
1289+
Audience: jose.Audience{"https://ca.example.com/acme/wire/challenge/azID/chID"},
1290+
Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)),
1291+
},
1292+
Challenge: "token",
1293+
Nonce: "nonce",
1294+
Cnf: struct {
1295+
Kid string `json:"kid,omitempty"`
1296+
}{
1297+
Kid: jwk.KeyID,
1298+
},
1299+
Proof: proof,
1300+
ClientID: "wireapp://[email protected]",
1301+
APIVersion: 5,
1302+
Scope: "wire_client_id",
1303+
})
1304+
require.NoError(t, err)
1305+
signed, err := signer.Sign(tokenBytes)
1306+
require.NoError(t, err)
1307+
accessToken, err := signed.CompactSerialize()
1308+
require.NoError(t, err)
1309+
payload, err := json.Marshal(struct {
1310+
AccessToken string `json:"access_token"`
1311+
}{
1312+
AccessToken: accessToken,
1313+
})
1314+
require.NoError(t, err)
1315+
valueBytes, err := json.Marshal(struct {
1316+
Name string `json:"name,omitempty"`
1317+
Domain string `json:"domain,omitempty"`
1318+
ClientID string `json:"client-id,omitempty"`
1319+
Handle string `json:"handle,omitempty"`
1320+
}{
1321+
Name: "Alice Smith",
1322+
Domain: "wire.com",
1323+
ClientID: "wireapp://[email protected]",
1324+
Handle: "wireapp://%[email protected]",
1325+
})
1326+
require.NoError(t, err)
1327+
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{
1328+
Wire: &wireprovisioner.Options{
1329+
OIDC: &wireprovisioner.OIDCOptions{
1330+
Provider: &wireprovisioner.Provider{
1331+
IssuerURL: "http://issuerexample.com",
1332+
Algorithms: []string{"ES256"},
1333+
},
1334+
Config: &wireprovisioner.Config{
1335+
ClientID: "test",
1336+
SignatureAlgorithms: []string{"ES256"},
1337+
Now: time.Now,
1338+
},
1339+
TransformTemplate: "",
1340+
},
1341+
DPOP: &wireprovisioner.DPOPOptions{
1342+
Target: "http://issuer.example.com",
1343+
SigningKey: signerPEMBytes,
1344+
},
1345+
},
1346+
}))
1347+
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
1348+
return test{
1349+
ch: &Challenge{
1350+
ID: "chID",
1351+
AuthorizationID: "azID",
1352+
AccountID: "accID",
1353+
Token: "token",
1354+
Type: "wire-dpop-01",
1355+
Status: StatusPending,
1356+
Value: string(valueBytes),
1357+
},
1358+
payload: payload,
1359+
ctx: ctx,
1360+
jwk: jwk,
1361+
db: &MockDB{},
1362+
err: &Error{
1363+
Type: "urn:ietf:params:acme:error:serverInternal",
1364+
Detail: "The server experienced an internal error",
1365+
Status: 500,
1366+
Err: errors.New("db *acme.MockDB is not a WireDB"),
1367+
},
1368+
}
1369+
},
11411370
}
11421371
for name, run := range tests {
11431372
t.Run(name, func(t *testing.T) {

acme/challenge_wire_test.go

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,12 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
2929
type test struct {
3030
ch *Challenge
3131
jwk *jose.JSONWebKey
32-
db DB
32+
db WireDB
3333
payload []byte
3434
ctx context.Context
3535
expectedErr *Error
3636
}
3737
tests := map[string]func(t *testing.T) test{
38-
"fail/no-wire-db": func(t *testing.T) test {
39-
return test{
40-
ctx: context.Background(),
41-
db: &MockDB{},
42-
expectedErr: &Error{
43-
Type: "urn:ietf:params:acme:error:serverInternal",
44-
Detail: "The server experienced an internal error",
45-
Status: 500,
46-
Err: errors.New("db *acme.MockDB is not a WireDB"),
47-
},
48-
}
49-
},
5038
"fail/no-provisioner": func(t *testing.T) test {
5139
return test{
5240
ctx: context.Background(),
@@ -1094,25 +1082,13 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
10941082
type test struct {
10951083
ch *Challenge
10961084
jwk *jose.JSONWebKey
1097-
db DB
1085+
db WireDB
10981086
payload []byte
10991087
srv *httptest.Server
11001088
ctx context.Context
11011089
expectedErr *Error
11021090
}
11031091
tests := map[string]func(t *testing.T) test{
1104-
"fail/no-wire-db": func(t *testing.T) test {
1105-
return test{
1106-
ctx: context.Background(),
1107-
db: &MockDB{},
1108-
expectedErr: &Error{
1109-
Type: "urn:ietf:params:acme:error:serverInternal",
1110-
Detail: "The server experienced an internal error",
1111-
Status: 500,
1112-
Err: errors.New("db *acme.MockDB is not a WireDB"),
1113-
},
1114-
}
1115-
},
11161092
"fail/no-provisioner": func(t *testing.T) test {
11171093
return test{
11181094
ctx: context.Background(),

0 commit comments

Comments
 (0)