yubikey/pkg/value/encryption/envelope/piv/card.go (49 lines of code) (raw):
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you 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
//
// http://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 (
"crypto/ecdsa"
"errors"
"fmt"
gopiv "github.com/go-piv/piv-go/piv"
)
// Prompter is used to describe PIN prompt contract.
type Prompter func(msg string) (string, error)
// Card is a PIV card abstraction.
type Card interface {
Close() error
Public() *ecdsa.PublicKey
SharedKey(peer *ecdsa.PublicKey, prompt Prompter) ([]byte, error)
}
// -----------------------------------------------------------------------------
type pivCard struct {
card *gopiv.YubiKey
serial uint32
slot gopiv.Slot
pub *ecdsa.PublicKey
}
func (c *pivCard) Close() error {
if c.card == nil {
return nil
}
return c.card.Close()
}
func (c *pivCard) Public() *ecdsa.PublicKey {
return c.pub
}
func (c *pivCard) SharedKey(peer *ecdsa.PublicKey, prompt Prompter) ([]byte, error) {
// Check arguments
if c.card == nil {
return nil, errors.New("card is not initialized")
}
if peer == nil {
return nil, errors.New("unable to proceed with a nil peer public key")
}
// Extract certificate private key.
priv, err := c.card.PrivateKey(c.slot, c.pub, gopiv.KeyAuth{
PINPrompt: func() (string, error) {
return prompt(fmt.Sprintf("Enter PIN for Yubikey with serial %d", c.serial))
},
})
if err != nil {
return nil, fmt.Errorf("cannot get PIV private key handle: %w", err)
}
// Compute ECDH shared secret from key.
shared, err := priv.(*gopiv.ECDSAPrivateKey).SharedKey(peer)
if err != nil {
return nil, fmt.Errorf("PIV ECDHE error: %w", err)
}
// No error
return shared, nil
}