github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/providers/security/pkcs11sec/pkcs11_security.go (about) 1 // Copyright (c) 2020-2022, R.I. Pienaar and the Choria Project contributors 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 5 package pkcs11sec 6 7 import ( 8 "bytes" 9 "context" 10 "crypto" 11 "crypto/rsa" 12 "crypto/tls" 13 "crypto/x509" 14 "encoding/pem" 15 "fmt" 16 "io" 17 "net/http" 18 "os" 19 "strings" 20 "time" 21 22 "github.com/AlecAivazis/survey/v2" 23 "github.com/choria-io/go-choria/inter" 24 "github.com/miekg/pkcs11" 25 "github.com/miekg/pkcs11/p11" 26 "github.com/sirupsen/logrus" 27 28 "github.com/choria-io/go-choria/providers/security/filesec" 29 ) 30 31 // fetched from https://golang.org/src/crypto/rsa/pkcs1v15.go 32 var hashPrefixes = map[crypto.Hash][]byte{ 33 crypto.MD5: {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10}, 34 crypto.SHA1: {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14}, 35 crypto.SHA224: {0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c}, 36 crypto.SHA256: {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}, 37 crypto.SHA384: {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30}, 38 crypto.SHA512: {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40}, 39 crypto.MD5SHA1: {}, 40 crypto.RIPEMD160: {0x30, 0x20, 0x30, 0x08, 0x06, 0x06, 0x28, 0xcf, 0x06, 0x03, 0x00, 0x31, 0x04, 0x14}, 41 } 42 43 type Pkcs11Security struct { 44 conf *Config 45 log *logrus.Entry 46 47 fsec *filesec.FileSecurity 48 49 cert *tls.Certificate 50 pKey *PrivateKey 51 pin *string 52 session p11.Session 53 } 54 55 type PrivateKey struct { 56 PublicKey crypto.PublicKey 57 PrivateKey *p11.PrivateKey 58 } 59 60 func (k *PrivateKey) Public() crypto.PublicKey { 61 return k.PublicKey 62 } 63 64 // Sign signs any compatible hash that is sent to it (see hashPrefixes for supported hashes) 65 // need to handle as many hash types as possible, since this is being used by http/tls driver 66 func (k *PrivateKey) Sign(_ io.Reader, msg []byte, opts crypto.SignerOpts) (signature []byte, err error) { 67 prefix, ok := hashPrefixes[opts.HashFunc()] 68 if !ok { 69 return nil, fmt.Errorf("unknown hash function") 70 } 71 mechanism := pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS, nil) 72 input := append(prefix, msg...) 73 74 output, err := k.PrivateKey.Sign(*mechanism, input) 75 if err != nil { 76 return nil, err 77 } 78 return output, nil 79 } 80 81 type Config struct { 82 // CAFile is the file where the trusted CA cert resides 83 CAFile string 84 85 // PrivilegedUsers is a list of regular expressions that identity privileged users 86 PrivilegedUsers []string 87 88 // AllowList is a list of regular expressions that identity valid users to allow in 89 AllowList []string 90 91 // DisableTLSVerify disables TLS verify in HTTP clients etc 92 DisableTLSVerify bool 93 94 // PKCS11DriverFile points to the dynamic library file to use (usually a .so file) 95 PKCS11DriverFile string 96 97 // PKCS11Slot specifies which slot of the pkcs11 device to use 98 PKCS11Slot uint 99 100 // RemoteSigner is the signer used to sign requests using a remote like AAA Service 101 RemoteSigner inter.RequestSigner 102 } 103 104 func New(opts ...Option) (*Pkcs11Security, error) { 105 p := &Pkcs11Security{} 106 107 for _, opt := range opts { 108 err := opt(p) 109 if err != nil { 110 return nil, err 111 } 112 } 113 114 if p.conf == nil { 115 return nil, fmt.Errorf("configuration not given") 116 } 117 118 if p.log == nil { 119 return nil, fmt.Errorf("logger not given") 120 } 121 122 if p.conf.PKCS11DriverFile == "" { 123 return nil, fmt.Errorf("pkcs11: PKCS11DriverFile option is required") 124 } 125 126 if p.pin != nil { 127 err := p.loginToToken() 128 if err != nil { 129 return nil, fmt.Errorf("failed to login to token in New(): %s", err) 130 } 131 } 132 133 return p, p.reinit() 134 } 135 136 func (p *Pkcs11Security) promptForPin() (*string, error) { 137 pin := "" 138 prompt := &survey.Password{ 139 Message: "PIN", 140 } 141 err := survey.AskOne(prompt, &pin) 142 if err != nil { 143 return nil, err 144 } 145 return &pin, nil 146 } 147 148 func (p *Pkcs11Security) loginToToken() error { 149 var err error 150 151 if p.pin == nil { 152 p.pin, err = p.promptForPin() 153 if err != nil { 154 fmt.Printf("err is %s", err.Error()) 155 return err 156 } 157 } 158 159 p.log.Debugf("Attempting to open PKCS11 driver file %s", p.conf.PKCS11DriverFile) 160 161 module, err := p11.OpenModule(p.conf.PKCS11DriverFile) 162 if err != nil { 163 return fmt.Errorf("failed to open PKCS11 driver file %s: %s", p.conf.PKCS11DriverFile, err) 164 } 165 166 p.log.Debug("Attempting to fetch PKCS11 driver slots") 167 168 slots, err := module.Slots() 169 if err != nil { 170 return fmt.Errorf("failed to fetch PKCS11 driver slots: %s", err) 171 } 172 173 var slot *p11.Slot 174 found := false 175 for _, aSlot := range slots { 176 p.log.Debugf("Found slot %d", aSlot.ID()) 177 178 if aSlot.ID() == p.conf.PKCS11Slot { 179 slot = &aSlot 180 found = true 181 break 182 } 183 } 184 if !found { 185 if len(slots) == 1 { 186 slot = &slots[0] 187 } else { 188 return fmt.Errorf("failed to find slot with label %d", p.conf.PKCS11Slot) 189 } 190 } 191 p.log.Debugf("Attempting to open session for selected slot %d", p.conf.PKCS11Slot) 192 193 session, err := slot.OpenSession() 194 if err != nil { 195 return fmt.Errorf("failed to open PKCS11 session: %s", err) 196 } 197 198 p.session = session 199 200 err = session.Login(*p.pin) 201 if err != nil { 202 if !strings.Contains(err.Error(), "CKR_USER_ALREADY_LOGGED_IN") { 203 return fmt.Errorf("failed to login with provided pin: %s", err) 204 } 205 } 206 207 p.log.Debug("Attempting to find private key object") 208 privateKeyObject, err := session.FindObject([]*pkcs11.Attribute{pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PRIVATE_KEY)}) 209 if err != nil { 210 return fmt.Errorf("failed to find private key object: %s", err) 211 } 212 213 p.log.Debug("Attempting to find certificate object") 214 certObject, err := session.FindObject([]*pkcs11.Attribute{pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_CERTIFICATE)}) 215 if err != nil { 216 return fmt.Errorf("failed to find certificate object: %s", err) 217 } 218 219 certData, err := certObject.Value() 220 if err != nil { 221 return fmt.Errorf("failed to get certificate object value: %s", err) 222 } 223 224 parsedCert, err := x509.ParseCertificate(certData) 225 if err != nil { 226 return fmt.Errorf("failed to parse X509 certificate: %s", err) 227 } 228 229 if parsedCert.Subject.CommonName == "" { 230 return fmt.Errorf("cert on token must have valid CommonName") 231 } 232 233 pubKey, ok := parsedCert.PublicKey.(crypto.PublicKey) 234 if !ok { 235 return fmt.Errorf("public key in certificate is not a crypto.PublicKey: %s", err) 236 } 237 238 privateKey := p11.PrivateKey(privateKeyObject) 239 240 p.pKey = &PrivateKey{ 241 PublicKey: pubKey, 242 PrivateKey: &privateKey, 243 } 244 245 p.cert = &tls.Certificate{ 246 Certificate: [][]byte{certData}, 247 Leaf: parsedCert, 248 PrivateKey: p.pKey, 249 } 250 251 return nil 252 } 253 254 // PublicCert is the parsed public certificate 255 func (p *Pkcs11Security) PublicCert() (*x509.Certificate, error) { 256 if p.cert == nil { 257 return nil, fmt.Errorf("not logged in") 258 } 259 260 return p.cert.Leaf, nil 261 } 262 263 func (p *Pkcs11Security) reinit() error { 264 var err error 265 266 fc := filesec.Config{ 267 AllowList: p.conf.AllowList, 268 DisableTLSVerify: p.conf.DisableTLSVerify, 269 PrivilegedUsers: p.conf.PrivilegedUsers, 270 CA: p.conf.CAFile, 271 Certificate: "unused", 272 Identity: "unused", 273 RemoteSigner: p.conf.RemoteSigner, 274 } 275 276 p.fsec, err = filesec.New(filesec.WithConfig(&fc), filesec.WithLog(p.log)) 277 if err != nil { 278 return err 279 } 280 281 return nil 282 } 283 284 func (p *Pkcs11Security) Logout() error { 285 return p.session.Logout() 286 } 287 288 func (p *Pkcs11Security) BackingTechnology() inter.SecurityTechnology { 289 return p.fsec.BackingTechnology() 290 } 291 292 func (p *Pkcs11Security) Provider() string { 293 return "pkcs11" 294 } 295 296 func (p *Pkcs11Security) TokenBytes() ([]byte, error) { 297 return nil, fmt.Errorf("tokens not available for pkcs11 security provider") 298 } 299 300 func (p *Pkcs11Security) Enroll(ctx context.Context, wait time.Duration, cb func(digest string, try int)) error { 301 return fmt.Errorf("pkcs11 security provider does not support enrollment") 302 } 303 304 // RemoteSignRequest signs a choria request against using a remote signer and returns a secure request 305 func (p *Pkcs11Security) RemoteSignRequest(ctx context.Context, str []byte) (signed []byte, err error) { 306 return nil, fmt.Errorf("pkcs11 security provider does not support remote signing requests") 307 } 308 309 func (p *Pkcs11Security) IsRemoteSigning() bool { return false } 310 311 // Validate determines if the node represents a valid SSL configuration 312 func (p *Pkcs11Security) Validate() ([]string, bool) { 313 var errorsList []string 314 315 stat, err := os.Stat(p.conf.CAFile) 316 switch { 317 case os.IsNotExist(err): 318 errorsList = append(errorsList, err.Error()) 319 case !stat.Mode().IsRegular(): 320 errorsList = append(errorsList, fmt.Sprintf("%s is not a regular file", p.conf.CAFile)) 321 } 322 323 if p.pin == nil { 324 p.log.Debug("Attempting to login to token in Validate()") 325 if err := p.loginToToken(); err != nil { 326 errorsList = append(errorsList, fmt.Sprintf("failed to login to token in Validate(): %s", err)) 327 } 328 } 329 330 return errorsList, len(errorsList) == 0 331 } 332 333 // ChecksumBytes calculates a sha256 checksum for data 334 func (p *Pkcs11Security) ChecksumBytes(data []byte) []byte { 335 return p.fsec.ChecksumBytes(data) 336 } 337 338 // SignBytes signs a message using a SHA256 PKCS1v15 protocol 339 func (p *Pkcs11Security) SignBytes(str []byte) ([]byte, error) { 340 hashed := p.ChecksumBytes(str) 341 mechanism := pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS, nil) 342 input := append(hashPrefixes[crypto.SHA256], hashed...) 343 344 output, err := p.pKey.PrivateKey.Sign(*mechanism, input) 345 if err != nil { 346 return nil, err 347 } 348 return output, nil 349 } 350 351 // VerifyByteSignature verify that dat matches signature sig made by the key, if pub cert is empty the active public key will be used 352 func (p *Pkcs11Security) VerifySignatureBytes(dat []byte, sig []byte, public ...[]byte) (should bool, signer string) { 353 if len(public) != 1 { 354 p.log.Errorf("Could not process public data: only single signer public data is supported") 355 return false, "" 356 } 357 358 pubcert := public[0] 359 360 var cert *x509.Certificate 361 var err error 362 363 if len(pubcert) > 0 { 364 pkpem, _ := pem.Decode(pubcert) 365 if pkpem == nil { 366 p.log.Errorf("Could not decode PEM data in public key: invalid pem data") 367 return false, "" 368 } 369 370 cert, err = x509.ParseCertificate(pkpem.Bytes) 371 if err != nil { 372 p.log.Errorf("Could not parse decoded PEM data for public key: %s", err) 373 return false, "" 374 } 375 } else { 376 cert = p.cert.Leaf 377 } 378 379 rsaPublicKey := cert.PublicKey.(*rsa.PublicKey) 380 hashed := p.ChecksumBytes(dat) 381 382 err = rsa.VerifyPKCS1v15(rsaPublicKey, crypto.SHA256, hashed[:], sig) 383 if err != nil { 384 p.log.Errorf("Signature verification failed: %s", err) 385 return false, "" 386 } 387 388 names := []string{cert.Subject.CommonName} 389 names = append(names, cert.DNSNames...) 390 391 if len(names) == 0 { 392 p.log.Errorf("Signature verification failed: no names found in signer certificate") 393 return false, "" 394 } 395 396 p.log.Debugf("Verified signature from %s", strings.Join(names, ", ")) 397 398 return true, names[0] 399 } 400 401 // CallerName creates a choria like caller name in the form of choria=identity 402 func (p *Pkcs11Security) CallerName() string { 403 return fmt.Sprintf("choria=%s", p.cert.Leaf.Subject.CommonName) 404 } 405 406 // CallerIdentity extracts the identity from a choria like caller name in the form of choria=identity 407 func (p *Pkcs11Security) CallerIdentity(caller string) (string, error) { 408 return p.fsec.CallerIdentity(caller) 409 } 410 411 // ShouldAllowCaller verifies the public data 412 func (p *Pkcs11Security) ShouldAllowCaller(name string, callers ...[]byte) (privileged bool, err error) { 413 return p.fsec.ShouldAllowCaller(name, callers...) 414 } 415 416 // VerifyCertificate verifies a certificate is signed with the configured CA and if 417 // name is not "" that it matches the name given 418 func (p *Pkcs11Security) VerifyCertificate(certpem []byte, name string) error { 419 return p.fsec.VerifyCertificate(certpem, name) 420 } 421 422 // publicCertPem retrieves the public certificate for this instance 423 func (p *Pkcs11Security) publicCertPem() (*pem.Block, error) { 424 pb := &pem.Block{ 425 Type: "CERTIFICATE", 426 Bytes: p.cert.Leaf.Raw, 427 } 428 429 return pb, nil 430 } 431 432 // PublicCertBytes retrieves pem data in textual form for the public certificate of the current identity 433 func (p *Pkcs11Security) PublicCertBytes() ([]byte, error) { 434 pemCert, err := p.publicCertPem() 435 if err != nil { 436 return nil, fmt.Errorf("failed to run publicCertPem: %s", err) 437 } 438 var buf bytes.Buffer 439 err = pem.Encode(&buf, pemCert) 440 if err != nil { 441 return nil, fmt.Errorf("failed to run pem.Encode: %s", err) 442 } 443 return buf.Bytes(), nil 444 } 445 446 // Identity determines the choria certname 447 func (p *Pkcs11Security) Identity() string { 448 return p.cert.Leaf.Subject.CommonName 449 } 450 451 // ClientTLSConfig creates a client TLS configuration 452 func (p *Pkcs11Security) ClientTLSConfig() (*tls.Config, error) { 453 return p.TLSConfig() 454 } 455 456 // TLSConfig creates a TLS configuration for use by NATS, HTTPS etc 457 func (p *Pkcs11Security) TLSConfig() (*tls.Config, error) { 458 caCert, err := os.ReadFile(p.conf.CAFile) 459 if err != nil { 460 return nil, err 461 } 462 463 caCertPool := x509.NewCertPool() 464 caCertPool.AppendCertsFromPEM(caCert) 465 466 tlsc := &tls.Config{ 467 MinVersion: tls.VersionTLS12, 468 Certificates: []tls.Certificate{*p.cert}, 469 GetClientCertificate: func(*tls.CertificateRequestInfo) (*tls.Certificate, error) { 470 return p.cert, nil 471 }, 472 ClientCAs: caCertPool, 473 RootCAs: caCertPool, 474 } 475 476 if p.conf.DisableTLSVerify { 477 tlsc.InsecureSkipVerify = true 478 } 479 480 return tlsc, nil 481 } 482 483 // SSLContext creates a SSL context loaded with our certs and ca 484 func (p *Pkcs11Security) SSLContext() (*http.Transport, error) { 485 tlsConfig, err := p.TLSConfig() 486 if err != nil { 487 return nil, err 488 } 489 490 transport := &http.Transport{TLSClientConfig: tlsConfig} 491 492 return transport, nil 493 } 494 495 func (p *Pkcs11Security) HTTPClient(secure bool) (*http.Client, error) { 496 client := &http.Client{} 497 498 if secure { 499 tlsc, err := p.TLSConfig() 500 if err != nil { 501 return nil, fmt.Errorf("pkcs11: could not set up HTTP connection: %s", err) 502 } 503 504 client.Transport = &http.Transport{TLSClientConfig: tlsc} 505 } 506 507 return client, nil 508 } 509 510 func (p *Pkcs11Security) ShouldSignReplies() bool { return false }