...

Source file src/github.com/sassoftware/relic/token/scdtoken/scdtoken.go

Documentation: github.com/sassoftware/relic/token/scdtoken

     1  //
     2  // Copyright (c) SAS Institute Inc.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  //
    16  
    17  package scdtoken
    18  
    19  import (
    20  	"crypto"
    21  	"crypto/ecdsa"
    22  	"crypto/rsa"
    23  	"crypto/x509"
    24  	"errors"
    25  	"fmt"
    26  	"io"
    27  	"os"
    28  	"strings"
    29  	"sync"
    30  
    31  	"github.com/sassoftware/relic/config"
    32  	"github.com/sassoftware/relic/lib/assuan"
    33  	"github.com/sassoftware/relic/lib/passprompt"
    34  	"github.com/sassoftware/relic/lib/x509tools"
    35  	"github.com/sassoftware/relic/signers/sigerrors"
    36  	"github.com/sassoftware/relic/token"
    37  )
    38  
    39  func init() {
    40  	token.Openers["scdtoken"] = Open
    41  	token.Listers["scdtoken"] = List
    42  }
    43  
    44  var defaultScdSockets = []string{
    45  	"/run/user/$UID/gnupg/S.scdaemon",
    46  	"/var/run/user/$UID/gnupg/S.scdaemon",
    47  	"$HOME/.gnupg/S.scdaemon",
    48  }
    49  
    50  type scdToken struct {
    51  	config      *config.Config
    52  	tokenConf   *config.TokenConfig
    53  	sock        *assuan.ScdConn
    54  	serial, pin string
    55  	keyInfos    []*assuan.ScdKey
    56  	mu          sync.Mutex
    57  }
    58  
    59  type scdKey struct {
    60  	token     *scdToken
    61  	keyConf   *config.KeyConfig
    62  	key       *assuan.ScdKey
    63  	publicKey crypto.PublicKey
    64  }
    65  
    66  func findSock() string {
    67  	uid := fmt.Sprintf("%d", os.Getuid())
    68  	for _, fp := range defaultScdSockets {
    69  		fp = strings.Replace(fp, "$UID", uid, -1)
    70  		fp = os.ExpandEnv(fp)
    71  		_, err := os.Stat(fp)
    72  		if err == nil {
    73  			return fp
    74  		}
    75  	}
    76  	return ""
    77  }
    78  
    79  func List(sockPath string, output io.Writer) error {
    80  	if sockPath == "" {
    81  		sockPath = findSock()
    82  	}
    83  	if sockPath == "" {
    84  		return errors.New("scdaemon not found; provide an explicit path to the scdaemon socket")
    85  	}
    86  	sock, err := assuan.DialScd(sockPath)
    87  	if err != nil {
    88  		return err
    89  	}
    90  	defer sock.Close()
    91  	keyInfos, err := sock.Learn()
    92  	if err != nil {
    93  		return err
    94  	}
    95  	if len(keyInfos) == 0 {
    96  		fmt.Fprintln(output, "token is empty or missing")
    97  	} else {
    98  		fmt.Fprintf(output, "serial: %s\n", keyInfos[0].Serial)
    99  	}
   100  	return nil
   101  }
   102  
   103  func Open(conf *config.Config, tokenName string, prompt passprompt.PasswordGetter) (token.Token, error) {
   104  	tconf, err := conf.GetToken(tokenName)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  	sockPath := tconf.Provider
   109  	if sockPath == "" {
   110  		sockPath = findSock()
   111  	}
   112  	if sockPath == "" {
   113  		return nil, fmt.Errorf("scdaemon not found; set tokens.%s.provider to the path to the scdaemon socket", tokenName)
   114  	}
   115  	sock, err := assuan.DialScd(sockPath)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  	tok := &scdToken{
   120  		config:    conf,
   121  		tokenConf: tconf,
   122  		sock:      sock,
   123  	}
   124  	if err := tok.login(prompt); err != nil {
   125  		sock.Close()
   126  		return nil, err
   127  	}
   128  	return tok, nil
   129  }
   130  
   131  func (tok *scdToken) login(prompt passprompt.PasswordGetter) error {
   132  	tconf := tok.tokenConf
   133  	keyInfos, err := tok.sock.Learn()
   134  	if err != nil {
   135  		return err
   136  	}
   137  	tok.keyInfos = keyInfos
   138  	tok.serial = keyInfos[0].Serial
   139  	if tconf.Serial != "" && tconf.Serial != tok.serial {
   140  		return fmt.Errorf("scdaemon token %s has serial %s but configuration specifies %s", tconf.Name(), tok.serial, tconf.Serial)
   141  	}
   142  	loginFunc := func(pin string) (bool, error) {
   143  		if err := tok.sock.CheckPin(pin); err == nil {
   144  			tok.pin = pin
   145  			return true, nil
   146  		} else if _, ok := err.(sigerrors.PinIncorrectError); ok {
   147  			return false, nil
   148  		} else {
   149  			return false, err
   150  		}
   151  	}
   152  	initialPrompt := fmt.Sprintf("PIN for token %s (serial %s): ", tconf.Name(), tok.serial)
   153  	keyringUser := tok.serial
   154  	return token.Login(tconf, prompt, loginFunc, keyringUser, initialPrompt)
   155  }
   156  
   157  func (tok *scdToken) Ping() error {
   158  	tok.mu.Lock()
   159  	defer tok.mu.Unlock()
   160  	// TODO
   161  	return nil
   162  }
   163  
   164  func (tok *scdToken) Close() error {
   165  	tok.mu.Lock()
   166  	defer tok.mu.Unlock()
   167  	if tok.sock != nil {
   168  		tok.sock.Close()
   169  		tok.sock = nil
   170  	}
   171  	return nil
   172  }
   173  
   174  func (tok *scdToken) Config() *config.TokenConfig {
   175  	return tok.tokenConf
   176  }
   177  
   178  func (tok *scdToken) ListKeys(opts token.ListOptions) error {
   179  	tok.mu.Lock()
   180  	defer tok.mu.Unlock()
   181  	fmt.Fprintf(opts.Output, "serial: %#v\n", tok.serial)
   182  	for i, key := range tok.keyInfos {
   183  		if opts.ID != "" && opts.ID != key.KeyId {
   184  			continue
   185  		}
   186  		fmt.Fprintf(opts.Output, "key %d:\n id:          %s\n fingerprint: %s\n keygrip:     %s\n", i+1, key.KeyId, key.Fingerprint, key.KeyGrip)
   187  		if opts.Values {
   188  			pub, err := key.Public()
   189  			if err != nil {
   190  				fmt.Fprintln(opts.Output, " error reading key:", err)
   191  				continue
   192  			}
   193  			switch k := pub.(type) {
   194  			case *rsa.PublicKey:
   195  				fmt.Fprintf(opts.Output, " n:           0x%x\n e:           %d\n", k.N, k.E)
   196  			case *ecdsa.PublicKey:
   197  				curve, err := x509tools.CurveByCurve(k.Curve)
   198  				if err == nil {
   199  					fmt.Fprintf(opts.Output, " bits:        %d\n", curve.Bits)
   200  				}
   201  				fmt.Fprintf(opts.Output, " x:           %x\n y:           %x\n", k.X, k.Y)
   202  			}
   203  		}
   204  	}
   205  	return nil
   206  }
   207  
   208  func (tok *scdToken) GetKey(keyName string) (token.Key, error) {
   209  	tok.mu.Lock()
   210  	defer tok.mu.Unlock()
   211  	keyConf, err := tok.config.GetKey(keyName)
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  	var key *assuan.ScdKey
   216  	for _, kc := range tok.keyInfos {
   217  		if keyConf.ID != "" && keyConf.ID != kc.KeyId {
   218  			continue
   219  		}
   220  		key = kc
   221  		break
   222  	}
   223  	if key.KeyId == "" {
   224  		return nil, fmt.Errorf("key %s not found in token %s", keyName, tok.tokenConf.Name())
   225  	}
   226  	pubkey, err := key.Public()
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  	return &scdKey{
   231  		token:     tok,
   232  		keyConf:   keyConf,
   233  		key:       key,
   234  		publicKey: pubkey,
   235  	}, nil
   236  }
   237  
   238  func (key *scdKey) Public() crypto.PublicKey {
   239  	return key.publicKey
   240  }
   241  
   242  func (key *scdKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
   243  	key.token.mu.Lock()
   244  	defer key.token.mu.Unlock()
   245  	return key.key.Sign(digest, opts, key.token.pin)
   246  }
   247  
   248  func (key *scdKey) Config() *config.KeyConfig {
   249  	return key.keyConf
   250  }
   251  
   252  func (key *scdKey) GetID() []byte {
   253  	return []byte(key.token.serial)
   254  }
   255  
   256  func (tok *scdToken) Import(keyName string, privKey crypto.PrivateKey) (token.Key, error) {
   257  	return nil, errors.New("function not implemented for tokens of type \"scdaemon\"")
   258  }
   259  
   260  func (tok *scdToken) ImportCertificate(cert *x509.Certificate, labelBase string) error {
   261  	return errors.New("function not implemented for tokens of type \"scdaemon\"")
   262  }
   263  
   264  func (tok *scdToken) Generate(keyName string, keyType token.KeyType, bits uint) (token.Key, error) {
   265  	// TODO - probably useful
   266  	return nil, errors.New("function not implemented for tokens of type \"scdaemon\"")
   267  }
   268  
   269  func (key *scdKey) ImportCertificate(cert *x509.Certificate) error {
   270  	return errors.New("function not implemented for tokens of type \"scdaemon\"")
   271  }
   272  

View as plain text