// // Copyright (c) SAS Institute Inc. // // 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 // // 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 p11token import ( "bytes" "crypto" "crypto/x509" "encoding/base64" "errors" "fmt" "io" "strings" "github.com/miekg/pkcs11" "github.com/sassoftware/relic/lib/x509tools" "github.com/sassoftware/relic/token" ) var classNames = map[uint]string{ pkcs11.CKO_DATA: "data", pkcs11.CKO_CERTIFICATE: "certificate", pkcs11.CKO_PUBLIC_KEY: "public_key", pkcs11.CKO_PRIVATE_KEY: "private_key", pkcs11.CKO_SECRET_KEY: "secret_key", pkcs11.CKO_HW_FEATURE: "hw_feature", pkcs11.CKO_DOMAIN_PARAMETERS: "domain_parameters", pkcs11.CKO_MECHANISM: "mechanism", pkcs11.CKO_OTP_KEY: "otp_key", } var keyTypes = map[uint]string{ pkcs11.CKK_RSA: "rsa", pkcs11.CKK_DSA: "dsa", pkcs11.CKK_EC: "ec", } func (tok *Token) ListKeys(opts token.ListOptions) error { filterKeyId, err := parseKeyID(opts.ID) if err != nil { return errors.New("invalid filter id") } tok.mutex.Lock() defer tok.mutex.Unlock() if err := tok.ctx.FindObjectsInit(tok.sh, nil); err != nil { return err } defer tok.ctx.FindObjectsFinal(tok.sh) for { objects, _, err := tok.ctx.FindObjects(tok.sh, 1) if err != nil { return err } else if len(objects) == 0 { break } for _, handle := range objects { objId := tok.getAttribute(handle, pkcs11.CKA_ID) label := tok.getAttribute(handle, pkcs11.CKA_LABEL) if opts.Label != "" && string(label) != opts.Label { continue } if len(filterKeyId) != 0 && !bytes.Equal(filterKeyId, objId) { continue } fmt.Fprintf(opts.Output, "handle 0x%08x:\n", handle) class := attrToInt(tok.getAttribute(handle, pkcs11.CKA_CLASS)) if name := classNames[class]; name != "" { fmt.Fprintf(opts.Output, " class: %s\n", name) } else { fmt.Fprintf(opts.Output, " class: 0x%08x\n", class) } if len(objId) > 0 { fmt.Fprintf(opts.Output, " id: %s\n", formatKeyID(objId)) } if len(label) > 0 { fmt.Fprintf(opts.Output, " label: %s\n", label) } switch class { case pkcs11.CKO_PUBLIC_KEY: tok.printKey(opts, handle) case pkcs11.CKO_PRIVATE_KEY: tok.printKey(opts, handle) case pkcs11.CKO_CERTIFICATE: tok.printCertificate(opts, handle) case pkcs11.CKO_DATA: value := tok.getAttribute(handle, pkcs11.CKA_VALUE) fmt.Fprintf(opts.Output, " size: %d\n", len(value)) if opts.Values { fmt.Fprintln(opts.Output, " value: !!binary |") dumpData(opts.Output, value) } } fmt.Fprintln(opts.Output) } } return nil } func (tok *Token) printKey(opts token.ListOptions, handle pkcs11.ObjectHandle) { keyType := attrToInt(tok.getAttribute(handle, pkcs11.CKA_KEY_TYPE)) if name := keyTypes[keyType]; name != "" { fmt.Fprintf(opts.Output, " type: %s\n", name) } else { fmt.Fprintf(opts.Output, " type: 0x%08x\n", keyType) } switch keyType { case pkcs11.CKK_RSA: if n := tok.getAttribute(handle, pkcs11.CKA_MODULUS); len(n) != 0 { fmt.Fprintf(opts.Output, " bits: %d\n", len(n)*8) if opts.Values { fmt.Fprintf(opts.Output, " n: 0x%x\n", bytesToBig(n)) } } if e := tok.getAttribute(handle, pkcs11.CKA_PUBLIC_EXPONENT); len(e) != 0 && opts.Values { fmt.Fprintf(opts.Output, " e: %s\n", bytesToBig(e)) } case pkcs11.CKK_EC: ecparams := tok.getAttribute(handle, pkcs11.CKA_EC_PARAMS) if len(ecparams) == 0 { return } curve, err := x509tools.CurveByDer(ecparams) if err != nil { fmt.Fprintf(opts.Output, " curve: %x\n", ecparams) return } fmt.Fprintf(opts.Output, " bits: %d\n", curve.Bits) ecpoint := tok.getAttribute(handle, pkcs11.CKA_EC_POINT) if len(ecpoint) > 0 && opts.Values { x, y := x509tools.DerToPoint(curve.Curve, ecpoint) if x != nil { fmt.Fprintf(opts.Output, " x: 0x%x\n", x) fmt.Fprintf(opts.Output, " y: 0x%x\n", y) } } } } func (tok *Token) printCertificate(opts token.ListOptions, handle pkcs11.ObjectHandle) { blob := tok.getAttribute(handle, pkcs11.CKA_VALUE) if len(blob) == 0 { fmt.Fprintln(opts.Output, "certificate is missing") return } cert, err := x509.ParseCertificate(blob) if err != nil { fmt.Fprintln(opts.Output, "certificate is invalid:", err) } d := crypto.SHA1.New() d.Write(blob) fmt.Fprintf(opts.Output, " subject: %s\n issuer: %s\n sha1: %x\n", x509tools.FormatSubject(cert), x509tools.FormatIssuer(cert), d.Sum(nil)) if opts.Values { fmt.Fprintln(opts.Output, " value: |\n -----BEGIN CERTIFICATE-----") dumpData(opts.Output, blob) fmt.Fprintln(opts.Output, " -----END CERTIFICATE-----") } } func formatKeyID(keyID []byte) string { chunks := make([]string, len(keyID)) for i, j := range keyID { chunks[i] = fmt.Sprintf("%02x", j) } return strings.Join(chunks, ":") } func dumpData(w io.Writer, d []byte) error { encoded := base64.StdEncoding.EncodeToString(d) for len(encoded) > 0 { n := 64 if n > len(encoded) { n = len(encoded) } if _, err := fmt.Fprintln(w, " ", encoded[:n]); err != nil { return err } encoded = encoded[n:] } return nil }