New Upstream Release - golang-github-go-piv-piv-go
Ready changes
Summary
Merged new upstream version: 1.10.0 (was: 1.8.0).
Resulting package
Built on 2022-10-20T04:51 (took 1m57s)
The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:
apt install -t fresh-releases golang-github-go-piv-piv-go-dev
Lintian Result
Diff
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 39d1882..1790b87 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -1,40 +1,49 @@
name: test
on:
+ push:
+ branches:
+ - master
pull_request:
branches:
- master
jobs:
build:
+ strategy:
+ matrix:
+ go-version: [1.18.x, 1.19.x]
name: Linux
runs-on: ubuntu-latest
steps:
- - name: Set up Go 1.14
+ - name: Set up Go
uses: actions/setup-go@v2
with:
- go-version: '^1.14.2'
+ go-version: ${{ matrix.go-version }}
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- - name: Install golint
- run: go get -u golang.org/x/lint/golint
+ - name: Install staticcheck
+ run: go install honnef.co/go/tools/cmd/staticcheck@v0.3.3
- name: Install libpcsc
run: sudo apt-get install -y libpcsclite-dev pcscd pcsc-tools
- name: Test
run: "make test"
build-windows:
+ strategy:
+ matrix:
+ go-version: [1.18.x, 1.19.x]
name: Windows
runs-on: windows-latest
steps:
- - name: Set up Go 1.14
+ - name: Set up Go
uses: actions/setup-go@v2
with:
- go-version: '^1.14.2'
+ go-version: ${{ matrix.go-version }}
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- - name: Install golint
- run: go get -u golang.org/x/lint/golint
+ - name: Install staticcheck
+ run: go install honnef.co/go/tools/cmd/staticcheck@v0.3.3
- name: Test
run: "make build"
env:
diff --git a/Makefile b/Makefile
index 15275a8..698d316 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ test: lint
.PHONY: lint
lint:
- golint -set_exit_status ./...
+ staticcheck ./...
.PHONY: build
build: lint
diff --git a/README.md b/README.md
index 5247022..059ce19 100644
--- a/README.md
+++ b/README.md
@@ -121,7 +121,7 @@ if err := yk.SetMetadata(newKey, m); err != nil {
fmt.Println("Credentials set. Your PIN is: %s", newPIN)
```
-The user can user the PIN later to fetch the management key:
+The user can use the PIN later to fetch the management key:
```go
m, err := yk.Metadata(pin)
diff --git a/debian/changelog b/debian/changelog
index ef17b80..a93799c 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+golang-github-go-piv-piv-go (1.10.0-1) UNRELEASED; urgency=low
+
+ * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk> Thu, 20 Oct 2022 04:49:31 -0000
+
golang-github-go-piv-piv-go (1.8.0-3) unstable; urgency=medium
[ Debian Janitor ]
diff --git a/go.mod b/go.mod
index b359a22..7306c19 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,3 @@
module github.com/go-piv/piv-go
-go 1.13
+go 1.16
diff --git a/piv/key.go b/piv/key.go
index a9f742e..d3ec1a5 100644
--- a/piv/key.go
+++ b/piv/key.go
@@ -76,16 +76,47 @@ type Version struct {
}
// Formfactor enumerates the physical set of forms a key can take. USB-A vs.
-// USB-C and Keychain vs. Nano.
+// USB-C and Keychain vs. Nano (and FIPS variants for these).
type Formfactor int
-// Formfactors recognized by this package.
+// The mapping between known Formfactor values and their descriptions.
+var formFactorStrings = map[Formfactor]string{
+ FormfactorUSBAKeychain: "USB-A Keychain",
+ FormfactorUSBANano: "USB-A Nano",
+ FormfactorUSBCKeychain: "USB-C Keychain",
+ FormfactorUSBCNano: "USB-C Nano",
+ FormfactorUSBCLightningKeychain: "USB-C/Lightning Keychain",
+
+ FormfactorUSBAKeychainFIPS: "USB-A Keychain FIPS",
+ FormfactorUSBANanoFIPS: "USB-A Nano FIPS",
+ FormfactorUSBCKeychainFIPS: "USB-C Keychain FIPS",
+ FormfactorUSBCNanoFIPS: "USB-C Nano FIPS",
+ FormfactorUSBCLightningKeychainFIPS: "USB-C/Lightning Keychain FIPS",
+}
+
+// String returns the human-readable description for the given form-factor
+// value, or a fallback value for any other, unknown form-factor.
+func (f Formfactor) String() string {
+ if s, ok := formFactorStrings[f]; ok {
+ return s
+ }
+ return fmt.Sprintf("unknown(0x%02x)", int(f))
+}
+
+// Formfactors recognized by this package. See the reference for more information:
+// https://developers.yubico.com/yubikey-manager/Config_Reference.html#_form_factor
const (
- FormfactorUSBAKeychain = iota + 1
- FormfactorUSBANano
- FormfactorUSBCKeychain
- FormfactorUSBCNano
- FormfactorUSBCLightningKeychain
+ FormfactorUSBAKeychain = 0x1
+ FormfactorUSBANano = 0x2
+ FormfactorUSBCKeychain = 0x3
+ FormfactorUSBCNano = 0x4
+ FormfactorUSBCLightningKeychain = 0x5
+
+ FormfactorUSBAKeychainFIPS = 0x81
+ FormfactorUSBANanoFIPS = 0x82
+ FormfactorUSBCKeychainFIPS = 0x83
+ FormfactorUSBCNanoFIPS = 0x84
+ FormfactorUSBCLightningKeychainFIPS = 0x85
)
// Prefix in the x509 Subject Common Name for YubiKey attestations
@@ -163,28 +194,11 @@ func (a *Attestation) addExt(e pkix.Extension) error {
if len(e.Value) != 1 {
return fmt.Errorf("expected 1 byte from formfactor, got: %d", len(e.Value))
}
- switch e.Value[0] {
- case 0x01:
- a.Formfactor = FormfactorUSBAKeychain
- case 0x02:
- a.Formfactor = FormfactorUSBANano
- case 0x03:
- a.Formfactor = FormfactorUSBCKeychain
- case 0x04:
- a.Formfactor = FormfactorUSBCNano
- case 0x05:
- a.Formfactor = FormfactorUSBCLightningKeychain
- default:
- return fmt.Errorf("unrecognized formfactor: 0x%x", e.Value[0])
- }
+ a.Formfactor = Formfactor(e.Value[0])
}
return nil
}
-func verifySignature(parent, c *x509.Certificate) error {
- return parent.CheckSignature(c.SignatureAlgorithm, c.RawTBSCertificate, c.Signature)
-}
-
// Verify proves that a key was generated on a YubiKey. It ensures the slot and
// YubiKey certificate chains up to the Yubico CA, parsing additional information
// out of the slot certificate, such as the touch and PIN policies of a key.
@@ -194,23 +208,36 @@ func Verify(attestationCert, slotCert *x509.Certificate) (*Attestation, error) {
}
type verifier struct {
- Root *x509.Certificate
+ Roots *x509.CertPool
}
func (v *verifier) Verify(attestationCert, slotCert *x509.Certificate) (*Attestation, error) {
- root := v.Root
- if root == nil {
- ca, err := yubicoCA()
+ o := x509.VerifyOptions{KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}}
+ o.Roots = v.Roots
+ if o.Roots == nil {
+ cas, err := yubicoCAs()
if err != nil {
- return nil, fmt.Errorf("parsing yubico ca: %v", err)
+ return nil, fmt.Errorf("failed to load yubico CAs: %v", err)
}
- root = ca
+ o.Roots = cas
}
- if err := verifySignature(root, attestationCert); err != nil {
- return nil, fmt.Errorf("attestation certifcate not signed by : %v", err)
+
+ o.Intermediates = x509.NewCertPool()
+
+ // The attestation cert in some yubikey 4 does not encode X509v3 Basic Constraints.
+ // This isn't valid as per https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.9
+ // (fourth paragraph) and thus makes x509.go validation fail.
+ // Work around this by setting this constraint here.
+ if !attestationCert.BasicConstraintsValid {
+ attestationCert.BasicConstraintsValid = true
+ attestationCert.IsCA = true
}
- if err := verifySignature(attestationCert, slotCert); err != nil {
- return nil, fmt.Errorf("slot certificate not signed by attestation certifcate: %v", err)
+
+ o.Intermediates.AddCert(attestationCert)
+
+ _, err := slotCert.Verify(o)
+ if err != nil {
+ return nil, fmt.Errorf("error verifying attestation certificate: %v", err)
}
return parseAttestation(slotCert)
}
@@ -256,10 +283,10 @@ func parseSlot(commonName string) (Slot, bool) {
return RetiredKeyManagementSlot(uint32(key))
}
-// yubicoPIVCAPEM is the PEM encoded attestation certificate used by Yubico.
+// yubicoPIVCAPEMAfter2018 is the PEM encoded attestation certificate used by Yubico.
//
// https://developers.yubico.com/PIV/Introduction/PIV_attestation.html
-const yubicoPIVCAPEM = `-----BEGIN CERTIFICATE-----
+const yubicoPIVCAPEMAfter2018 = `-----BEGIN CERTIFICATE-----
MIIDFzCCAf+gAwIBAgIDBAZHMA0GCSqGSIb3DQEBCwUAMCsxKTAnBgNVBAMMIFl1
YmljbyBQSVYgUm9vdCBDQSBTZXJpYWwgMjYzNzUxMCAXDTE2MDMxNDAwMDAwMFoY
DzIwNTIwNDE3MDAwMDAwWjArMSkwJwYDVQQDDCBZdWJpY28gUElWIFJvb3QgQ0Eg
@@ -279,12 +306,58 @@ Fqyi4+JE014cSgR57Jcu3dZiehB6UtAPgad9L5cNvua/IWRmm+ANy3O2LH++Pyl8
SREzU8onbBsjMg9QDiSf5oJLKvd/Ren+zGY7
-----END CERTIFICATE-----`
-func yubicoCA() (*x509.Certificate, error) {
- b, _ := pem.Decode([]byte(yubicoPIVCAPEM))
- if b == nil {
+// Yubikeys manufactured sometime in 2018 and prior to mid-2017
+// were certified using the U2F root CA with serial number 457200631
+// See https://github.com/Yubico/developers.yubico.com/pull/392/commits/a58f1003f003e04fc9baf09cad9f64f0c284fd47
+// Cert available at https://developers.yubico.com/U2F/yubico-u2f-ca-certs.txt
+const yubicoPIVCAPEMU2F = `-----BEGIN CERTIFICATE-----
+MIIDHjCCAgagAwIBAgIEG0BT9zANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZ
+dWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAw
+MDBaGA8yMDUwMDkwNDAwMDAwMFowLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290
+IENBIFNlcmlhbCA0NTcyMDA2MzEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC/jwYuhBVlqaiYWEMsrWFisgJ+PtM91eSrpI4TK7U53mwCIawSDHy8vUmk
+5N2KAj9abvT9NP5SMS1hQi3usxoYGonXQgfO6ZXyUA9a+KAkqdFnBnlyugSeCOep
+8EdZFfsaRFtMjkwz5Gcz2Py4vIYvCdMHPtwaz0bVuzneueIEz6TnQjE63Rdt2zbw
+nebwTG5ZybeWSwbzy+BJ34ZHcUhPAY89yJQXuE0IzMZFcEBbPNRbWECRKgjq//qT
+9nmDOFVlSRCt2wiqPSzluwn+v+suQEBsUjTGMEd25tKXXTkNW21wIWbxeSyUoTXw
+LvGS6xlwQSgNpk2qXYwf8iXg7VWZAgMBAAGjQjBAMB0GA1UdDgQWBBQgIvz0bNGJ
+hjgpToksyKpP9xv9oDAPBgNVHRMECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAN
+BgkqhkiG9w0BAQsFAAOCAQEAjvjuOMDSa+JXFCLyBKsycXtBVZsJ4Ue3LbaEsPY4
+MYN/hIQ5ZM5p7EjfcnMG4CtYkNsfNHc0AhBLdq45rnT87q/6O3vUEtNMafbhU6kt
+hX7Y+9XFN9NpmYxr+ekVY5xOxi8h9JDIgoMP4VB1uS0aunL1IGqrNooL9mmFnL2k
+LVVee6/VR6C5+KSTCMCWppMuJIZII2v9o4dkoZ8Y7QRjQlLfYzd3qGtKbw7xaF1U
+sG/5xUb/Btwb2X2g4InpiB/yt/3CpQXpiWX/K4mBvUKiGn05ZsqeY1gx4g0xLBqc
+U9psmyPzK+Vsgw2jeRQ5JlKDyqE0hebfC1tvFu0CCrJFcw==
+-----END CERTIFICATE-----`
+
+func yubicoCAs() (*x509.CertPool, error) {
+ certPool := x509.NewCertPool()
+
+ if !certPool.AppendCertsFromPEM([]byte(yubicoPIVCAPEMAfter2018)) {
+ return nil, fmt.Errorf("failed to parse yubico cert")
+ }
+
+ bU2F, _ := pem.Decode([]byte(yubicoPIVCAPEMU2F))
+ if bU2F == nil {
return nil, fmt.Errorf("failed to decode yubico pem data")
}
- return x509.ParseCertificate(b.Bytes)
+
+ certU2F, err := x509.ParseCertificate(bU2F.Bytes)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse yubico cert: %v", err)
+ }
+
+ // The U2F root cert has pathlen x509 basic constraint set to 0.
+ // As per RFC 5280 this means that no intermediate cert is allowed
+ // in the validation path. This isn't really helpful since we do
+ // want to use the device attestation cert as intermediate cert in
+ // the chain. To make this work, set pathlen of the U2F root to 1.
+ //
+ // See https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.9
+ certU2F.MaxPathLen = 1
+ certPool.AddCert(certU2F)
+
+ return certPool, nil
}
// Slot combinations pre-defined by this package.
@@ -670,14 +743,6 @@ type KeyAuth struct {
PINPolicy PINPolicy
}
-func isAuthErr(err error) bool {
- var e *apduErr
- if !errors.As(err, &e) {
- return false
- }
- return e.sw1 == 0x69 && e.sw2 == 0x82 // "security status not satisfied"
-}
-
func (k KeyAuth) authTx(yk *YubiKey, pp PINPolicy) error {
// PINPolicyNever shouldn't require a PIN.
if pp == PINPolicyNever {
diff --git a/piv/key_test.go b/piv/key_test.go
index 1133682..46b9b8a 100644
--- a/piv/key_test.go
+++ b/piv/key_test.go
@@ -25,6 +25,7 @@ import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
+ "encoding/pem"
"errors"
"math/big"
"testing"
@@ -766,7 +767,7 @@ func TestSetRSAPrivateKey(t *testing.T) {
t.Fatalf("decrypting data: %v", err)
}
- if bytes.Compare(data, decrypted) != 0 {
+ if !bytes.Equal(data, decrypted) {
t.Fatalf("decrypted data is different to the source data")
}
})
@@ -896,3 +897,68 @@ func TestParseSlot(t *testing.T) {
})
}
}
+
+func TestVerify(t *testing.T) {
+ tests := []struct {
+ name string
+ deviceCert string
+ keyCert string
+ ok bool
+ }{
+ {
+ // Valid attestation chain from a recent YubiKey.
+ name: "ValidChain",
+ deviceCert: "-----BEGIN CERTIFICATE-----\nMIIC+jCCAeKgAwIBAgIJAKs/UIpBjg1uMA0GCSqGSIb3DQEBCwUAMCsxKTAnBgNV\nBAMMIFl1YmljbyBQSVYgUm9vdCBDQSBTZXJpYWwgMjYzNzUxMCAXDTE2MDMxNDAw\nMDAwMFoYDzIwNTIwNDE3MDAwMDAwWjAhMR8wHQYDVQQDDBZZdWJpY28gUElWIEF0\ndGVzdGF0aW9uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0zdJWGnk\naLE8Rb+TP7iSffhJV9SJEp2Me4QcfVidgHqyIdo0lruBk69RF1nrmS3i+G1yyUh/\nymAPZkcQCpms0E23Dmhue1VRpBedcsVtO/xSrfu0qAWTslp/k57ry6vkidrQU1cx\nl2KodH3KTmnZmaskQD8eGtxXwcmLOmhKem6GSqhN/3QznaDhZmVUAvUKSOaIzOxn\n2u1mDHhGwaHhR7dklsDwN7oni4WWX1GJXtzpB8j6JhoqyqXwSbq+ck54PfzUoOFd\n/2yKyFRDXnQvzbNL7+afbxBQQMxxo1e24DNE/cp+K09eT7Gh1Urao6meaSssN4aV\nFfmkhC2NapGKMQIDAQABoykwJzARBgorBgEEAYLECgMDBAMFBAMwEgYDVR0TAQH/\nBAgwBgEB/wIBADANBgkqhkiG9w0BAQsFAAOCAQEAJfOLOQYGyIMQ5y+sDkYz+e6G\nH8BqqiYL9VOC3U3KQX9mrtZnaIexqJOCQyCFOSvaTFJvOfNiCCKQuLbmS+Qn4znd\nnSitCsdJSFKskQP7hbXqUK01epb6iTuuko4w3V57YVudnniZBD2s4XoNcJ6BFizZ\n3iXQqRMaLVfFHS9Qx0iLZLcR2s29nIl6NI/qFdIgkyo07J5cPnBiD6wxQft8FdfR\nbgx9yrrjY0mvj/k5LRN6lab8lTolgI5luJtKNueq96LVkTkAzcCaJPQ9YQ4cxeU9\nOapsEeOk6xf5bRPtdf0WhEKthXywt9D0pSHhAI+fpLNe/VtlZpt3hn9aTbqSug==\n-----END CERTIFICATE-----\n",
+ keyCert: "-----BEGIN CERTIFICATE-----\nMIICVTCCAT2gAwIBAgIQAU4Yg7Qnw9FZgMBEaJ7ZMzANBgkqhkiG9w0BAQsFADAh\nMR8wHQYDVQQDDBZZdWJpY28gUElWIEF0dGVzdGF0aW9uMCAXDTE2MDMxNDAwMDAw\nMFoYDzIwNTIwNDE3MDAwMDAwWjAlMSMwIQYDVQQDDBpZdWJpS2V5IFBJViBBdHRl\nc3RhdGlvbiA5YTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABATzM3sJuwemL2Ha\nHkGIzmCVjUMreNIVrRLOvnbZjoVflk1eab/iLUlKzk/2jXTu9TISRg2dhyXcutct\nvnqr66yjTjBMMBEGCisGAQQBgsQKAwMEAwUEAzAUBgorBgEEAYLECgMHBAYCBADw\nDxQwEAYKKwYBBAGCxAoDCAQCAgEwDwYKKwYBBAGCxAoDCQQBBDANBgkqhkiG9w0B\nAQsFAAOCAQEAFX0hL5gi/g4ZM7vCH5kDAtma7eBp0LpbCzR313GGyBR7pJFtuj2l\nbWU+V3SFRihXBTDb8q+uvyCBqgz1szdZzrpfjqNkhEPfPNabxjxJxVoe6Gdcn115\naduxfqqT2u+YIsERzaIIIisehLQkc/5zLkpocA6jbKBZnZWUBJIxuz4QmYTIf0O4\nHPE2o4JbAyGx/hRaqVvDgNeAz94ZFjb4Mp3RNbbdRUZB0ehrT/IGRJoHRu2HKFGM\nylRJL2kjKPoEc4XHbCu+MfmAIrQ4Xseg85zyI7ThhYvAzktdLHhQyfYr4wrrLCN3\noeTzmiqIHe9AataJXQ+mEQEEc9TNY23RFg==\n-----END CERTIFICATE-----\n",
+ ok: true,
+ },
+ {
+ // Valid attestation chain from a yubikey manufactured in 2018 showing a manufacture bug (device certified using U2F root, and device cert does not encode X509 basic constraints).
+ name: "ValidChain2018",
+ deviceCert: "-----BEGIN CERTIFICATE-----\nMIIC6TCCAdGgAwIBAgIJALvwZFDESwMlMA0GCSqGSIb3DQEBCwUAMC4xLDAqBgNV\nBAMTI1l1YmljbyBVMkYgUm9vdCBDQSBTZXJpYWwgNDU3MjAwNjMxMCAXDTE0MDgw\nMTAwMDAwMFoYDzIwNTAwOTA0MDAwMDAwWjAhMR8wHQYDVQQDDBZZdWJpY28gUElW\nIEF0dGVzdGF0aW9uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqXnZ\n+lxX0nNzy3jn+lrZ+1cHTVUNYVKPqGTjvRw/7XOEnInWC1VCPJqwHYtnnoH4EIXN\n7kDGXwInfs9pwyjpgQw/V23yywFtUhaR8Xgw8zqC/YfJpeK4PetJ9/k+xFbICuX7\nWDv/k5Wth3VZSaVjm/tunWajtt3OLOQQaMSoLqP41XAHHuCyzfCwJ2Vsa2FyCINF\nyG6XobokeICDRnH44POqudcLVIDvZLQqu2LF+mZd+OO5nqmTa68kkwRf/m93eOJP\no7GvYtQSp7CPJC7ks2gl8U7wuT9DQT5/0wqkoEyLZg/KLUlzgXjMa+7GtCLTC1Ku\nOh9vw02f4K44RW4nWwIDAQABoxUwEzARBgorBgEEAYLECgMDBAMEAwcwDQYJKoZI\nhvcNAQELBQADggEBAHD/uXqNgCYywj2ee7s7kix2TT4XN9OIn0fTNh5LEiUN+q7U\nzJc9q7b5WD7PfaG6UNyuaSnLaq+dLOCJ4bX4h+/MwQSndQg0epMra1ThVQZkMkGa\nktAJ5JT6j9qxNxD1RWMl91e4JwtGzFyDwFyyUGnSwhMsqMdwfBsmTpvgxmAD/NMs\nkWB/m91FV9D+UBqsZRoLoc44kEFYBZ09ypTsR699oJRsBfG0AqVYyK7rnG6663fF\nGUSWk7noVdUPXedlwXCqCymCsVheoss9qF1cffaFIl9RxGvVvCFybx0LGiYDxfgv\n80yGZIY/mAqZVDWyHZSs4f6kWK9GeLKU2Y9yby4=\n-----END CERTIFICATE-----\n",
+ keyCert: "-----BEGIN CERTIFICATE-----\nMIICLzCCARegAwIBAgIRAIxiihk4fSKK6keqJYujvnkwDQYJKoZIhvcNAQELBQAw\nITEfMB0GA1UEAwwWWXViaWNvIFBJViBBdHRlc3RhdGlvbjAgFw0xNDA4MDEwMDAw\nMDBaGA8yMDUwMDkwNDAwMDAwMFowJTEjMCEGA1UEAwwaWXViaUtleSBQSVYgQXR0\nZXN0YXRpb24gOWEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATHEzJsrhTHuvsx\n685AiWsAuT8Poe/zQfDRZNfpUSzJ31v6MZ9nz70pNrdd/sbG7O1UA6ceWhq1jHTU\n96Dnp99voycwJTARBgorBgEEAYLECgMDBAMEAwcwEAYKKwYBBAGCxAoDCAQCAgEw\nDQYJKoZIhvcNAQELBQADggEBADoswZ1LJ5GYVNgtRE0+zMQkAzam8YqeKmIDHtir\nvolIpGtJHzgCG2SdJlR/KnjRWF/1i8TRMhQ0O/KgkIEh+IyhJtD7DojgWvIBsCnX\nJXF7EPQMy17l7/9940QSOnQRIDb+z0eq9ACAjC3FWzqeR5VgN4C1QpCw7gKgqLTs\npmmDHHg4HsKl0PsPwim0bYIqEHttrLjPQiPnoa3qixzNKbwJjXb4/f/dvCTx9dRP\n0FVABj5Yh8f728xzrzw2nLZ9X/c0GoXfKu9s7lGNLcZ5OO+zys1ATei2h/PFJLDH\nAdrenw31WOYRtdjcNBKyAk80ajryjTAX3GXfbKpkdVB9hEo=\n-----END CERTIFICATE-----\n",
+ ok: true,
+ },
+ {
+ // Invalid attestation chain. Device cert from yubikey A, key cert from yubikey B.
+ name: "InvalidChain",
+ deviceCert: "-----BEGIN CERTIFICATE-----\nMIIC+jCCAeKgAwIBAgIJAKs/UIpBjg1uMA0GCSqGSIb3DQEBCwUAMCsxKTAnBgNV\nBAMMIFl1YmljbyBQSVYgUm9vdCBDQSBTZXJpYWwgMjYzNzUxMCAXDTE2MDMxNDAw\nMDAwMFoYDzIwNTIwNDE3MDAwMDAwWjAhMR8wHQYDVQQDDBZZdWJpY28gUElWIEF0\ndGVzdGF0aW9uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0zdJWGnk\naLE8Rb+TP7iSffhJV9SJEp2Me4QcfVidgHqyIdo0lruBk69RF1nrmS3i+G1yyUh/\nymAPZkcQCpms0E23Dmhue1VRpBedcsVtO/xSrfu0qAWTslp/k57ry6vkidrQU1cx\nl2KodH3KTmnZmaskQD8eGtxXwcmLOmhKem6GSqhN/3QznaDhZmVUAvUKSOaIzOxn\n2u1mDHhGwaHhR7dklsDwN7oni4WWX1GJXtzpB8j6JhoqyqXwSbq+ck54PfzUoOFd\n/2yKyFRDXnQvzbNL7+afbxBQQMxxo1e24DNE/cp+K09eT7Gh1Urao6meaSssN4aV\nFfmkhC2NapGKMQIDAQABoykwJzARBgorBgEEAYLECgMDBAMFBAMwEgYDVR0TAQH/\nBAgwBgEB/wIBADANBgkqhkiG9w0BAQsFAAOCAQEAJfOLOQYGyIMQ5y+sDkYz+e6G\nH8BqqiYL9VOC3U3KQX9mrtZnaIexqJOCQyCFOSvaTFJvOfNiCCKQuLbmS+Qn4znd\nnSitCsdJSFKskQP7hbXqUK01epb6iTuuko4w3V57YVudnniZBD2s4XoNcJ6BFizZ\n3iXQqRMaLVfFHS9Qx0iLZLcR2s29nIl6NI/qFdIgkyo07J5cPnBiD6wxQft8FdfR\nbgx9yrrjY0mvj/k5LRN6lab8lTolgI5luJtKNueq96LVkTkAzcCaJPQ9YQ4cxeU9\nOapsEeOk6xf5bRPtdf0WhEKthXywt9D0pSHhAI+fpLNe/VtlZpt3hn9aTbqSug==\n-----END CERTIFICATE-----\n",
+ keyCert: "-----BEGIN CERTIFICATE-----\nMIICLzCCARegAwIBAgIRAIxiihk4fSKK6keqJYujvnkwDQYJKoZIhvcNAQELBQAw\nITEfMB0GA1UEAwwWWXViaWNvIFBJViBBdHRlc3RhdGlvbjAgFw0xNDA4MDEwMDAw\nMDBaGA8yMDUwMDkwNDAwMDAwMFowJTEjMCEGA1UEAwwaWXViaUtleSBQSVYgQXR0\nZXN0YXRpb24gOWEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATHEzJsrhTHuvsx\n685AiWsAuT8Poe/zQfDRZNfpUSzJ31v6MZ9nz70pNrdd/sbG7O1UA6ceWhq1jHTU\n96Dnp99voycwJTARBgorBgEEAYLECgMDBAMEAwcwEAYKKwYBBAGCxAoDCAQCAgEw\nDQYJKoZIhvcNAQELBQADggEBADoswZ1LJ5GYVNgtRE0+zMQkAzam8YqeKmIDHtir\nvolIpGtJHzgCG2SdJlR/KnjRWF/1i8TRMhQ0O/KgkIEh+IyhJtD7DojgWvIBsCnX\nJXF7EPQMy17l7/9940QSOnQRIDb+z0eq9ACAjC3FWzqeR5VgN4C1QpCw7gKgqLTs\npmmDHHg4HsKl0PsPwim0bYIqEHttrLjPQiPnoa3qixzNKbwJjXb4/f/dvCTx9dRP\n0FVABj5Yh8f728xzrzw2nLZ9X/c0GoXfKu9s7lGNLcZ5OO+zys1ATei2h/PFJLDH\nAdrenw31WOYRtdjcNBKyAk80ajryjTAX3GXfbKpkdVB9hEo=\n-----END CERTIFICATE-----\n",
+ ok: false,
+ },
+ {
+ // Invalid attestation chain. Device cert from yubikey B, key cert from yubikey A.
+ name: "InvalidChain2",
+ deviceCert: "-----BEGIN CERTIFICATE-----\nMIIC6TCCAdGgAwIBAgIJALvwZFDESwMlMA0GCSqGSIb3DQEBCwUAMC4xLDAqBgNV\nBAMTI1l1YmljbyBVMkYgUm9vdCBDQSBTZXJpYWwgNDU3MjAwNjMxMCAXDTE0MDgw\nMTAwMDAwMFoYDzIwNTAwOTA0MDAwMDAwWjAhMR8wHQYDVQQDDBZZdWJpY28gUElW\nIEF0dGVzdGF0aW9uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqXnZ\n+lxX0nNzy3jn+lrZ+1cHTVUNYVKPqGTjvRw/7XOEnInWC1VCPJqwHYtnnoH4EIXN\n7kDGXwInfs9pwyjpgQw/V23yywFtUhaR8Xgw8zqC/YfJpeK4PetJ9/k+xFbICuX7\nWDv/k5Wth3VZSaVjm/tunWajtt3OLOQQaMSoLqP41XAHHuCyzfCwJ2Vsa2FyCINF\nyG6XobokeICDRnH44POqudcLVIDvZLQqu2LF+mZd+OO5nqmTa68kkwRf/m93eOJP\no7GvYtQSp7CPJC7ks2gl8U7wuT9DQT5/0wqkoEyLZg/KLUlzgXjMa+7GtCLTC1Ku\nOh9vw02f4K44RW4nWwIDAQABoxUwEzARBgorBgEEAYLECgMDBAMEAwcwDQYJKoZI\nhvcNAQELBQADggEBAHD/uXqNgCYywj2ee7s7kix2TT4XN9OIn0fTNh5LEiUN+q7U\nzJc9q7b5WD7PfaG6UNyuaSnLaq+dLOCJ4bX4h+/MwQSndQg0epMra1ThVQZkMkGa\nktAJ5JT6j9qxNxD1RWMl91e4JwtGzFyDwFyyUGnSwhMsqMdwfBsmTpvgxmAD/NMs\nkWB/m91FV9D+UBqsZRoLoc44kEFYBZ09ypTsR699oJRsBfG0AqVYyK7rnG6663fF\nGUSWk7noVdUPXedlwXCqCymCsVheoss9qF1cffaFIl9RxGvVvCFybx0LGiYDxfgv\n80yGZIY/mAqZVDWyHZSs4f6kWK9GeLKU2Y9yby4=\n-----END CERTIFICATE-----\n",
+ keyCert: "-----BEGIN CERTIFICATE-----\nMIICVTCCAT2gAwIBAgIQAU4Yg7Qnw9FZgMBEaJ7ZMzANBgkqhkiG9w0BAQsFADAh\nMR8wHQYDVQQDDBZZdWJpY28gUElWIEF0dGVzdGF0aW9uMCAXDTE2MDMxNDAwMDAw\nMFoYDzIwNTIwNDE3MDAwMDAwWjAlMSMwIQYDVQQDDBpZdWJpS2V5IFBJViBBdHRl\nc3RhdGlvbiA5YTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABATzM3sJuwemL2Ha\nHkGIzmCVjUMreNIVrRLOvnbZjoVflk1eab/iLUlKzk/2jXTu9TISRg2dhyXcutct\nvnqr66yjTjBMMBEGCisGAQQBgsQKAwMEAwUEAzAUBgorBgEEAYLECgMHBAYCBADw\nDxQwEAYKKwYBBAGCxAoDCAQCAgEwDwYKKwYBBAGCxAoDCQQBBDANBgkqhkiG9w0B\nAQsFAAOCAQEAFX0hL5gi/g4ZM7vCH5kDAtma7eBp0LpbCzR313GGyBR7pJFtuj2l\nbWU+V3SFRihXBTDb8q+uvyCBqgz1szdZzrpfjqNkhEPfPNabxjxJxVoe6Gdcn115\naduxfqqT2u+YIsERzaIIIisehLQkc/5zLkpocA6jbKBZnZWUBJIxuz4QmYTIf0O4\nHPE2o4JbAyGx/hRaqVvDgNeAz94ZFjb4Mp3RNbbdRUZB0ehrT/IGRJoHRu2HKFGM\nylRJL2kjKPoEc4XHbCu+MfmAIrQ4Xseg85zyI7ThhYvAzktdLHhQyfYr4wrrLCN3\noeTzmiqIHe9AataJXQ+mEQEEc9TNY23RFg==\n-----END CERTIFICATE-----\n",
+ ok: false,
+ },
+ }
+
+ parseCert := func(cert string) (*x509.Certificate, error) {
+ block, _ := pem.Decode([]byte(cert))
+ if block == nil {
+ t.Fatalf("decoding PEM cert, empty block")
+ }
+ return x509.ParseCertificate(block.Bytes)
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ deviceCert, err := parseCert(test.deviceCert)
+ if err != nil {
+ t.Fatalf("parsing device cert: %v", err)
+ }
+
+ keyCert, err := parseCert(test.keyCert)
+ if err != nil {
+ t.Fatalf("parsing key cert: %v", err)
+ }
+
+ _, err = Verify(deviceCert, keyCert)
+ if (err == nil) != test.ok {
+ t.Errorf("Verify returned %v, expected test outcome %v", err, test.ok)
+ }
+ })
+ }
+}
diff --git a/piv/pcsc_openbsd.go b/piv/pcsc_openbsd.go
new file mode 100644
index 0000000..54e6d13
--- /dev/null
+++ b/piv/pcsc_openbsd.go
@@ -0,0 +1,30 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package piv
+
+import "C"
+
+// Return codes for PCSC are different on different platforms (int vs. long).
+
+func scCheck(rc C.long) error {
+ if rc == rcSuccess {
+ return nil
+ }
+ return &scErr{int64(rc)}
+}
+
+func isRCNoReaders(rc C.long) bool {
+ return rc == 0x8010002E
+}
diff --git a/piv/pcsc_unix.go b/piv/pcsc_unix.go
index 55d6318..a43d259 100644
--- a/piv/pcsc_unix.go
+++ b/piv/pcsc_unix.go
@@ -12,7 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// +build darwin linux freebsd
+//go:build darwin || linux || freebsd || openbsd
+// +build darwin linux freebsd openbsd
package piv
@@ -24,6 +25,10 @@ package piv
// #cgo freebsd CFLAGS: -I/usr/local/include/PCSC
// #cgo freebsd LDFLAGS: -L/usr/local/lib/
// #cgo freebsd LDFLAGS: -lpcsclite
+// #cgo openbsd CFLAGS: -I/usr/local/include/
+// #cgo openbsd CFLAGS: -I/usr/local/include/PCSC
+// #cgo openbsd LDFLAGS: -L/usr/local/lib/
+// #cgo openbsd LDFLAGS: -lpcsclite
// #include <PCSC/winscard.h>
// #include <PCSC/wintypes.h>
import "C"
diff --git a/piv/pcsc_windows.go b/piv/pcsc_windows.go
index 930ef38..845194f 100644
--- a/piv/pcsc_windows.go
+++ b/piv/pcsc_windows.go
@@ -127,9 +127,13 @@ func (c *scContext) Connect(reader string) (*scHandle, error) {
handle syscall.Handle
activeProtocol uint16
)
+ readerPtr, err := syscall.UTF16PtrFromString(reader)
+ if err != nil {
+ return nil, fmt.Errorf("invalid reader string: %v", err)
+ }
r0, _, _ := procSCardConnectW.Call(
uintptr(c.ctx),
- uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(reader))),
+ uintptr(unsafe.Pointer(readerPtr)),
scardShareExclusive,
scardProtocolT1,
uintptr(unsafe.Pointer(&handle)),
diff --git a/piv/piv.go b/piv/piv.go
index 001d5c3..4e7171a 100644
--- a/piv/piv.go
+++ b/piv/piv.go
@@ -402,12 +402,12 @@ func ykAuthenticate(tx *scTx, key [24]byte, rand io.Reader) error {
response := make([]byte, 8)
block.Encrypt(response, challenge)
- data := append([]byte{
+ data := []byte{
0x7c, // Dynamic Authentication Template tag
20, // 2+8+2+8
0x80, // 'Witness'
0x08, // Tag length
- })
+ }
data = append(data, cardResponse...)
data = append(data,
0x81, // 'Challenge'
@@ -632,47 +632,6 @@ func ykSerial(tx *scTx, v *version) (uint32, error) {
return binary.BigEndian.Uint32(resp), nil
}
-// ykChangeManagementKey sets the Management Key to the new key provided. The
-// user must have authenticated with the existing key first.
-func ykChangeManagementKey(tx *scTx, key [24]byte) error {
- cmd := apdu{
- instruction: insSetMGMKey,
- param1: 0xff,
- param2: 0xff, // TODO: support touch policy
- data: append([]byte{
- alg3DES, keyCardManagement, 24,
- }, key[:]...),
- }
- if _, err := tx.Transmit(cmd); err != nil {
- return fmt.Errorf("command failed: %w", err)
- }
- return nil
-}
-
-func unmarshalDERField(b []byte, tag uint64) (obj []byte, err error) {
- var prefix []byte
- for tag > 0 {
- prefix = append(prefix, byte(tag))
- tag = tag >> 8
- }
- for i, j := 0, len(prefix)-1; i < j; i, j = i+1, j-1 {
- prefix[i], prefix[j] = prefix[j], prefix[i]
- }
-
- hasPrefix := bytes.HasPrefix(b, prefix)
- for len(b) > 0 {
- var v asn1.RawValue
- b, err = asn1.Unmarshal(b, &v)
- if err != nil {
- return nil, err
- }
- if hasPrefix {
- return v.Bytes, nil
- }
- }
- return nil, fmt.Errorf("no der value with tag 0x%x", prefix)
-}
-
// Metadata returns protected data stored on the card. This can be used to
// retrieve PIN protected management keys.
func (yk *YubiKey) Metadata(pin string) (*Metadata, error) {
@@ -780,9 +739,7 @@ func (m *Metadata) unmarshal(b []byte) error {
return fmt.Errorf("invalid management key length: %d", len(v.Bytes))
}
var key [24]byte
- for i := 0; i < len(v.Bytes); i++ {
- key[i] = v.Bytes[i]
- }
+ copy(key[:], v.Bytes)
m.ManagementKey = &key
}
return nil
Debdiff
[The following lists of changes regard files as different if they have different names, permissions or owners.]
Files in second set of .debs but not in first
-rw-r--r-- root/root /usr/share/gocode/src/github.com/go-piv/piv-go/piv/pcsc_openbsd.go
No differences were encountered in the control files