Skip to content

Commit 1fc663b

Browse files
committed
Refactor step crypto keypair tests to use testscript
1 parent 825b161 commit 1fc663b

5 files changed

Lines changed: 256 additions & 110 deletions

File tree

go.mod

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,8 @@ require (
1414
github.com/icrowley/fake v0.0.0-20221112152111-d7b7e2276db2
1515
github.com/manifoldco/promptui v0.9.0
1616
github.com/pkg/errors v0.9.1
17-
<<<<<<< HEAD
1817
github.com/pquerna/otp v1.5.0
19-
=======
20-
github.com/pquerna/otp v1.4.0
2118
github.com/rogpeppe/go-internal v1.13.1
22-
>>>>>>> b69ee0ea (Refactor CLI to make it testable using `testscript`)
2319
github.com/slackhq/nebula v1.9.5
2420
github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262
2521
github.com/smallstep/certificates v0.28.3
@@ -142,12 +138,12 @@ require (
142138
golang.org/x/sync v0.15.0 // indirect
143139
golang.org/x/text v0.26.0 // indirect
144140
golang.org/x/time v0.11.0 // indirect
141+
golang.org/x/tools v0.33.0 // indirect
145142
google.golang.org/api v0.234.0 // indirect
146143
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 // indirect
147144
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 // indirect
148145
google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9 // indirect
149146
google.golang.org/grpc v1.72.2 // indirect
150-
golang.org/x/tools v0.31.0 // indirect
151147
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 // indirect
152148
gopkg.in/yaml.v3 v3.0.1 // indirect
153149
howett.net/plist v1.0.0 // indirect

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,8 +474,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
474474
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
475475
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
476476
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
477-
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
478-
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
477+
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
478+
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
479479
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
480480
google.golang.org/api v0.234.0 h1:d3sAmYq3E9gdr2mpmiWGbm9pHsA/KJmyiLkwKfHBqU4=
481481
google.golang.org/api v0.234.0/go.mod h1:QpeJkemzkFKe5VCE/PMv7GsUfn9ZF+u+q1Q7w6ckxTg=

integration/keypair_test.go

Lines changed: 0 additions & 102 deletions
This file was deleted.

integration/script/crypto_test.go

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
package script
22

33
import (
4+
"bytes"
5+
"crypto"
6+
"crypto/ecdsa"
7+
"crypto/elliptic"
8+
"crypto/rsa"
9+
"fmt"
410
"os"
511
"path/filepath"
12+
"strconv"
13+
"strings"
614
"testing"
715
"time"
816

@@ -44,7 +52,6 @@ func TestCryptoJWTCommand(t *testing.T) {
4452
testscript.Run(t, testscript.Params{
4553
Files: []string{"testdata/crypto/jwt.txtar"},
4654
Setup: func(e *testscript.Env) error {
47-
4855
err := os.WriteFile(filepath.Join(e.Cd, "p256.pem"), b, 0600)
4956
require.NoError(t, err)
5057
err = os.WriteFile(filepath.Join(e.Cd, "token.txt"), []byte(raw), 0600)
@@ -55,8 +62,121 @@ func TestCryptoJWTCommand(t *testing.T) {
5562
})
5663
}
5764

65+
func TestCryptoKeyPair(t *testing.T) {
66+
testscript.Run(t, testscript.Params{
67+
Files: []string{"testdata/crypto/keypair.txtar"},
68+
Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){
69+
"check_key_pair": checkKeyPair,
70+
},
71+
})
72+
}
73+
5874
func TestCryptoHelp(t *testing.T) {
5975
testscript.Run(t, testscript.Params{
6076
Files: []string{"testdata/crypto/help.txtar"},
6177
})
6278
}
79+
80+
func keyLength(jwk *jose.JSONWebKey) (int, error) {
81+
switch key := jwk.Key.(type) {
82+
case []byte:
83+
return len(key) * 8, nil
84+
case *rsa.PrivateKey:
85+
return key.N.BitLen(), nil
86+
case *rsa.PublicKey:
87+
return key.N.BitLen(), nil
88+
case *ecdsa.PrivateKey:
89+
return key.Params().BitSize, nil
90+
case *ecdsa.PublicKey:
91+
return key.Params().BitSize, nil
92+
default:
93+
return 0, fmt.Errorf("unsupported key type: %T", key)
94+
}
95+
}
96+
97+
func keyCurve(jwk *jose.JSONWebKey) (elliptic.Curve, error) {
98+
switch key := jwk.Key.(type) {
99+
case *ecdsa.PrivateKey:
100+
return key.Curve, nil
101+
case *ecdsa.PublicKey:
102+
return key.Curve, nil
103+
default:
104+
return nil, fmt.Errorf("unsupported key type: %T", key)
105+
}
106+
}
107+
108+
// checkKeyPair checks that the public/private key pair is valid. It performs
109+
// the following checks:
110+
//
111+
// - Read and parse the JWK public key, validating it's a valid public key
112+
// - Read and parse the JWK private key, validating it's a valid private key
113+
// - Compare the public and private key SHA-1 thumbprints to verify they match
114+
// - The type of the key that was created
115+
// - For RSA keys, the key size is the expected size
116+
// - For EC keys, the key curve is the expected curve
117+
func checkKeyPair(ts *testscript.TestScript, neg bool, args []string) {
118+
if len(args) < 4 {
119+
ts.Fatalf("expected at least 4 arguments, got %d", len(args))
120+
}
121+
122+
pub, err := jose.ParseKey([]byte(ts.ReadFile(args[0])))
123+
ts.Check(err)
124+
priv, err := jose.ParseKey([]byte(ts.ReadFile(args[1])), jose.WithPassword([]byte("password")))
125+
ts.Check(err)
126+
127+
pubHash, err := pub.Thumbprint(crypto.SHA1)
128+
ts.Check(err)
129+
privHash, err := priv.Thumbprint(crypto.SHA1)
130+
ts.Check(err)
131+
132+
if !bytes.Equal(pubHash, privHash) {
133+
ts.Fatalf("%s and %s have different thumbprints", args[0], args[1])
134+
}
135+
136+
expectRSA := false
137+
if s := strings.ToUpper(args[2]); s == "RSA" {
138+
expectRSA = true
139+
}
140+
141+
if expectRSA {
142+
if !strings.HasPrefix(pub.Algorithm, "RS") {
143+
ts.Fatalf("expected RSA key type, got %q", pub.Algorithm)
144+
}
145+
146+
expectedLength, err := strconv.Atoi(args[3])
147+
ts.Check(err)
148+
149+
length, err := keyLength(pub)
150+
ts.Check(err)
151+
152+
if length != expectedLength {
153+
ts.Fatalf("key length mismatch: expected %d, got %d", expectedLength, length)
154+
}
155+
156+
return
157+
}
158+
159+
if !strings.HasPrefix(pub.Algorithm, "ES") {
160+
ts.Fatalf("expected EC key type, got %q", pub.Algorithm)
161+
}
162+
163+
kc, err := keyCurve(pub)
164+
ts.Check(err)
165+
166+
switch crv := strings.ToUpper(args[3]); crv {
167+
case "P-256":
168+
if kc != elliptic.P256() {
169+
ts.Fatalf("expected P-256 curve, got %q", kc)
170+
}
171+
case "P-384":
172+
if kc != elliptic.P384() {
173+
ts.Fatalf("expected P-384 curve, got %q", kc)
174+
}
175+
case "P-521":
176+
if kc != elliptic.P521() {
177+
ts.Fatalf("expected P-521 curve, got %q", kc)
178+
}
179+
default:
180+
ts.Fatalf("unknown curve %q", crv)
181+
}
182+
}

0 commit comments

Comments
 (0)