@@ -22,14 +22,30 @@ const Scheme = "yubikey"
2222
2323// YubiKey implements the KMS interface on a YubiKey.
2424type YubiKey struct {
25- yk * piv. YubiKey
25+ yk pivKey
2626 pin string
2727 managementKey [24 ]byte
2828}
2929
30+ type pivKey interface {
31+ Certificate (slot piv.Slot ) (* x509.Certificate , error )
32+ SetCertificate (key [24 ]byte , slot piv.Slot , cert * x509.Certificate ) error
33+ GenerateKey (key [24 ]byte , slot piv.Slot , opts piv.Key ) (crypto.PublicKey , error )
34+ PrivateKey (slot piv.Slot , public crypto.PublicKey , auth piv.KeyAuth ) (crypto.PrivateKey , error )
35+ Attest (slot piv.Slot ) (* x509.Certificate , error )
36+ Close () error
37+ }
38+
39+ var pivCards = piv .Cards
40+
41+ var pivOpen = func (card string ) (pivKey , error ) {
42+ return piv .Open (card )
43+ }
44+
3045// New initializes a new YubiKey.
3146// TODO(mariano): only one card is currently supported.
3247func New (ctx context.Context , opts apiv1.Options ) (* YubiKey , error ) {
48+ pin := "123456"
3349 managementKey := piv .DefaultManagementKey
3450
3551 if opts .URI != "" {
@@ -57,22 +73,26 @@ func New(ctx context.Context, opts apiv1.Options) (*YubiKey, error) {
5773 copy (managementKey [:], b [:24 ])
5874 }
5975
60- cards , err := piv .Cards ()
76+ if opts .Pin != "" {
77+ pin = opts .Pin
78+ }
79+
80+ cards , err := pivCards ()
6181 if err != nil {
6282 return nil , err
6383 }
6484 if len (cards ) == 0 {
6585 return nil , errors .New ("error detecting yubikey: try removing and reconnecting the device" )
6686 }
6787
68- yk , err := piv . Open (cards [0 ])
88+ yk , err := pivOpen (cards [0 ])
6989 if err != nil {
7090 return nil , errors .Wrap (err , "error opening yubikey" )
7191 }
7292
7393 return & YubiKey {
7494 yk : yk ,
75- pin : opts . Pin ,
95+ pin : pin ,
7696 managementKey : managementKey ,
7797 }, nil
7898}
@@ -170,13 +190,21 @@ func (k *YubiKey) CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, e
170190 return nil , err
171191 }
172192
193+ pin := k .pin
194+ if pin == "" {
195+ // Attempt to get the pin from the uri
196+ if u , err := uri .ParseWithScheme (Scheme , req .SigningKey ); err == nil {
197+ pin = u .Pin ()
198+ }
199+ }
200+
173201 pub , err := k .getPublicKey (slot )
174202 if err != nil {
175203 return nil , err
176204 }
177205
178206 priv , err := k .yk .PrivateKey (slot , pub , piv.KeyAuth {
179- PIN : k . pin ,
207+ PIN : pin ,
180208 PINPolicy : piv .PINPolicyAlways ,
181209 })
182210 if err != nil {
@@ -190,6 +218,35 @@ func (k *YubiKey) CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, e
190218 return signer , nil
191219}
192220
221+ // CreateAttestation creates an attestation certificate from a YubiKey slot.
222+ //
223+ // # Experimental
224+ //
225+ // Notice: This API is EXPERIMENTAL and may be changed or removed in a later
226+ // release.
227+ func (k * YubiKey ) CreateAttestation (req * apiv1.CreateAttestationRequest ) (* apiv1.CreateAttestationResponse , error ) {
228+ slot , err := getSlot (req .Name )
229+ if err != nil {
230+ return nil , err
231+ }
232+
233+ cert , err := k .yk .Attest (slot )
234+ if err != nil {
235+ return nil , errors .Wrap (err , "error attesting slot" )
236+ }
237+
238+ intermediate , err := k .yk .Certificate (slotAttestation )
239+ if err != nil {
240+ return nil , errors .Wrap (err , "error retrieving attestation certificate" )
241+ }
242+
243+ return & apiv1.CreateAttestationResponse {
244+ Certificate : cert ,
245+ CertificateChain : []* x509.Certificate {intermediate },
246+ PublicKey : cert .PublicKey ,
247+ }, nil
248+ }
249+
193250// Close releases the connection to the YubiKey.
194251func (k * YubiKey ) Close () error {
195252 return errors .Wrap (k .yk .Close (), "error closing yubikey" )
@@ -258,6 +315,8 @@ func getSignatureAlgorithm(alg apiv1.SignatureAlgorithm, bits int) (piv.Algorith
258315 }
259316}
260317
318+ var slotAttestation = piv.Slot {Key : 0xf9 , Object : 0x5fff01 }
319+
261320var slotMapping = map [string ]piv.Slot {
262321 "9a" : piv .SlotAuthentication ,
263322 "9c" : piv .SlotSignature ,
@@ -307,7 +366,7 @@ func getSlotAndName(name string) (piv.Slot, string, error) {
307366 return piv.Slot {}, "" , errors .Wrapf (err , "error parsing '%s'" , name )
308367 }
309368 if slotID = v .Get ("slot-id" ); slotID == "" {
310- return piv.Slot {}, "" , errors .Wrapf ( err , "error parsing '%s': slot-id is missing" , name )
369+ return piv.Slot {}, "" , errors .Errorf ( "error parsing '%s': slot-id is missing" , name )
311370 }
312371 } else {
313372 slotID = name
0 commit comments