Skip to content

Commit

Permalink
Add the correct gpg code.
Browse files Browse the repository at this point in the history
Needs some cleanup and need to check on the whole pcsc interface
  • Loading branch information
areese committed Feb 20, 2024
1 parent d85c6f7 commit 1ec9fad
Show file tree
Hide file tree
Showing 7 changed files with 574 additions and 54 deletions.
40 changes: 22 additions & 18 deletions piv/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ import (
// is given keys using different algorithms.
var errMismatchingAlgorithms = errors.New("mismatching key algorithms")

// errUnsupportedKeySize is returned when a key has an unsupported size
// errUnsupportedKeySize is returned when a key has an unsupported size.
var errUnsupportedKeySize = errors.New("unsupported key size")

// unsupportedCurveError is used when a key has an unsupported curve
// unsupportedCurveError is used when a key has an unsupported curve.
type unsupportedCurveError struct {
curve int
}
Expand Down Expand Up @@ -427,7 +427,7 @@ func RetiredKeyManagementSlot(key uint32) (Slot, bool) {
return slot, ok
}

// String returns the two-character hex representation of the slot
// String returns the two-character hex representation of the slot.
func (s Slot) String() string {
return strconv.FormatUint(uint64(s.Key), 16)
}
Expand Down Expand Up @@ -571,7 +571,7 @@ func (yk *YubiKey) Attest(slot Slot) (*x509.Certificate, error) {
return nil, err
}

func ykAttest(tx *scTx, slot Slot) (*x509.Certificate, error) {
func ykAttest(tx SCTx, slot Slot) (*x509.Certificate, error) {
cmd := apdu{
instruction: insAttest,
param1: byte(slot.Key),
Expand Down Expand Up @@ -680,7 +680,7 @@ func (yk *YubiKey) KeyInfo(slot Slot) (KeyInfo, error) {
return ki, nil
}

// Certificate returns the certifiate object stored in a given slot.
// Certificate returns the certificate object stored in a given slot.
//
// If a certificate hasn't been set in the provided slot, the returned error
// wraps ErrNotFound.
Expand Down Expand Up @@ -750,7 +750,7 @@ func (yk *YubiKey) SetCertificate(key [24]byte, slot Slot, cert *x509.Certificat
return ykStoreCertificate(yk.tx, slot, cert)
}

func ykStoreCertificate(tx *scTx, slot Slot, cert *x509.Certificate) error {
func ykStoreCertificate(tx SCTx, slot Slot, cert *x509.Certificate) error {
// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-73-4.pdf#page=40
data := marshalASN1(0x70, cert.Raw)
// "for a certificate encoded in uncompressed form CertInfo shall be 0x00"
Expand Down Expand Up @@ -804,7 +804,7 @@ func (yk *YubiKey) GenerateKey(key [24]byte, slot Slot, opts Key) (crypto.Public
return ykGenerateKey(yk.tx, slot, opts)
}

func ykGenerateKey(tx *scTx, slot Slot, o Key) (crypto.PublicKey, error) {
func ykGenerateKey(tx SCTx, slot Slot, o Key) (crypto.PublicKey, error) {
alg, ok := algorithmsMap[o.Algorithm]
if !ok {
return nil, fmt.Errorf("unsupported algorithm")
Expand Down Expand Up @@ -918,7 +918,7 @@ func (k KeyAuth) authTx(yk *YubiKey, pp PINPolicy) error {
return ykLogin(yk.tx, pin)
}

func (k KeyAuth) do(yk *YubiKey, pp PINPolicy, f func(tx *scTx) ([]byte, error)) ([]byte, error) {
func (k KeyAuth) do(yk *YubiKey, pp PINPolicy, f func(tx SCTx) ([]byte, error)) ([]byte, error) {
if err := k.authTx(yk, pp); err != nil {
return nil, err
}
Expand Down Expand Up @@ -1080,7 +1080,7 @@ func (yk *YubiKey) SetPrivateKeyInsecure(key [24]byte, slot Slot, private crypto
return ykImportKey(yk.tx, tags, slot, policy)
}

func ykImportKey(tx *scTx, tags []byte, slot Slot, o Key) error {
func ykImportKey(tx SCTx, tags []byte, slot Slot, o Key) error {
alg, ok := algorithmsMap[o.Algorithm]
if !ok {
return fmt.Errorf("unsupported algorithm")
Expand Down Expand Up @@ -1136,7 +1136,7 @@ var _ crypto.Signer = (*ECDSAPrivateKey)(nil)

// Sign implements crypto.Signer.
func (k *ECDSAPrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
return k.auth.do(k.yk, k.pp, func(tx *scTx) ([]byte, error) {
return k.auth.do(k.yk, k.pp, func(tx SCTx) ([]byte, error) {
return ykSignECDSA(tx, k.slot, k.pub, digest)
})
}
Expand All @@ -1155,7 +1155,7 @@ func (k *ECDSAPrivateKey) SharedKey(peer *ecdsa.PublicKey) ([]byte, error) {
return nil, errMismatchingAlgorithms
}
msg := elliptic.Marshal(peer.Curve, peer.X, peer.Y)
return k.auth.do(k.yk, k.pp, func(tx *scTx) ([]byte, error) {
return k.auth.do(k.yk, k.pp, func(tx SCTx) ([]byte, error) {
var alg byte
size := k.pub.Params().BitSize
switch size {
Expand Down Expand Up @@ -1206,7 +1206,7 @@ func (k *keyEd25519) Public() crypto.PublicKey {
}

func (k *keyEd25519) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
return k.auth.do(k.yk, k.pp, func(tx *scTx) ([]byte, error) {
return k.auth.do(k.yk, k.pp, func(tx SCTx) ([]byte, error) {
return skSignEd25519(tx, k.slot, k.pub, digest)
})
}
Expand All @@ -1224,18 +1224,18 @@ func (k *keyRSA) Public() crypto.PublicKey {
}

func (k *keyRSA) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
return k.auth.do(k.yk, k.pp, func(tx *scTx) ([]byte, error) {
return k.auth.do(k.yk, k.pp, func(tx SCTx) ([]byte, error) {
return ykSignRSA(tx, rand, k.slot, k.pub, digest, opts)
})
}

func (k *keyRSA) Decrypt(rand io.Reader, msg []byte, opts crypto.DecrypterOpts) ([]byte, error) {
return k.auth.do(k.yk, k.pp, func(tx *scTx) ([]byte, error) {
return k.auth.do(k.yk, k.pp, func(tx SCTx) ([]byte, error) {
return ykDecryptRSA(tx, k.slot, k.pub, msg)
})
}

func ykSignECDSA(tx *scTx, slot Slot, pub *ecdsa.PublicKey, digest []byte) ([]byte, error) {
func ykSignECDSA(tx SCTx, slot Slot, pub *ecdsa.PublicKey, digest []byte) ([]byte, error) {
var alg byte
size := pub.Params().BitSize
switch size {
Expand Down Expand Up @@ -1280,7 +1280,7 @@ func ykSignECDSA(tx *scTx, slot Slot, pub *ecdsa.PublicKey, digest []byte) ([]by

// This function only works on SoloKeys prototypes and other PIV devices that choose
// to implement Ed25519 signatures under alg 0x22.
func skSignEd25519(tx *scTx, slot Slot, pub ed25519.PublicKey, digest []byte) ([]byte, error) {
func skSignEd25519(tx SCTx, slot Slot, pub ed25519.PublicKey, digest []byte) ([]byte, error) {
// Adaptation of
// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-73-4.pdf#page=118
cmd := apdu{
Expand Down Expand Up @@ -1387,7 +1387,7 @@ func rsaAlg(pub *rsa.PublicKey) (byte, error) {
}
}

func ykDecryptRSA(tx *scTx, slot Slot, pub *rsa.PublicKey, data []byte) ([]byte, error) {
func ykDecryptRSA(tx SCTx, slot Slot, pub *rsa.PublicKey, data []byte) ([]byte, error) {
alg, err := rsaAlg(pub)
if err != nil {
return nil, err
Expand Down Expand Up @@ -1426,12 +1426,16 @@ func ykDecryptRSA(tx *scTx, slot Slot, pub *rsa.PublicKey, data []byte) ([]byte,
// PKCS#1 v15 is largely informed by the standard library
// https://github.com/golang/go/blob/go1.13.5/src/crypto/rsa/pkcs1v15.go

func ykSignRSA(tx *scTx, rand io.Reader, slot Slot, pub *rsa.PublicKey, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
func ykSignRSA(tx SCTx, rand io.Reader, slot Slot, pub *rsa.PublicKey, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
hash := opts.HashFunc()
if hash.Size() != len(digest) {
return nil, fmt.Errorf("input must be a hashed message")
}

if _, ok := opts.(*rsa.PSSOptions); ok {
return nil, fmt.Errorf("rsassa-pss signatures not supported")
}

alg, err := rsaAlg(pub)
if err != nil {
return nil, err
Expand Down
74 changes: 71 additions & 3 deletions piv/pcsc.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
package piv

import (
"encoding/hex"
"errors"
"fmt"

"github.com/go-piv/piv-go/bertlv"
)

type scErr struct {
Expand All @@ -39,12 +42,16 @@ type AuthErr struct {
Retries int
}

func (v AuthErr) Error() string {
func retries(n int) string {
r := "retries"
if v.Retries == 1 {
if n == 1 {
r = "retry"
}
return fmt.Sprintf("verification failed (%d %s remaining)", v.Retries, r)
return fmt.Sprintf("verification failed (%d %s remaining)", n, r)
}

func (v AuthErr) Error() string {
return retries(v.Retries)
}

// ErrNotFound is returned when the requested object on the smart card is not found.
Expand All @@ -70,21 +77,51 @@ func (a *apduErr) Error() string {
msg = u.Error()
}

if a.sw1 == 0x61 {
msg = fmt.Sprintf("0x%02x bytes available", a.sw2)
}

if a.sw1 == 0x63 && a.sw2&0xf0 == 0xc0 {
msg = retries(int(a.Status() & 0x0f))
}

switch a.Status() {
case 0x6581:
msg = "decryption failed"

case 0x6600:
msg = "security-related issues (reserved for UIF in this application)"

case 0x6700:
msg = "wrong length (Lc and/or Le)"

// 0x6300 is "verification failed", represented as AuthErr{0}
// 0x63Cn is "verification failed" with retry, represented as AuthErr{n}
case 0x6881:
msg = "logical channel not supported"
case 0x6882:
msg = "secure messaging not supported"
case 0x6883:
msg = "last command of the chain expected"
case 0x6884:
msg = "command chaining not supported"

case 0x6982:
// Security status not satisfied PW wrong PW not checked (command not allowed) Secure messaging incorrect (checksum and/or cryptogram)
msg = "security status not satisfied"
case 0x6983:
// This will also be AuthErr{0} but we override the message here
// so that it's clear that the reason is a block rather than a simple
// failed authentication verification.
// Authentication method blocked PW blocked (error counter zero)
msg = "authentication method blocked"
case 0x6985:
msg = "Condition of use not satisfied"
case 0x6987:
// Expected secure messaging DOs missing (e. g. SM-key)
msg = "expected secure messaging data objects are missing"
case 0x6988:
// SM data objects incorrect (e. g. wrong TLV-structure in command data)
msg = "secure messaging data objects are incorrect"
case 0x6a80:
msg = "incorrect parameter in command data field"
Expand All @@ -96,7 +133,18 @@ func (a *apduErr) Error() string {
case 0x6a86:
msg = "incorrect parameter in P1 or P2"
case 0x6a88:
// Referenced data, reference data or DO not found
msg = "referenced data or reference data not found"
case 0x6b00:
msg = "Wrong parameters P1-P2"
case 0x6d00:
msg = "Instruction code (INS) not supported or invalid"
case 0x6e00:
msg = "Class (CLA) not supported"
case 0x6f00:
msg = "No precise diagnosis"
case 0x9000:
msg = "Command correct"
}

if msg != "" {
Expand Down Expand Up @@ -138,6 +186,10 @@ type apdu struct {
}

func (t *scTx) Transmit(d apdu) ([]byte, error) {
if t.debug {
fmt.Printf("Transmit: [%s]\n", bertlv.MakeJSONString(d))
}

data := d.data
var resp []byte
const maxAPDUDataSize = 0xff
Expand All @@ -152,6 +204,10 @@ func (t *scTx) Transmit(d apdu) ([]byte, error) {
data = data[maxAPDUDataSize:]
_, r, err := t.transmit(req)
if err != nil {
if t.debug {
fmt.Printf("Transmit failed: %v\nreq:\n%s\nresp:\n%s\n", err, hex.Dump(req), hex.Dump(r))
}

return nil, fmt.Errorf("transmitting initial chunk %w", err)
}
resp = append(resp, r...)
Expand All @@ -165,6 +221,10 @@ func (t *scTx) Transmit(d apdu) ([]byte, error) {
copy(req[5:], data)
hasMore, r, err := t.transmit(req)
if err != nil {
if t.debug {
fmt.Printf("Transmit failed: %v hasmore: %t\nreq:\n%s\nresp:\n%s\n", err, hasMore, hex.Dump(req), hex.Dump(r))
}

return nil, err
}
resp = append(resp, r...)
Expand All @@ -175,10 +235,18 @@ func (t *scTx) Transmit(d apdu) ([]byte, error) {
var r []byte
hasMore, r, err = t.transmit(req)
if err != nil {
if t.debug {
fmt.Printf("Transmit failed: %v hasmore: %t\nreq:\n%s\nresp:\n%s\n", err, hasMore, hex.Dump(req), hex.Dump(r))
}

return nil, fmt.Errorf("reading further response: %w", err)
}
resp = append(resp, r...)
}

if t.debug {
fmt.Printf("Response:\n%s\n", hex.Dump(resp[:]))
}

return resp, nil
}
Loading

0 comments on commit 1ec9fad

Please sign in to comment.