...

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

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

     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 p11token
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"runtime"
    24  	"sync"
    25  
    26  	"github.com/miekg/pkcs11"
    27  	"github.com/sassoftware/relic/config"
    28  	"github.com/sassoftware/relic/lib/passprompt"
    29  	"github.com/sassoftware/relic/signers/sigerrors"
    30  	"github.com/sassoftware/relic/token"
    31  )
    32  
    33  const (
    34  	CKS_RO_PUBLIC_SESSION = 0
    35  	CKS_RO_USER_FUNCTIONS = 1
    36  	CKS_RW_PUBLIC_SESSION = 2
    37  	CKS_RW_USER_FUNCTIONS = 3
    38  	CKS_RW_SO_FUNCTIONS   = 4
    39  
    40  	CKA_ID            = pkcs11.CKA_ID
    41  	CKA_LABEL         = pkcs11.CKA_LABEL
    42  	CKA_SERIAL_NUMBER = pkcs11.CKA_SERIAL_NUMBER
    43  
    44  	CKK_RSA   = pkcs11.CKK_RSA
    45  	CKK_ECDSA = pkcs11.CKK_ECDSA
    46  )
    47  
    48  func init() {
    49  	token.Openers["pkcs11"] = open
    50  	token.Listers["pkcs11"] = List
    51  }
    52  
    53  var providerMap map[string]*pkcs11.Ctx
    54  var providerMutex sync.Mutex
    55  
    56  type Token struct {
    57  	config    *config.Config
    58  	tokenConf *config.TokenConfig
    59  	ctx       *pkcs11.Ctx
    60  	sh        pkcs11.SessionHandle
    61  	mutex     sync.Mutex
    62  }
    63  
    64  func List(provider string, output io.Writer) error {
    65  	ctx := pkcs11.New(provider)
    66  	if ctx == nil {
    67  		return errors.New("Failed to initialize pkcs11 provider")
    68  	}
    69  	defer ctx.Destroy()
    70  	if err := ctx.Initialize(); err != nil {
    71  		return err
    72  	}
    73  	slots, err := ctx.GetSlotList(false)
    74  	if err != nil {
    75  		return err
    76  	}
    77  	for _, slot := range slots {
    78  		info, err := ctx.GetTokenInfo(slot)
    79  		if rv, ok := err.(pkcs11.Error); ok && rv == pkcs11.CKR_TOKEN_NOT_PRESENT {
    80  			continue
    81  		}
    82  		fmt.Fprintf(output, "slot %d:\n manuf:  %s\n model:  %s\n label:  %s\n serial: %s\n", slot, info.ManufacturerID, info.Model, info.Label, info.SerialNumber)
    83  	}
    84  	return nil
    85  }
    86  
    87  // Load a PKCS#11 provider, open a session, and login
    88  func Open(config *config.Config, tokenName string, pinProvider passprompt.PasswordGetter) (*Token, error) {
    89  	tokenConf, err := config.GetToken(tokenName)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	ctx, err := openLib(tokenConf, true)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	tok := &Token{
    98  		ctx:       ctx,
    99  		config:    config,
   100  		tokenConf: tokenConf,
   101  	}
   102  	runtime.SetFinalizer(tok, (*Token).Close)
   103  	slot, err := tok.findSlot()
   104  	if err != nil {
   105  		tok.Close()
   106  		return nil, err
   107  	}
   108  	mode := uint(pkcs11.CKF_SERIAL_SESSION | pkcs11.CKF_RW_SESSION)
   109  	sh, err := tok.ctx.OpenSession(slot, mode)
   110  	if err != nil {
   111  		tok.Close()
   112  		return nil, err
   113  	}
   114  	tok.sh = sh
   115  	err = tok.autoLogIn(pinProvider)
   116  	if err != nil {
   117  		tok.Close()
   118  		return nil, err
   119  	}
   120  	return tok, nil
   121  }
   122  
   123  // compat shim for token.Openers
   124  func open(cfg *config.Config, tokenName string, prompt passprompt.PasswordGetter) (token.Token, error) {
   125  	return Open(cfg, tokenName, prompt)
   126  }
   127  
   128  func openLib(tokenConf *config.TokenConfig, write bool) (*pkcs11.Ctx, error) {
   129  	if tokenConf.Provider == "" {
   130  		return nil, errors.New("Missing attribute \"provider\" in token configuration")
   131  	}
   132  	providerMutex.Lock()
   133  	defer providerMutex.Unlock()
   134  	if providerMap == nil {
   135  		providerMap = make(map[string]*pkcs11.Ctx)
   136  	}
   137  	ctx, ok := providerMap[tokenConf.Provider]
   138  	if ok {
   139  		return ctx, nil
   140  	}
   141  	ctx = pkcs11.New(tokenConf.Provider)
   142  	if ctx == nil {
   143  		return nil, errors.New("Failed to initialize pkcs11 provider")
   144  	}
   145  	err := ctx.Initialize()
   146  	if err != nil {
   147  		ctx.Destroy()
   148  		return nil, err
   149  	}
   150  	providerMap[tokenConf.Provider] = ctx
   151  	return ctx, nil
   152  }
   153  
   154  // Close the token session
   155  func (tok *Token) Close() error {
   156  	tok.mutex.Lock()
   157  	defer tok.mutex.Unlock()
   158  	if tok.ctx != nil {
   159  		tok.ctx.CloseSession(tok.sh)
   160  		tok.ctx = nil
   161  		runtime.SetFinalizer(tok, nil)
   162  	}
   163  	return nil
   164  }
   165  
   166  func (tok *Token) Config() *config.TokenConfig {
   167  	return tok.tokenConf
   168  }
   169  
   170  func (tok *Token) findSlot() (uint, error) {
   171  	tokenConf := tok.tokenConf
   172  	slots, err := tok.ctx.GetSlotList(false)
   173  	if err != nil {
   174  		return 0, nil
   175  	}
   176  	candidates := make([]uint, 0, len(slots))
   177  	for _, slot := range slots {
   178  		info, err := tok.ctx.GetTokenInfo(slot)
   179  		if err != nil {
   180  			if rv, ok := err.(pkcs11.Error); ok && rv == pkcs11.CKR_TOKEN_NOT_PRESENT {
   181  				continue
   182  			}
   183  			return 0, err
   184  		}
   185  		if tokenConf.Label != "" && tokenConf.Label != info.Label {
   186  			continue
   187  		} else if tokenConf.Serial != "" && tokenConf.Serial != info.SerialNumber {
   188  			continue
   189  		}
   190  		candidates = append(candidates, slot)
   191  	}
   192  	if len(candidates) == 0 {
   193  		return 0, errors.New("No token found with the specified attributes")
   194  	} else if len(candidates) != 1 {
   195  		return 0, errors.New("Multiple tokens matched the specified attributes")
   196  	} else {
   197  		return candidates[0], nil
   198  	}
   199  }
   200  
   201  // Test that the token is responding and the user is (still) logged in
   202  func (tok *Token) isLoggedIn() (bool, error) {
   203  	tok.mutex.Lock()
   204  	defer tok.mutex.Unlock()
   205  	info, err := tok.ctx.GetSessionInfo(tok.sh)
   206  	if err != nil {
   207  		return false, err
   208  	}
   209  	return (info.State == CKS_RO_USER_FUNCTIONS || info.State == CKS_RW_USER_FUNCTIONS || info.State == CKS_RW_SO_FUNCTIONS), nil
   210  }
   211  
   212  func (tok *Token) Ping() error {
   213  	loggedIn, err := tok.isLoggedIn()
   214  	if err != nil {
   215  		return err
   216  	} else if !loggedIn {
   217  		return errors.New("token not logged in")
   218  	}
   219  	return nil
   220  }
   221  
   222  func (tok *Token) login(user uint, pin string) error {
   223  	tok.mutex.Lock()
   224  	defer tok.mutex.Unlock()
   225  	err := tok.ctx.Login(tok.sh, user, pin)
   226  	if err != nil {
   227  		if rv, ok := err.(pkcs11.Error); ok && rv == pkcs11.CKR_PIN_INCORRECT {
   228  			return sigerrors.PinIncorrectError{}
   229  		}
   230  	}
   231  	return err
   232  }
   233  
   234  func (tok *Token) autoLogIn(pinProvider passprompt.PasswordGetter) error {
   235  	tokenConf := tok.tokenConf
   236  	loggedIn, err := tok.isLoggedIn()
   237  	if err != nil {
   238  		return err
   239  	}
   240  	if loggedIn {
   241  		return nil
   242  	}
   243  	user := pkcs11.CKU_USER
   244  	if tokenConf.User != nil {
   245  		user = *tokenConf.User
   246  	}
   247  	loginFunc := func(pin string) (bool, error) {
   248  		if err := tok.login(user, pin); err == nil {
   249  			return true, nil
   250  		} else if _, ok := err.(sigerrors.PinIncorrectError); ok {
   251  			return false, nil
   252  		} else {
   253  			return false, err
   254  		}
   255  	}
   256  	initialPrompt := fmt.Sprintf("PIN for token %s user %08x: ", tokenConf.Name(), user)
   257  	keyringUser := fmt.Sprintf("%s.%08x", tokenConf.Name(), user)
   258  	return token.Login(tokenConf, pinProvider, loginFunc, keyringUser, initialPrompt)
   259  }
   260  
   261  func (tok *Token) getAttribute(handle pkcs11.ObjectHandle, attr uint) []byte {
   262  	attrs, err := tok.ctx.GetAttributeValue(tok.sh, handle, []*pkcs11.Attribute{pkcs11.NewAttribute(attr, nil)})
   263  	if err != nil {
   264  		return nil
   265  	}
   266  	return attrs[0].Value
   267  }
   268  
   269  func (tok *Token) findObject(attrs []*pkcs11.Attribute) ([]pkcs11.ObjectHandle, error) {
   270  	if err := tok.ctx.FindObjectsInit(tok.sh, attrs); err != nil {
   271  		return nil, err
   272  	}
   273  	objects, _, err := tok.ctx.FindObjects(tok.sh, 10)
   274  	if err != nil {
   275  		tok.ctx.FindObjectsFinal(tok.sh)
   276  		return nil, err
   277  	}
   278  	if err := tok.ctx.FindObjectsFinal(tok.sh); err != nil {
   279  		return nil, err
   280  	}
   281  	return objects, nil
   282  }
   283  
   284  func attrToInt(value []byte) uint {
   285  	var n uint
   286  	for i := len(value) - 1; i >= 0; i-- {
   287  		n = n<<8 | uint(value[i])
   288  	}
   289  	return n
   290  }
   291  

View as plain text