Skip to content

Commit 6b76a34

Browse files
authored
Merge pull request #7 from ovh/split
Split binary into lib & cmd
2 parents 61a3f02 + 472b4dd commit 6b76a34

13 files changed

+141
-112
lines changed

.github/workflows/release.yml

+6-10
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,18 @@ jobs:
1212
goreleaser:
1313
runs-on: ubuntu-latest
1414
steps:
15-
-
16-
name: Checkout
15+
- name: Checkout
1716
uses: actions/checkout@v4
1817
with:
1918
fetch-depth: 0
20-
-
21-
name: Set up Go
22-
uses: actions/setup-go@v4
19+
- name: Setup Go
20+
uses: actions/setup-go@v5
2321
with:
24-
go-version: 1.21
25-
-
26-
name: Run GoReleaser
27-
uses: goreleaser/goreleaser-action@v5
22+
go-version: '1.23.x'
23+
- name: Run GoReleaser
24+
uses: goreleaser/goreleaser-action@v6
2825
with:
2926
distribution: goreleaser
3027
version: latest
31-
args: release --rm-dist
3228
env:
3329
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/tests.yml

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: Go
2+
on: [push]
3+
4+
jobs:
5+
build:
6+
runs-on: ubuntu-latest
7+
8+
steps:
9+
- uses: actions/checkout@v4
10+
- name: Setup Go
11+
uses: actions/setup-go@v5
12+
with:
13+
go-version: '1.23.x'
14+
- name: Build
15+
run: go build -v ./cmd/yubico-piv-checker
16+
- name: Test
17+
run: go test -v ./...

.goreleaser.yml

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
before:
2-
hooks:
3-
- go mod download
1+
version: 2
42
builds:
53
- env:
64
- CGO_ENABLED=0
@@ -23,10 +21,9 @@ builds:
2321
- "7"
2422
gomips:
2523
- hardfloat
24+
main: ./cmd/yubico-piv-checker
2625
checksum:
2726
name_template: 'checksums.txt'
28-
snapshot:
29-
name_template: "{{ .Tag }}-next"
3027
changelog:
3128
sort: asc
3229
nfpms:

cmd/yubico-piv-checker/main.go

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"log"
7+
"os"
8+
9+
"github.com/ovh/yubico-piv-checker/lib/checker"
10+
)
11+
12+
func init() {
13+
log.SetOutput(os.Stderr)
14+
}
15+
16+
func main() {
17+
if len(os.Args) != 4 {
18+
fmt.Fprintf(os.Stderr, "Usage: %s ssh-key attestation key-certificate\n", os.Args[0])
19+
os.Exit(-1)
20+
}
21+
22+
r, err := checker.VerifySSHKey(os.Args[1], os.Args[2], os.Args[3])
23+
if err != nil {
24+
log.Fatal(err)
25+
}
26+
27+
err = json.NewEncoder(os.Stdout).Encode(r)
28+
if err != nil {
29+
log.Fatal(err)
30+
}
31+
}
File renamed without changes.

go.mod

+4-7
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
module github.com/ovh/yubico-piv-checker
22

3-
go 1.21
3+
go 1.23.0
44

55
require (
6-
github.com/pkg/errors v0.9.1
7-
github.com/stretchr/testify v1.6.1
8-
golang.org/x/crypto v0.18.0
6+
github.com/maxatome/go-testdeep v1.14.0
7+
golang.org/x/crypto v0.36.0
98
)
109

1110
require (
1211
github.com/davecgh/go-spew v1.1.1 // indirect
13-
github.com/pmezard/go-difflib v1.0.0 // indirect
14-
golang.org/x/sys v0.16.0 // indirect
15-
gopkg.in/yaml.v3 v3.0.1 // indirect
12+
golang.org/x/sys v0.31.0 // indirect
1613
)

go.sum

+8-19
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,10 @@
1-
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
21
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
32
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4-
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
5-
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
6-
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
7-
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
8-
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
9-
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
10-
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
11-
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
12-
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
13-
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
14-
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
15-
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
16-
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
17-
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
18-
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
19-
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
20-
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
21-
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
3+
github.com/maxatome/go-testdeep v1.14.0 h1:rRlLv1+kI8eOI3OaBXZwb3O7xY3exRzdW5QyX48g9wI=
4+
github.com/maxatome/go-testdeep v1.14.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM=
5+
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
6+
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
7+
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
8+
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
9+
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
10+
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=

main.go lib/checker/checker.go

+18-47
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,60 @@
1-
package main
1+
package checker
22

33
import (
44
"bytes"
55
"crypto/x509"
66
"encoding/asn1"
7-
"encoding/json"
87
"encoding/pem"
98
"fmt"
10-
"log"
11-
"os"
129

13-
"golang.org/x/crypto/ssh"
10+
"github.com/ovh/yubico-piv-checker/lib/types"
1411

15-
"github.com/pkg/errors"
12+
"golang.org/x/crypto/ssh"
1613
)
1714

18-
func init() {
19-
log.SetOutput(os.Stderr)
20-
}
21-
22-
func ParseCertificate(cert string) (*x509.Certificate, error) {
15+
func parseCertificate(cert string) (*x509.Certificate, error) {
2316
block, _ := pem.Decode([]byte(cert))
2417
if block == nil || block.Type != "CERTIFICATE" {
25-
return nil, errors.New("Invalid PEM type")
18+
return nil, fmt.Errorf("invalid PEM type")
2619
}
2720

2821
return x509.ParseCertificate(block.Bytes)
2922
}
3023

31-
func VerifySSHKey(sshKey string, attestation string, keyCertificate string) (*Result, error) {
24+
func VerifySSHKey(sshKey string, attestation string, keyCertificate string) (*types.Result, error) {
3225
sshPubKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(sshKey))
3326
if err != nil {
34-
return nil, errors.Wrapf(err, "Failed to parse SSH Key %q", sshKey)
27+
return nil, fmt.Errorf("failed to parse SSH Key %q: %w", sshKey, err)
3528
}
3629

3730
// Parse attestation and check associated public key
38-
att, err := ParseCertificate(attestation)
31+
att, err := parseCertificate(attestation)
3932
if err != nil {
40-
return nil, errors.Wrap(err, "Failed to parse attestation")
33+
return nil, fmt.Errorf("failed to parse attestation: %w", err)
4134
}
4235
attPubKey, err := ssh.NewPublicKey(att.PublicKey)
4336
if err != nil {
44-
return nil, errors.Wrap(err, "Failed to compute SSH Key from attestation")
37+
return nil, fmt.Errorf("failed to compute SSH Key from attestation: %w", err)
4538
}
4639
if !bytes.Equal(sshPubKey.Marshal(), attPubKey.Marshal()) {
47-
return nil, errors.New("SSH Key doesn't match attestation")
40+
return nil, fmt.Errorf("SSH Key doesn't match attestation")
4841
}
4942

5043
// Parse key certificate and verify attestation signature
51-
keyCert, err := ParseCertificate(keyCertificate)
44+
keyCert, err := parseCertificate(keyCertificate)
5245
if err != nil {
53-
return nil, errors.Wrap(err, "Failed to parse Key Certificate")
46+
return nil, fmt.Errorf("failed to parse Key Certificate: %w", err)
5447
}
5548
err = keyCert.CheckSignature(att.SignatureAlgorithm, att.RawTBSCertificate, att.Signature)
5649
if err != nil {
57-
return nil, errors.Wrap(err, "Invalid attestation signature")
58-
}
59-
60-
// Parse YubicoCA and verify keyCertificate signature
61-
yubiCert, err := ParseCertificate(yubicoCertificate)
62-
if err != nil {
63-
return nil, errors.Wrap(err, "Failed to parse Yubico Certificate")
50+
return nil, fmt.Errorf("invalid attestation signature: %w", err)
6451
}
6552
err = yubiCert.CheckSignature(keyCert.SignatureAlgorithm, keyCert.RawTBSCertificate, keyCert.Signature)
6653
if err != nil {
67-
return nil, errors.Wrap(err, "Invalid Key Certificate signature")
54+
return nil, fmt.Errorf("invalid Key Certificate signature: %w", err)
6855
}
6956

70-
var r Result
57+
var r types.Result
7158
r.SSHKey.FingerprintMD5 = ssh.FingerprintLegacyMD5(sshPubKey)
7259
r.SSHKey.FingerprintSHA = ssh.FingerprintSHA256(sshPubKey)
7360

@@ -81,26 +68,10 @@ func VerifySSHKey(sshKey string, attestation string, keyCertificate string) (*Re
8168
} else if e.Id.Equal(oidExtensionYubikeyFirmware) && len(e.Value) == 3 {
8269
r.Yubikey.FirmwareVersion = fmt.Sprintf("%d.%d.%d", e.Value[0], e.Value[1], e.Value[2])
8370
} else if e.Id.Equal(oidExtensionYubikeyPolicy) && len(e.Value) == 2 {
84-
r.Yubikey.PinPolicy = YubicoPinPolicy(e.Value[0])
85-
r.Yubikey.TouchPolicy = YubicoTouchPolicy(e.Value[1])
71+
r.Yubikey.PinPolicy = types.YubicoPinPolicy(e.Value[0])
72+
r.Yubikey.TouchPolicy = types.YubicoTouchPolicy(e.Value[1])
8673
}
8774
}
8875

8976
return &r, nil
9077
}
91-
92-
func main() {
93-
if len(os.Args) != 4 {
94-
fmt.Fprintf(os.Stderr, "Usage: %s ssh-key attestation key-certificate\n", os.Args[0])
95-
os.Exit(-1)
96-
}
97-
98-
r, err := VerifySSHKey(os.Args[1], os.Args[2], os.Args[3])
99-
if err != nil {
100-
log.Fatal(err)
101-
}
102-
err = json.NewEncoder(os.Stdout).Encode(r)
103-
if err != nil {
104-
log.Fatal(err)
105-
}
106-
}

main_test.go lib/checker/checker_test.go

+20-10
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
package main
1+
package checker_test
22

33
import (
44
"testing"
55

6-
"github.com/stretchr/testify/assert"
6+
"github.com/maxatome/go-testdeep/td"
7+
"github.com/ovh/yubico-piv-checker/lib/checker"
8+
"github.com/ovh/yubico-piv-checker/lib/types"
79
)
810

911
var sshKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQClBrnz47s5ER1vnhBSaKIYddvDBty9LFoLOJ3/EmJahzMex80vZA61QO+vRAjM64gwDHgtmoSjiwCAq20J7EZgqJDOuxgX5zLG7rA6xxooQEvVMkmKlHkIeCnBlOwhtr5YjQ4hk0DboLK+955c7kiqW7dJkzHVnzyYG0ILQiSlrY+cCEa/UceGv74fgMQe71B8UC32N27IxN/gssqgHSvgMiQ8nMNQJW2h0mIT3/pKceu+gt4qscZCYYq9Qoz6tPIDZA7KaBZb0Y7kSAenEwjsTQvy5/iE8ELPRBZtmHdW/R78bljX/UZ5sEN5lw9MRHz2zFhFPxdcfpnnQopFH0QJ`
@@ -48,13 +50,21 @@ LL62+racaCSKom8Ty1yBgNiZmcho8+buAfU=
4850
-----END CERTIFICATE-----`
4951

5052
func TestCheck(t *testing.T) {
51-
result, err := VerifySSHKey(sshKey, attestation, keyCertificate)
52-
assert.NoError(t, err)
53+
result, err := checker.VerifySSHKey(sshKey, attestation, keyCertificate)
54+
td.Require(t).CmpNoError(err)
5355

54-
assert.Equal(t, "46:00:b0:eb:d1:fd:b7:86:ea:da:09:7a:49:dd:e3:56", result.SSHKey.FingerprintMD5)
55-
assert.Equal(t, "SHA256:V0yDye/t5QVSC6nAnQ8MsgYUS/bZZKQmB0cYk6+UgqI", result.SSHKey.FingerprintSHA)
56-
assert.Equal(t, 5970478, result.Yubikey.SerialNumber)
57-
assert.Equal(t, "4.3.5", result.Yubikey.FirmwareVersion)
58-
assert.Equal(t, PinPolicyAlways, result.Yubikey.PinPolicy)
59-
assert.Equal(t, TouchPolicyNever, result.Yubikey.TouchPolicy)
56+
td.Cmp(t, result, td.Struct(
57+
&types.Result{
58+
SSHKey: types.SSHKey{
59+
FingerprintMD5: "46:00:b0:eb:d1:fd:b7:86:ea:da:09:7a:49:dd:e3:56",
60+
FingerprintSHA: "SHA256:V0yDye/t5QVSC6nAnQ8MsgYUS/bZZKQmB0cYk6+UgqI",
61+
},
62+
Yubikey: types.Yubikey{
63+
SerialNumber: 5970478,
64+
FirmwareVersion: "4.3.5",
65+
PinPolicy: types.PinPolicyAlways,
66+
TouchPolicy: types.TouchPolicyNever,
67+
},
68+
},
69+
))
6070
}

consts.go lib/checker/consts.go

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
package main
1+
package checker
2+
3+
import (
4+
"crypto/x509"
5+
"fmt"
6+
)
27

38
// See https://developers.yubico.com/PIV/Introduction/PIV_attestation.html
49
var oidExtensionYubikeyFirmware = []int{1, 3, 6, 1, 4, 1, 41482, 3, 3}
@@ -25,3 +30,15 @@ bW5yWvyS9zNXaqGaUmP3U9/b6DlHdDogMLu3VLpBB9bm5bjaKWWJYgWltCVgUbFq
2530
Fqyi4+JE014cSgR57Jcu3dZiehB6UtAPgad9L5cNvua/IWRmm+ANy3O2LH++Pyl8
2631
SREzU8onbBsjMg9QDiSf5oJLKvd/Ren+zGY7
2732
-----END CERTIFICATE-----`
33+
34+
var yubiCert *x509.Certificate
35+
36+
func init() {
37+
// Parse YubicoCA and verify keyCertificate signature
38+
cert, err := parseCertificate(yubicoCertificate)
39+
if err != nil {
40+
panic(fmt.Errorf("failed to parse Yubico Certificate: %w", err))
41+
}
42+
43+
yubiCert = cert
44+
}

types.go lib/types/types.go

+15-11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package main
1+
package types
22

33
//go:generate enumer -type YubicoPinPolicy -json -text -trimprefix PinPolicy
44
//go:generate enumer -type YubicoTouchPolicy -json -text -trimprefix TouchPolicy
@@ -19,15 +19,19 @@ const (
1919
TouchPolicyCached15s
2020
)
2121

22+
type SSHKey struct {
23+
FingerprintMD5 string
24+
FingerprintSHA string
25+
}
26+
27+
type Yubikey struct {
28+
SerialNumber int
29+
FirmwareVersion string
30+
PinPolicy YubicoPinPolicy
31+
TouchPolicy YubicoTouchPolicy
32+
}
33+
2234
type Result struct {
23-
SSHKey struct {
24-
FingerprintMD5 string
25-
FingerprintSHA string
26-
}
27-
Yubikey struct {
28-
SerialNumber int
29-
FirmwareVersion string
30-
PinPolicy YubicoPinPolicy
31-
TouchPolicy YubicoTouchPolicy
32-
}
35+
SSHKey SSHKey
36+
Yubikey Yubikey
3337
}

yubicopinpolicy_enumer.go lib/types/yubicopinpolicy_enumer.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

yubicotouchpolicy_enumer.go lib/types/yubicotouchpolicy_enumer.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)