From 42ebbe9e7c029dc38d34feac80bff5ae6e8c62c5 Mon Sep 17 00:00:00 2001 From: alegal Date: Mon, 2 Nov 2020 15:19:21 -0800 Subject: [PATCH 1/8] changes to support http and https for CNS --- cns/common/service.go | 2 + cns/configuration/cns_config.json | 6 +- cns/configuration/configuration.go | 4 + cns/service.go | 16 +- cns/service/main.go | 9 + common/listener.go | 92 +++ go.mod | 2 + go.sum | 2 + .../custom_certtostore_windows.go | 609 ++++++++++++++++ server/tls/tls_certificate_retriever.go | 13 + server/tls/tlscertificate_retriever.go | 20 + .../tls/tlscertificate_retriever_disabled.go | 21 + server/tls/tlscertificate_retriever_file.go | 87 +++ .../tls/tlscertificate_retriever_file_test.go | 91 +++ .../tls/tlscertificate_retriever_windows.go | 85 +++ vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go | 77 ++ .../golang.org/x/crypto/pkcs12/bmp-string.go | 50 ++ vendor/golang.org/x/crypto/pkcs12/crypto.go | 131 ++++ vendor/golang.org/x/crypto/pkcs12/errors.go | 23 + .../x/crypto/pkcs12/internal/rc2/rc2.go | 271 +++++++ vendor/golang.org/x/crypto/pkcs12/mac.go | 45 ++ vendor/golang.org/x/crypto/pkcs12/pbkdf.go | 170 +++++ vendor/golang.org/x/crypto/pkcs12/pkcs12.go | 350 +++++++++ vendor/golang.org/x/crypto/pkcs12/safebags.go | 57 ++ .../src/go-pkcs12/.gitattributes | 10 + .../src/go-pkcs12/.gitignore | 2 + .../src/go-pkcs12/LICENSE | 28 + .../src/go-pkcs12/README.md | 38 + .../src/go-pkcs12/bmp-string.go | 50 ++ .../src/go-pkcs12/crypto.go | 265 +++++++ .../src/go-pkcs12/errors.go | 23 + .../src/go-pkcs12/internal/rc2/rc2.go | 271 +++++++ .../software.sslmate.com/src/go-pkcs12/mac.go | 70 ++ .../src/go-pkcs12/pbkdf.go | 177 +++++ .../src/go-pkcs12/pkcs12.go | 686 ++++++++++++++++++ .../src/go-pkcs12/safebags.go | 99 +++ 36 files changed, 3947 insertions(+), 5 deletions(-) create mode 100644 server/tls/customCertToStore/custom_certtostore_windows.go create mode 100644 server/tls/tls_certificate_retriever.go create mode 100644 server/tls/tlscertificate_retriever.go create mode 100644 server/tls/tlscertificate_retriever_disabled.go create mode 100644 server/tls/tlscertificate_retriever_file.go create mode 100644 server/tls/tlscertificate_retriever_file_test.go create mode 100644 server/tls/tlscertificate_retriever_windows.go create mode 100644 vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/bmp-string.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/crypto.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/errors.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/mac.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/pbkdf.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/pkcs12.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/safebags.go create mode 100644 vendor/software.sslmate.com/src/go-pkcs12/.gitattributes create mode 100644 vendor/software.sslmate.com/src/go-pkcs12/.gitignore create mode 100644 vendor/software.sslmate.com/src/go-pkcs12/LICENSE create mode 100644 vendor/software.sslmate.com/src/go-pkcs12/README.md create mode 100644 vendor/software.sslmate.com/src/go-pkcs12/bmp-string.go create mode 100644 vendor/software.sslmate.com/src/go-pkcs12/crypto.go create mode 100644 vendor/software.sslmate.com/src/go-pkcs12/errors.go create mode 100644 vendor/software.sslmate.com/src/go-pkcs12/internal/rc2/rc2.go create mode 100644 vendor/software.sslmate.com/src/go-pkcs12/mac.go create mode 100644 vendor/software.sslmate.com/src/go-pkcs12/pbkdf.go create mode 100644 vendor/software.sslmate.com/src/go-pkcs12/pkcs12.go create mode 100644 vendor/software.sslmate.com/src/go-pkcs12/safebags.go diff --git a/cns/common/service.go b/cns/common/service.go index e17e936b58..ff9918f717 100644 --- a/cns/common/service.go +++ b/cns/common/service.go @@ -5,6 +5,7 @@ package common import ( "errors" + "github.com/Azure/azure-container-networking/server/tls" "github.com/Azure/azure-container-networking/cns/logger" acn "github.com/Azure/azure-container-networking/common" @@ -37,6 +38,7 @@ type ServiceConfig struct { ErrChan chan error Store store.KeyValueStore ChannelMode string + TlsSettings tls.TlsSettings } // NewService creates a new Service object. diff --git a/cns/configuration/cns_config.json b/cns/configuration/cns_config.json index dc4ef96233..6b61507c48 100644 --- a/cns/configuration/cns_config.json +++ b/cns/configuration/cns_config.json @@ -14,5 +14,9 @@ "NodeID": "", "NodeSyncIntervalInSeconds": 30 }, - "ChannelMode": "Direct" + "ChannelMode": "Direct", + "UseHttps" : false, + "TlsSubjectName" : "", + "TlsCertificateFilePath" : "", + "TlsEndpoint" : "localhost:10091" } diff --git a/cns/configuration/configuration.go b/cns/configuration/configuration.go index 7ca927c9e9..66cd07fb14 100644 --- a/cns/configuration/configuration.go +++ b/cns/configuration/configuration.go @@ -20,6 +20,10 @@ type CNSConfig struct { TelemetrySettings TelemetrySettings ManagedSettings ManagedSettings ChannelMode string + UseHttps bool + TlsSubjectName string + TlsCertificateFilePath string + TlsEndpoint string } type TelemetrySettings struct { diff --git a/cns/service.go b/cns/service.go index 15420b1e5b..9b899aaebf 100644 --- a/cns/service.go +++ b/cns/service.go @@ -70,10 +70,18 @@ func (service *Service) Initialize(config *common.ServiceConfig) error { return err } - // Start the listener. - err = listener.Start(config.ErrChan) - if err != nil { - return err + if(config.TlsSettings.TlsEndpoint != ""){ + // Start the listener and HTTP and HTTPS server. + err = listener.StartTLS(config.ErrChan, config.TlsSettings) + if err != nil { + return err + } + } else{ + // Start the listener. + err = listener.Start(config.ErrChan) + if err != nil { + return err + } } config.Listener = listener diff --git a/cns/service/main.go b/cns/service/main.go index f4c757f42d..afc0458a72 100644 --- a/cns/service/main.go +++ b/cns/service/main.go @@ -8,6 +8,7 @@ import ( "context" "encoding/json" "fmt" + localtls "github.com/Azure/azure-container-networking/server/tls" "net/http" "os" "os/signal" @@ -424,6 +425,14 @@ func main() { // Start CNS. if httpRestService != nil { + if(cnsconfig.UseHttps){ + config.TlsSettings = localtls.TlsSettings{ + TlsCertificateSubjectName: cnsconfig.TlsSubjectName, + TlsCertificateFilePath: cnsconfig.TlsCertificateFilePath, + TlsEndpoint: cnsconfig.TlsEndpoint, + } + } + err = httpRestService.Start(&config) if err != nil { logger.Errorf("Failed to start CNS, err:%v.\n", err) diff --git a/common/listener.go b/common/listener.go index 5c9fc5ec9e..cd55ba822a 100644 --- a/common/listener.go +++ b/common/listener.go @@ -4,12 +4,14 @@ package common import ( + "crypto/tls" "encoding/json" "fmt" "net" "net/http" "net/url" "os" + localtls "github.com/Azure/azure-container-networking/server/tls" "github.com/Azure/azure-container-networking/log" ) @@ -22,6 +24,7 @@ type Listener struct { endpoints []string active bool l net.Listener + securelistener net.Listener mux *http.ServeMux } @@ -38,6 +41,90 @@ func NewListener(u *url.URL) (*Listener, error) { return &listener, nil } +func GetTlsConfig(tlsSettings localtls.TlsSettings) (*tls.Config, error){ + tlsCertRetriever, err := localtls.GetTlsCertificateRetriever(tlsSettings) + if err != nil { + return nil, fmt.Errorf("Failed to get certificate retriever", err) + } + + leafCertificate, err := tlsCertRetriever.GetCertificate() + + if err != nil { + return nil, fmt.Errorf("Failed to get certificate", err) + } + + if leafCertificate == nil { + return nil, fmt.Errorf("Certificate retrival returned empty", err) + } + + privateKey, err := tlsCertRetriever.GetPrivateKey() + + if err != nil { + return nil, fmt.Errorf("Failed to get certificate private key", err) + } + + tlsCert := tls.Certificate{ + Certificate: [][]byte{leafCertificate.Raw}, + PrivateKey: privateKey, + Leaf: leafCertificate, + } + + TlsConfig := &tls.Config{ + MaxVersion: tls.VersionTLS12, + MinVersion: tls.VersionTLS12, + Certificates: []tls.Certificate{ + tlsCert, + }, + } + return TlsConfig, nil +} +// Start creates the listener socket and starts the HTTPS server. +func (listener *Listener) StartTLS(errChan chan error, tlsSettings localtls.TlsSettings) error { + var err error + var tlsConfig *tls.Config + + tlsConfig, err = GetTlsConfig(tlsSettings) + + server := http.Server{ + TLSConfig: tlsConfig, + Handler: listener.mux, + } + + // listen on a seperate endpoint for secure tls connections + listener.securelistener, err = net.Listen(listener.protocol, tlsSettings.TlsEndpoint) + + if err != nil { + log.Printf("[Listener] Failed to listen on TlsEndpoint: %+v", err) + return err + } + log.Printf("[Listener] Started listening on tls endpoint %s.", tlsSettings.TlsEndpoint) + + // continue to listen on the normal endpoint for http traffic, this will be supported + // for sometime until partners migrate fully to https + listener.l, err = net.Listen(listener.protocol, listener.localAddress) + + if err != nil { + log.Printf("[Listener] Failed to listen: %+v", err) + return err + } + + log.Printf("[Listener] Started listening on %s.", listener.localAddress) + + // Launch goroutine for servicing https requests + go func() { + errChan <- server.ServeTLS(listener.securelistener, "", "") + }() + + // Launch goroutine for servicing http requests + go func() { + errChan <- http.Serve(listener.l, listener.mux) + }() + + listener.active = true + return nil + +} + // Start creates the listener socket and starts the HTTP server. func (listener *Listener) Start(errChan chan error) error { var err error @@ -75,6 +162,11 @@ func (listener *Listener) Stop() { // Stop servicing requests. listener.l.Close() + if(listener.securelistener != nil){ + // Stop servicing requests on secure listener + listener.securelistener.Close() + } + // Delete the unix socket. if listener.protocol == "unix" { os.Remove(listener.localAddress) diff --git a/go.mod b/go.mod index bc704d7f3e..d18e2bb341 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,7 @@ require ( github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.3.2 go.opencensus.io v0.22.2 // indirect + golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 golang.org/x/net v0.0.0-20191112182307-2180aed22343 // indirect golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect @@ -36,4 +37,5 @@ require ( k8s.io/apimachinery v0.18.2 k8s.io/client-go v0.18.2 sigs.k8s.io/controller-runtime v0.6.0 + software.sslmate.com/src/go-pkcs12 v0.0.0-20201102150903-66718f75db0e ) diff --git a/go.sum b/go.sum index 6ddaeefa35..f483fc67ec 100644 --- a/go.sum +++ b/go.sum @@ -630,3 +630,5 @@ sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +software.sslmate.com/src/go-pkcs12 v0.0.0-20201102150903-66718f75db0e h1:GP6k9CR+zxSHXs+agkNmd7ucZ/YuPY8vRRaKMGIFuWE= +software.sslmate.com/src/go-pkcs12 v0.0.0-20201102150903-66718f75db0e/go.mod h1:/xvNRWUqm0+/ZMiF4EX00vrSCMsE4/NHb+Pt3freEeQ= diff --git a/server/tls/customCertToStore/custom_certtostore_windows.go b/server/tls/customCertToStore/custom_certtostore_windows.go new file mode 100644 index 0000000000..e6e6c55e66 --- /dev/null +++ b/server/tls/customCertToStore/custom_certtostore_windows.go @@ -0,0 +1,609 @@ +// +build windows + +// The below file was borrowed from this repo +// and modified to our needs per the below license permits +// https://github.com/google/certtostore + + +// Copyright 2017 Google 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 customcerttostore + +import ( + "bytes" + "crypto" + "crypto/rsa" + "crypto/x509" + "encoding/binary" + "errors" + "fmt" + "io" + "math/big" + "reflect" + "strings" + "syscall" + "unicode/utf16" + "unsafe" + + "golang.org/x/sys/windows" +) + +const ( + // wincrypt.h constants + acquireCached = 0x1 // CRYPT_ACQUIRE_CACHE_FLAG + acquireSilent = 0x40 // CRYPT_ACQUIRE_SILENT_FLAG + acquireOnlyNCryptKey = 0x40000 // CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG + encodingX509ASN = 1 // X509_ASN_ENCODING + encodingPKCS7 = 65536 // PKCS_7_ASN_ENCODING + certStoreProvSystem = 10 // CERT_STORE_PROV_SYSTEM + certStoreLocalMachine = uint32(certStoreLocalMachineID << compareShift) // CERT_SYSTEM_STORE_LOCAL_MACHINE + certStoreLocalMachineID = 2 // CERT_SYSTEM_STORE_LOCAL_MACHINE_ID + compareNameStrW = 8 // CERT_COMPARE_NAME_STR_A + compareShift = 16 // CERT_COMPARE_SHIFT + ncryptKeySpec = 0xFFFFFFFF // CERT_NCRYPT_KEY_SPEC + infoSubjectFlag = 7 //CERT_INFO_SUBJECT_FLAG + findSubjectStr = compareNameStrW< 8 { + return nil, fmt.Errorf("unsupported public exponent size (%d bits)", header.PublicExpSize*8) + } + + exp := make([]byte, 8) + if n, err := r.Read(exp[8-header.PublicExpSize:]); n != int(header.PublicExpSize) || err != nil { + return nil, fmt.Errorf("failed to read public exponent (%d, %v)", n, err) + } + + mod := make([]byte, header.ModulusSize) + if n, err := r.Read(mod); n != int(header.ModulusSize) || err != nil { + return nil, fmt.Errorf("failed to read modulus (%d, %v)", n, err) + } + + pub := &rsa.PublicKey{ + N: new(big.Int).SetBytes(mod), + E: int(binary.BigEndian.Uint64(exp)), + } + return pub, nil +} + +// Verify interface conformance. +var _ Credential = &Key{} \ No newline at end of file diff --git a/server/tls/tls_certificate_retriever.go b/server/tls/tls_certificate_retriever.go new file mode 100644 index 0000000000..4361c7c207 --- /dev/null +++ b/server/tls/tls_certificate_retriever.go @@ -0,0 +1,13 @@ +package tls + +import ( + "crypto" + "crypto/x509" +) + +// TlsCertificateRetriever is the interface used by +// both windows cert store implementation, linux and cert from file retriever. +type TlsCertificateRetriever interface { + GetCertificate() (*x509.Certificate, error) + GetPrivateKey() (crypto.PrivateKey, error) +} \ No newline at end of file diff --git a/server/tls/tlscertificate_retriever.go b/server/tls/tlscertificate_retriever.go new file mode 100644 index 0000000000..ed6e8fffbd --- /dev/null +++ b/server/tls/tlscertificate_retriever.go @@ -0,0 +1,20 @@ +package tls + +// TlsCertificateSettins - Details related to the TLS certificate. +type TlsSettings struct { + TlsCertificateSubjectName string + TlsCertificateFilePath string + TlsEndpoint string +} + +func GetTlsCertificateRetriever(settings TlsSettings) (TlsCertificateRetriever, error) { + + if settings.TlsCertificateFilePath != ""{ + return NewFileTlsCertificateRetriever(settings) + } + + // if Windows build flag is set, the below will return a windows implementation + // otherwise it will return a error as caller should of already received a + // tls certificate parsed from disk. + return NewTlsCertificateRetriever(settings) +} diff --git a/server/tls/tlscertificate_retriever_disabled.go b/server/tls/tlscertificate_retriever_disabled.go new file mode 100644 index 0000000000..3783b8b4fd --- /dev/null +++ b/server/tls/tlscertificate_retriever_disabled.go @@ -0,0 +1,21 @@ +// +build linux + +// This file is to ensure a implementation for NewTlsCertificateRetriever exists +// so we avoid a compilation error + +package tls + +import ( + "fmt" +) + +// NewTlsCertificateRetriever should not be called +// Linux currently uses tls file certificate retriever +// this indicates the caller has not set the Tls Certificate Path in the server settings +func NewTlsCertificateRetriever(settings TlsCertificateSettings) (TlsCertificateRetriever, error) { + if settings.TlsCertificateFilePath == ""{ + return nil, fmt.Errorf("TLS certificate file path not set") + } + + return nil,fmt.Errorf("Not implemented") +} \ No newline at end of file diff --git a/server/tls/tlscertificate_retriever_file.go b/server/tls/tlscertificate_retriever_file.go new file mode 100644 index 0000000000..57544f23ab --- /dev/null +++ b/server/tls/tlscertificate_retriever_file.go @@ -0,0 +1,87 @@ +// Copyright 2020 Microsoft. All rights reserved. + +package tls + +import ( + "crypto" + "crypto/x509" + "encoding/pem" + "fmt" + "golang.org/x/crypto/pkcs12" + "io/ioutil" +) + +const ( + CertLabel = "CERTIFICATE" + PrivateKeyLabel = "PRIVATE KEY" +) + +type filetlsCertificateRetriever struct { + pemBlock []*pem.Block + settings TlsSettings +} + +// GetCertificate Returns the certificate associated with the pfx +func (fcert *filetlsCertificateRetriever) GetCertificate() (*x509.Certificate, error) { + for _, block := range fcert.pemBlock { + if block.Type == CertLabel { + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, fmt.Errorf("Failed to parse certificate with error %+v", err) + } + if cert.IsCA != true { + return cert, nil + } + } + } + return nil, fmt.Errorf("No Certificate block found") +} + +// GetPrivateKey Returns the private key associated with the pfx +func (fcert *filetlsCertificateRetriever) GetPrivateKey() (crypto.PrivateKey, error) { + + for _, block := range fcert.pemBlock { + if block.Type == PrivateKeyLabel { + pk, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("Could not parse private key %+v", err) + } + return pk, nil + } + } + + return nil, nil +} + +// readPemFile reads a pfx certificate converts it to PEM +func (fcert *filetlsCertificateRetriever) readPemFile() error { + content, err := ioutil.ReadFile(fcert.settings.TlsCertificateFilePath) + if err != nil { + return fmt.Errorf("Error reading file: %+v ", err) + } + pemBlock, err := pkcs12.ToPEM(content, "") + + if err != nil { + return fmt.Errorf("Could not convert pfx to PEM format") + } + + fcert.pemBlock = pemBlock + + return nil +} + +// NewFileTlsCertificateRetriever creates a TlsCertificateRetriever +// NewFileTlsCertificateRetriever depends on the pfx being available +// linux users generally store certificates at /etc/ssl/certs/ +func NewFileTlsCertificateRetriever(settings TlsSettings) (TlsCertificateRetriever, error) { + fileCertStoreRetriever := &filetlsCertificateRetriever{ + settings: settings, + } + + err := fileCertStoreRetriever.readPemFile() + if err != nil { + return nil, fmt.Errorf("Failed to read pfx file with error %+v", err) + } + + return fileCertStoreRetriever, nil +} diff --git a/server/tls/tlscertificate_retriever_file_test.go b/server/tls/tlscertificate_retriever_file_test.go new file mode 100644 index 0000000000..f65c321607 --- /dev/null +++ b/server/tls/tlscertificate_retriever_file_test.go @@ -0,0 +1,91 @@ +// Copyright 2020 Microsoft. All rights reserved. + +package tls + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "fmt" + "io/ioutil" + "math/big" + "os" + "software.sslmate.com/src/go-pkcs12" + "testing" + "time" +) + + +const ( + validFor = time.Duration(365*24*time.Hour) + rsaBits = 2048 + commonName = "dnc.azure.com" +) + + +func TestPfxConsumption(t *testing.T) { + pfxContent := createPfxCertificate(t) + currentDirectory, _ := os.Getwd() + + pfxLocation := fmt.Sprintf("%s/%s.pfx",currentDirectory,commonName) + + ioutil.WriteFile(pfxLocation, pfxContent,0 ) + defer os.Remove(pfxLocation) + + config := TlsSettings{ + TlsCertificateFilePath:pfxLocation, + TlsCertificateSubjectName: commonName, + } + fileCertRetriever, err := NewFileTlsCertificateRetriever(config) + + if err != nil { + t.Fatalf("Failed to open file certificate retriever %+v",err) + } + certificate, err := fileCertRetriever.GetCertificate() + if err != nil { + t.Fatalf("Failed to get certificate %+v",err) + } + + if certificate.Subject.CommonName != commonName{ + t.Fatalf("Recieved a unexpected subject name %+v",err) + } + + _ , err = fileCertRetriever.GetPrivateKey() + if err != nil{ + t.Fatalf("Failed to get private key %+v",err) + } + + +} + +func createPfxCertificate(t *testing.T) []byte { + + priv, err := rsa.GenerateKey(rand.Reader, rsaBits) + if err != nil { + t.Fatalf("Failed to generate private key: %v", err) + } + + template := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + Organization: []string{"Acme Co"}, + CommonName: commonName, + }, + } + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + if err != nil { + t.Fatalf("Failed to create certificate: %v", err) + } + + certificate, err := x509.ParseCertificate(derBytes) + if err != nil { + t.Fatalf("Could not parse certificate") + } + pfxcontent, err := pkcs12.Encode(rand.Reader, priv,certificate,[]*x509.Certificate{},"") + if err != nil { + t.Fatalf("Could not encode certificate to pkcs12") + } + return pfxcontent +} \ No newline at end of file diff --git a/server/tls/tlscertificate_retriever_windows.go b/server/tls/tlscertificate_retriever_windows.go new file mode 100644 index 0000000000..97d4cbc4a0 --- /dev/null +++ b/server/tls/tlscertificate_retriever_windows.go @@ -0,0 +1,85 @@ +// Copyright 2020 Microsoft. All rights reserved. + +package tls + +import ( + "crypto" + "crypto/x509" + "fmt" + certtostore "github.com/Azure/azure-container-networking/server/tls/customCertToStore" + "golang.org/x/sys/windows" +) + +type windowsTlsCertificateRetriever struct{ + certStore *certtostore.WinCertStore + certContext *windows.CertContext + settings TlsSettings +} +// Get certificate reads from the windows cert store +// it depends on the TlsCertificateSubjectName being set +// in the server settings to retrieve the cert +func (wtls *windowsTlsCertificateRetriever) GetCertificate() (*x509.Certificate, error){ + + if wtls.settings.TlsCertificateSubjectName == ""{ + return nil, fmt.Errorf("Certificate subject name is not empty in the settings") + } + cert, certContext, err := wtls.certStore.CertBySubjectName(wtls.settings.TlsCertificateSubjectName) + if err != nil { + return nil, fmt.Errorf("Retrieving certificate from cert store returned error %+v", err) + } + if cert == nil { + return nil,fmt.Errorf("Call to cert store succeeded but gave a empty certificate") + } + if certContext == nil { + return nil, fmt.Errorf("Cert context returned empty") + } + wtls.certContext = certContext + + return cert, nil +} + +// Get private key retrieves the private key from the windows cert store +// it returns a private key that implements crypto.Signer with an RSA based key +func (wtls *windowsTlsCertificateRetriever) GetPrivateKey() (crypto.PrivateKey, error) { + + certKey, err := wtls.certStore.CertKey(wtls.certContext) + + if err != nil { + return nil, fmt.Errorf("Retrieving private key returned error %+v ", err) + } + if certKey == nil { + return nil, fmt.Errorf("Empty private key returned") + } + return certKey, nil +} + +//Open cert store opens the cert store +// its the responsbility of the caller to close the cert store handle +func (wtls *windowsTlsCertificateRetriever) openCertStore() error { + certStore, err := certtostore.OpenWinCertStore(certtostore.ProviderMSSoftware, "0", nil, nil, false, true) + if err != nil { + return fmt.Errorf("Error opening cert store %+v", err) + } + if certStore == nil { + return fmt.Errorf("Error opening cert store %+v", err) + } + wtls.certStore = certStore + return nil +} +// NewWindowsTlsCertificateRetriever creates a TlsCertificateRetriever +// NewFileTlsCertificateRetriever depends on the pfx being available on the windows cert store +func NewTlsCertificateRetriever(settings TlsSettings) (TlsCertificateRetriever, error) { + + windowsCertStoreRetriever := &windowsTlsCertificateRetriever{ + settings: settings, + } + + err := windowsCertStoreRetriever.openCertStore() + + if err != nil { + return nil, fmt.Errorf("Failed to open cert store with %+v:", err) + } + return windowsCertStoreRetriever,nil +} + + diff --git a/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go b/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go new file mode 100644 index 0000000000..593f653008 --- /dev/null +++ b/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go @@ -0,0 +1,77 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package pbkdf2 implements the key derivation function PBKDF2 as defined in RFC +2898 / PKCS #5 v2.0. + +A key derivation function is useful when encrypting data based on a password +or any other not-fully-random data. It uses a pseudorandom function to derive +a secure encryption key based on the password. + +While v2.0 of the standard defines only one pseudorandom function to use, +HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved +Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To +choose, you can pass the `New` functions from the different SHA packages to +pbkdf2.Key. +*/ +package pbkdf2 // import "golang.org/x/crypto/pbkdf2" + +import ( + "crypto/hmac" + "hash" +) + +// Key derives a key from the password, salt and iteration count, returning a +// []byte of length keylen that can be used as cryptographic key. The key is +// derived based on the method described as PBKDF2 with the HMAC variant using +// the supplied hash function. +// +// For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you +// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by +// doing: +// +// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New) +// +// Remember to get a good random salt. At least 8 bytes is recommended by the +// RFC. +// +// Using a higher iteration count will increase the cost of an exhaustive +// search but will also make derivation proportionally slower. +func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte { + prf := hmac.New(h, password) + hashLen := prf.Size() + numBlocks := (keyLen + hashLen - 1) / hashLen + + var buf [4]byte + dk := make([]byte, 0, numBlocks*hashLen) + U := make([]byte, hashLen) + for block := 1; block <= numBlocks; block++ { + // N.B.: || means concatenation, ^ means XOR + // for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter + // U_1 = PRF(password, salt || uint(i)) + prf.Reset() + prf.Write(salt) + buf[0] = byte(block >> 24) + buf[1] = byte(block >> 16) + buf[2] = byte(block >> 8) + buf[3] = byte(block) + prf.Write(buf[:4]) + dk = prf.Sum(dk) + T := dk[len(dk)-hashLen:] + copy(U, T) + + // U_n = PRF(password, U_(n-1)) + for n := 2; n <= iter; n++ { + prf.Reset() + prf.Write(U) + U = U[:0] + U = prf.Sum(U) + for x := range U { + T[x] ^= U[x] + } + } + } + return dk[:keyLen] +} diff --git a/vendor/golang.org/x/crypto/pkcs12/bmp-string.go b/vendor/golang.org/x/crypto/pkcs12/bmp-string.go new file mode 100644 index 0000000000..233b8b62cc --- /dev/null +++ b/vendor/golang.org/x/crypto/pkcs12/bmp-string.go @@ -0,0 +1,50 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "errors" + "unicode/utf16" +) + +// bmpString returns s encoded in UCS-2 with a zero terminator. +func bmpString(s string) ([]byte, error) { + // References: + // https://tools.ietf.org/html/rfc7292#appendix-B.1 + // https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane + // - non-BMP characters are encoded in UTF 16 by using a surrogate pair of 16-bit codes + // EncodeRune returns 0xfffd if the rune does not need special encoding + // - the above RFC provides the info that BMPStrings are NULL terminated. + + ret := make([]byte, 0, 2*len(s)+2) + + for _, r := range s { + if t, _ := utf16.EncodeRune(r); t != 0xfffd { + return nil, errors.New("pkcs12: string contains characters that cannot be encoded in UCS-2") + } + ret = append(ret, byte(r/256), byte(r%256)) + } + + return append(ret, 0, 0), nil +} + +func decodeBMPString(bmpString []byte) (string, error) { + if len(bmpString)%2 != 0 { + return "", errors.New("pkcs12: odd-length BMP string") + } + + // strip terminator if present + if l := len(bmpString); l >= 2 && bmpString[l-1] == 0 && bmpString[l-2] == 0 { + bmpString = bmpString[:l-2] + } + + s := make([]uint16, 0, len(bmpString)/2) + for len(bmpString) > 0 { + s = append(s, uint16(bmpString[0])<<8+uint16(bmpString[1])) + bmpString = bmpString[2:] + } + + return string(utf16.Decode(s)), nil +} diff --git a/vendor/golang.org/x/crypto/pkcs12/crypto.go b/vendor/golang.org/x/crypto/pkcs12/crypto.go new file mode 100644 index 0000000000..484ca51b71 --- /dev/null +++ b/vendor/golang.org/x/crypto/pkcs12/crypto.go @@ -0,0 +1,131 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "bytes" + "crypto/cipher" + "crypto/des" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + + "golang.org/x/crypto/pkcs12/internal/rc2" +) + +var ( + oidPBEWithSHAAnd3KeyTripleDESCBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3}) + oidPBEWithSHAAnd40BitRC2CBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 6}) +) + +// pbeCipher is an abstraction of a PKCS#12 cipher. +type pbeCipher interface { + // create returns a cipher.Block given a key. + create(key []byte) (cipher.Block, error) + // deriveKey returns a key derived from the given password and salt. + deriveKey(salt, password []byte, iterations int) []byte + // deriveKey returns an IV derived from the given password and salt. + deriveIV(salt, password []byte, iterations int) []byte +} + +type shaWithTripleDESCBC struct{} + +func (shaWithTripleDESCBC) create(key []byte) (cipher.Block, error) { + return des.NewTripleDESCipher(key) +} + +func (shaWithTripleDESCBC) deriveKey(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 24) +} + +func (shaWithTripleDESCBC) deriveIV(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8) +} + +type shaWith40BitRC2CBC struct{} + +func (shaWith40BitRC2CBC) create(key []byte) (cipher.Block, error) { + return rc2.New(key, len(key)*8) +} + +func (shaWith40BitRC2CBC) deriveKey(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 5) +} + +func (shaWith40BitRC2CBC) deriveIV(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8) +} + +type pbeParams struct { + Salt []byte + Iterations int +} + +func pbDecrypterFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.BlockMode, int, error) { + var cipherType pbeCipher + + switch { + case algorithm.Algorithm.Equal(oidPBEWithSHAAnd3KeyTripleDESCBC): + cipherType = shaWithTripleDESCBC{} + case algorithm.Algorithm.Equal(oidPBEWithSHAAnd40BitRC2CBC): + cipherType = shaWith40BitRC2CBC{} + default: + return nil, 0, NotImplementedError("algorithm " + algorithm.Algorithm.String() + " is not supported") + } + + var params pbeParams + if err := unmarshal(algorithm.Parameters.FullBytes, ¶ms); err != nil { + return nil, 0, err + } + + key := cipherType.deriveKey(params.Salt, password, params.Iterations) + iv := cipherType.deriveIV(params.Salt, password, params.Iterations) + + block, err := cipherType.create(key) + if err != nil { + return nil, 0, err + } + + return cipher.NewCBCDecrypter(block, iv), block.BlockSize(), nil +} + +func pbDecrypt(info decryptable, password []byte) (decrypted []byte, err error) { + cbc, blockSize, err := pbDecrypterFor(info.Algorithm(), password) + if err != nil { + return nil, err + } + + encrypted := info.Data() + if len(encrypted) == 0 { + return nil, errors.New("pkcs12: empty encrypted data") + } + if len(encrypted)%blockSize != 0 { + return nil, errors.New("pkcs12: input is not a multiple of the block size") + } + decrypted = make([]byte, len(encrypted)) + cbc.CryptBlocks(decrypted, encrypted) + + psLen := int(decrypted[len(decrypted)-1]) + if psLen == 0 || psLen > blockSize { + return nil, ErrDecryption + } + + if len(decrypted) < psLen { + return nil, ErrDecryption + } + ps := decrypted[len(decrypted)-psLen:] + decrypted = decrypted[:len(decrypted)-psLen] + if bytes.Compare(ps, bytes.Repeat([]byte{byte(psLen)}, psLen)) != 0 { + return nil, ErrDecryption + } + + return +} + +// decryptable abstracts an object that contains ciphertext. +type decryptable interface { + Algorithm() pkix.AlgorithmIdentifier + Data() []byte +} diff --git a/vendor/golang.org/x/crypto/pkcs12/errors.go b/vendor/golang.org/x/crypto/pkcs12/errors.go new file mode 100644 index 0000000000..7377ce6fb2 --- /dev/null +++ b/vendor/golang.org/x/crypto/pkcs12/errors.go @@ -0,0 +1,23 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import "errors" + +var ( + // ErrDecryption represents a failure to decrypt the input. + ErrDecryption = errors.New("pkcs12: decryption error, incorrect padding") + + // ErrIncorrectPassword is returned when an incorrect password is detected. + // Usually, P12/PFX data is signed to be able to verify the password. + ErrIncorrectPassword = errors.New("pkcs12: decryption password incorrect") +) + +// NotImplementedError indicates that the input is not currently supported. +type NotImplementedError string + +func (e NotImplementedError) Error() string { + return "pkcs12: " + string(e) +} diff --git a/vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2.go b/vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2.go new file mode 100644 index 0000000000..7499e3fb69 --- /dev/null +++ b/vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2.go @@ -0,0 +1,271 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package rc2 implements the RC2 cipher +/* +https://www.ietf.org/rfc/rfc2268.txt +http://people.csail.mit.edu/rivest/pubs/KRRR98.pdf + +This code is licensed under the MIT license. +*/ +package rc2 + +import ( + "crypto/cipher" + "encoding/binary" +) + +// The rc2 block size in bytes +const BlockSize = 8 + +type rc2Cipher struct { + k [64]uint16 +} + +// New returns a new rc2 cipher with the given key and effective key length t1 +func New(key []byte, t1 int) (cipher.Block, error) { + // TODO(dgryski): error checking for key length + return &rc2Cipher{ + k: expandKey(key, t1), + }, nil +} + +func (*rc2Cipher) BlockSize() int { return BlockSize } + +var piTable = [256]byte{ + 0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d, + 0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2, + 0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32, + 0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82, + 0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc, + 0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26, + 0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03, + 0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7, + 0x08, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a, + 0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec, + 0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39, + 0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31, + 0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9, + 0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9, + 0x0d, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e, + 0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad, +} + +func expandKey(key []byte, t1 int) [64]uint16 { + + l := make([]byte, 128) + copy(l, key) + + var t = len(key) + var t8 = (t1 + 7) / 8 + var tm = byte(255 % uint(1<<(8+uint(t1)-8*uint(t8)))) + + for i := len(key); i < 128; i++ { + l[i] = piTable[l[i-1]+l[uint8(i-t)]] + } + + l[128-t8] = piTable[l[128-t8]&tm] + + for i := 127 - t8; i >= 0; i-- { + l[i] = piTable[l[i+1]^l[i+t8]] + } + + var k [64]uint16 + + for i := range k { + k[i] = uint16(l[2*i]) + uint16(l[2*i+1])*256 + } + + return k +} + +func rotl16(x uint16, b uint) uint16 { + return (x >> (16 - b)) | (x << b) +} + +func (c *rc2Cipher) Encrypt(dst, src []byte) { + + r0 := binary.LittleEndian.Uint16(src[0:]) + r1 := binary.LittleEndian.Uint16(src[2:]) + r2 := binary.LittleEndian.Uint16(src[4:]) + r3 := binary.LittleEndian.Uint16(src[6:]) + + var j int + + for j <= 16 { + // mix r0 + r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) + r0 = rotl16(r0, 1) + j++ + + // mix r1 + r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2) + r1 = rotl16(r1, 2) + j++ + + // mix r2 + r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3) + r2 = rotl16(r2, 3) + j++ + + // mix r3 + r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) + r3 = rotl16(r3, 5) + j++ + + } + + r0 = r0 + c.k[r3&63] + r1 = r1 + c.k[r0&63] + r2 = r2 + c.k[r1&63] + r3 = r3 + c.k[r2&63] + + for j <= 40 { + // mix r0 + r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) + r0 = rotl16(r0, 1) + j++ + + // mix r1 + r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2) + r1 = rotl16(r1, 2) + j++ + + // mix r2 + r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3) + r2 = rotl16(r2, 3) + j++ + + // mix r3 + r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) + r3 = rotl16(r3, 5) + j++ + + } + + r0 = r0 + c.k[r3&63] + r1 = r1 + c.k[r0&63] + r2 = r2 + c.k[r1&63] + r3 = r3 + c.k[r2&63] + + for j <= 60 { + // mix r0 + r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) + r0 = rotl16(r0, 1) + j++ + + // mix r1 + r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2) + r1 = rotl16(r1, 2) + j++ + + // mix r2 + r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3) + r2 = rotl16(r2, 3) + j++ + + // mix r3 + r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) + r3 = rotl16(r3, 5) + j++ + } + + binary.LittleEndian.PutUint16(dst[0:], r0) + binary.LittleEndian.PutUint16(dst[2:], r1) + binary.LittleEndian.PutUint16(dst[4:], r2) + binary.LittleEndian.PutUint16(dst[6:], r3) +} + +func (c *rc2Cipher) Decrypt(dst, src []byte) { + + r0 := binary.LittleEndian.Uint16(src[0:]) + r1 := binary.LittleEndian.Uint16(src[2:]) + r2 := binary.LittleEndian.Uint16(src[4:]) + r3 := binary.LittleEndian.Uint16(src[6:]) + + j := 63 + + for j >= 44 { + // unmix r3 + r3 = rotl16(r3, 16-5) + r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0) + j-- + + // unmix r2 + r2 = rotl16(r2, 16-3) + r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3) + j-- + + // unmix r1 + r1 = rotl16(r1, 16-2) + r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2) + j-- + + // unmix r0 + r0 = rotl16(r0, 16-1) + r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) + j-- + } + + r3 = r3 - c.k[r2&63] + r2 = r2 - c.k[r1&63] + r1 = r1 - c.k[r0&63] + r0 = r0 - c.k[r3&63] + + for j >= 20 { + // unmix r3 + r3 = rotl16(r3, 16-5) + r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0) + j-- + + // unmix r2 + r2 = rotl16(r2, 16-3) + r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3) + j-- + + // unmix r1 + r1 = rotl16(r1, 16-2) + r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2) + j-- + + // unmix r0 + r0 = rotl16(r0, 16-1) + r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) + j-- + + } + + r3 = r3 - c.k[r2&63] + r2 = r2 - c.k[r1&63] + r1 = r1 - c.k[r0&63] + r0 = r0 - c.k[r3&63] + + for j >= 0 { + // unmix r3 + r3 = rotl16(r3, 16-5) + r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0) + j-- + + // unmix r2 + r2 = rotl16(r2, 16-3) + r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3) + j-- + + // unmix r1 + r1 = rotl16(r1, 16-2) + r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2) + j-- + + // unmix r0 + r0 = rotl16(r0, 16-1) + r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) + j-- + + } + + binary.LittleEndian.PutUint16(dst[0:], r0) + binary.LittleEndian.PutUint16(dst[2:], r1) + binary.LittleEndian.PutUint16(dst[4:], r2) + binary.LittleEndian.PutUint16(dst[6:], r3) +} diff --git a/vendor/golang.org/x/crypto/pkcs12/mac.go b/vendor/golang.org/x/crypto/pkcs12/mac.go new file mode 100644 index 0000000000..5f38aa7de8 --- /dev/null +++ b/vendor/golang.org/x/crypto/pkcs12/mac.go @@ -0,0 +1,45 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "crypto/hmac" + "crypto/sha1" + "crypto/x509/pkix" + "encoding/asn1" +) + +type macData struct { + Mac digestInfo + MacSalt []byte + Iterations int `asn1:"optional,default:1"` +} + +// from PKCS#7: +type digestInfo struct { + Algorithm pkix.AlgorithmIdentifier + Digest []byte +} + +var ( + oidSHA1 = asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}) +) + +func verifyMac(macData *macData, message, password []byte) error { + if !macData.Mac.Algorithm.Algorithm.Equal(oidSHA1) { + return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String()) + } + + key := pbkdf(sha1Sum, 20, 64, macData.MacSalt, password, macData.Iterations, 3, 20) + + mac := hmac.New(sha1.New, key) + mac.Write(message) + expectedMAC := mac.Sum(nil) + + if !hmac.Equal(macData.Mac.Digest, expectedMAC) { + return ErrIncorrectPassword + } + return nil +} diff --git a/vendor/golang.org/x/crypto/pkcs12/pbkdf.go b/vendor/golang.org/x/crypto/pkcs12/pbkdf.go new file mode 100644 index 0000000000..5c419d41e3 --- /dev/null +++ b/vendor/golang.org/x/crypto/pkcs12/pbkdf.go @@ -0,0 +1,170 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "bytes" + "crypto/sha1" + "math/big" +) + +var ( + one = big.NewInt(1) +) + +// sha1Sum returns the SHA-1 hash of in. +func sha1Sum(in []byte) []byte { + sum := sha1.Sum(in) + return sum[:] +} + +// fillWithRepeats returns v*ceiling(len(pattern) / v) bytes consisting of +// repeats of pattern. +func fillWithRepeats(pattern []byte, v int) []byte { + if len(pattern) == 0 { + return nil + } + outputLen := v * ((len(pattern) + v - 1) / v) + return bytes.Repeat(pattern, (outputLen+len(pattern)-1)/len(pattern))[:outputLen] +} + +func pbkdf(hash func([]byte) []byte, u, v int, salt, password []byte, r int, ID byte, size int) (key []byte) { + // implementation of https://tools.ietf.org/html/rfc7292#appendix-B.2 , RFC text verbatim in comments + + // Let H be a hash function built around a compression function f: + + // Z_2^u x Z_2^v -> Z_2^u + + // (that is, H has a chaining variable and output of length u bits, and + // the message input to the compression function of H is v bits). The + // values for u and v are as follows: + + // HASH FUNCTION VALUE u VALUE v + // MD2, MD5 128 512 + // SHA-1 160 512 + // SHA-224 224 512 + // SHA-256 256 512 + // SHA-384 384 1024 + // SHA-512 512 1024 + // SHA-512/224 224 1024 + // SHA-512/256 256 1024 + + // Furthermore, let r be the iteration count. + + // We assume here that u and v are both multiples of 8, as are the + // lengths of the password and salt strings (which we denote by p and s, + // respectively) and the number n of pseudorandom bits required. In + // addition, u and v are of course non-zero. + + // For information on security considerations for MD5 [19], see [25] and + // [1], and on those for MD2, see [18]. + + // The following procedure can be used to produce pseudorandom bits for + // a particular "purpose" that is identified by a byte called "ID". + // This standard specifies 3 different values for the ID byte: + + // 1. If ID=1, then the pseudorandom bits being produced are to be used + // as key material for performing encryption or decryption. + + // 2. If ID=2, then the pseudorandom bits being produced are to be used + // as an IV (Initial Value) for encryption or decryption. + + // 3. If ID=3, then the pseudorandom bits being produced are to be used + // as an integrity key for MACing. + + // 1. Construct a string, D (the "diversifier"), by concatenating v/8 + // copies of ID. + var D []byte + for i := 0; i < v; i++ { + D = append(D, ID) + } + + // 2. Concatenate copies of the salt together to create a string S of + // length v(ceiling(s/v)) bits (the final copy of the salt may be + // truncated to create S). Note that if the salt is the empty + // string, then so is S. + + S := fillWithRepeats(salt, v) + + // 3. Concatenate copies of the password together to create a string P + // of length v(ceiling(p/v)) bits (the final copy of the password + // may be truncated to create P). Note that if the password is the + // empty string, then so is P. + + P := fillWithRepeats(password, v) + + // 4. Set I=S||P to be the concatenation of S and P. + I := append(S, P...) + + // 5. Set c=ceiling(n/u). + c := (size + u - 1) / u + + // 6. For i=1, 2, ..., c, do the following: + A := make([]byte, c*20) + var IjBuf []byte + for i := 0; i < c; i++ { + // A. Set A2=H^r(D||I). (i.e., the r-th hash of D||1, + // H(H(H(... H(D||I)))) + Ai := hash(append(D, I...)) + for j := 1; j < r; j++ { + Ai = hash(Ai) + } + copy(A[i*20:], Ai[:]) + + if i < c-1 { // skip on last iteration + // B. Concatenate copies of Ai to create a string B of length v + // bits (the final copy of Ai may be truncated to create B). + var B []byte + for len(B) < v { + B = append(B, Ai[:]...) + } + B = B[:v] + + // C. Treating I as a concatenation I_0, I_1, ..., I_(k-1) of v-bit + // blocks, where k=ceiling(s/v)+ceiling(p/v), modify I by + // setting I_j=(I_j+B+1) mod 2^v for each j. + { + Bbi := new(big.Int).SetBytes(B) + Ij := new(big.Int) + + for j := 0; j < len(I)/v; j++ { + Ij.SetBytes(I[j*v : (j+1)*v]) + Ij.Add(Ij, Bbi) + Ij.Add(Ij, one) + Ijb := Ij.Bytes() + // We expect Ijb to be exactly v bytes, + // if it is longer or shorter we must + // adjust it accordingly. + if len(Ijb) > v { + Ijb = Ijb[len(Ijb)-v:] + } + if len(Ijb) < v { + if IjBuf == nil { + IjBuf = make([]byte, v) + } + bytesShort := v - len(Ijb) + for i := 0; i < bytesShort; i++ { + IjBuf[i] = 0 + } + copy(IjBuf[bytesShort:], Ijb) + Ijb = IjBuf + } + copy(I[j*v:(j+1)*v], Ijb) + } + } + } + } + // 7. Concatenate A_1, A_2, ..., A_c together to form a pseudorandom + // bit string, A. + + // 8. Use the first n bits of A as the output of this entire process. + return A[:size] + + // If the above process is being used to generate a DES key, the process + // should be used to create 64 random bits, and the key's parity bits + // should be set after the 64 bits have been produced. Similar concerns + // hold for 2-key and 3-key triple-DES keys, for CDMF keys, and for any + // similar keys with parity bits "built into them". +} diff --git a/vendor/golang.org/x/crypto/pkcs12/pkcs12.go b/vendor/golang.org/x/crypto/pkcs12/pkcs12.go new file mode 100644 index 0000000000..3e2ce69407 --- /dev/null +++ b/vendor/golang.org/x/crypto/pkcs12/pkcs12.go @@ -0,0 +1,350 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package pkcs12 implements some of PKCS#12. +// +// This implementation is distilled from https://tools.ietf.org/html/rfc7292 +// and referenced documents. It is intended for decoding P12/PFX-stored +// certificates and keys for use with the crypto/tls package. +// +// This package is frozen. If it's missing functionality you need, consider +// an alternative like software.sslmate.com/src/go-pkcs12. +package pkcs12 + +import ( + "crypto/ecdsa" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/hex" + "encoding/pem" + "errors" +) + +var ( + oidDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 1}) + oidEncryptedDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 6}) + + oidFriendlyName = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 20}) + oidLocalKeyID = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 21}) + oidMicrosoftCSPName = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 311, 17, 1}) +) + +type pfxPdu struct { + Version int + AuthSafe contentInfo + MacData macData `asn1:"optional"` +} + +type contentInfo struct { + ContentType asn1.ObjectIdentifier + Content asn1.RawValue `asn1:"tag:0,explicit,optional"` +} + +type encryptedData struct { + Version int + EncryptedContentInfo encryptedContentInfo +} + +type encryptedContentInfo struct { + ContentType asn1.ObjectIdentifier + ContentEncryptionAlgorithm pkix.AlgorithmIdentifier + EncryptedContent []byte `asn1:"tag:0,optional"` +} + +func (i encryptedContentInfo) Algorithm() pkix.AlgorithmIdentifier { + return i.ContentEncryptionAlgorithm +} + +func (i encryptedContentInfo) Data() []byte { return i.EncryptedContent } + +type safeBag struct { + Id asn1.ObjectIdentifier + Value asn1.RawValue `asn1:"tag:0,explicit"` + Attributes []pkcs12Attribute `asn1:"set,optional"` +} + +type pkcs12Attribute struct { + Id asn1.ObjectIdentifier + Value asn1.RawValue `asn1:"set"` +} + +type encryptedPrivateKeyInfo struct { + AlgorithmIdentifier pkix.AlgorithmIdentifier + EncryptedData []byte +} + +func (i encryptedPrivateKeyInfo) Algorithm() pkix.AlgorithmIdentifier { + return i.AlgorithmIdentifier +} + +func (i encryptedPrivateKeyInfo) Data() []byte { + return i.EncryptedData +} + +// PEM block types +const ( + certificateType = "CERTIFICATE" + privateKeyType = "PRIVATE KEY" +) + +// unmarshal calls asn1.Unmarshal, but also returns an error if there is any +// trailing data after unmarshaling. +func unmarshal(in []byte, out interface{}) error { + trailing, err := asn1.Unmarshal(in, out) + if err != nil { + return err + } + if len(trailing) != 0 { + return errors.New("pkcs12: trailing data found") + } + return nil +} + +// ToPEM converts all "safe bags" contained in pfxData to PEM blocks. +func ToPEM(pfxData []byte, password string) ([]*pem.Block, error) { + encodedPassword, err := bmpString(password) + if err != nil { + return nil, ErrIncorrectPassword + } + + bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword) + + if err != nil { + return nil, err + } + + blocks := make([]*pem.Block, 0, len(bags)) + for _, bag := range bags { + block, err := convertBag(&bag, encodedPassword) + if err != nil { + return nil, err + } + blocks = append(blocks, block) + } + + return blocks, nil +} + +func convertBag(bag *safeBag, password []byte) (*pem.Block, error) { + block := &pem.Block{ + Headers: make(map[string]string), + } + + for _, attribute := range bag.Attributes { + k, v, err := convertAttribute(&attribute) + if err != nil { + return nil, err + } + block.Headers[k] = v + } + + switch { + case bag.Id.Equal(oidCertBag): + block.Type = certificateType + certsData, err := decodeCertBag(bag.Value.Bytes) + if err != nil { + return nil, err + } + block.Bytes = certsData + case bag.Id.Equal(oidPKCS8ShroundedKeyBag): + block.Type = privateKeyType + + key, err := decodePkcs8ShroudedKeyBag(bag.Value.Bytes, password) + if err != nil { + return nil, err + } + + switch key := key.(type) { + case *rsa.PrivateKey: + block.Bytes = x509.MarshalPKCS1PrivateKey(key) + case *ecdsa.PrivateKey: + block.Bytes, err = x509.MarshalECPrivateKey(key) + if err != nil { + return nil, err + } + default: + return nil, errors.New("found unknown private key type in PKCS#8 wrapping") + } + default: + return nil, errors.New("don't know how to convert a safe bag of type " + bag.Id.String()) + } + return block, nil +} + +func convertAttribute(attribute *pkcs12Attribute) (key, value string, err error) { + isString := false + + switch { + case attribute.Id.Equal(oidFriendlyName): + key = "friendlyName" + isString = true + case attribute.Id.Equal(oidLocalKeyID): + key = "localKeyId" + case attribute.Id.Equal(oidMicrosoftCSPName): + // This key is chosen to match OpenSSL. + key = "Microsoft CSP Name" + isString = true + default: + return "", "", errors.New("pkcs12: unknown attribute with OID " + attribute.Id.String()) + } + + if isString { + if err := unmarshal(attribute.Value.Bytes, &attribute.Value); err != nil { + return "", "", err + } + if value, err = decodeBMPString(attribute.Value.Bytes); err != nil { + return "", "", err + } + } else { + var id []byte + if err := unmarshal(attribute.Value.Bytes, &id); err != nil { + return "", "", err + } + value = hex.EncodeToString(id) + } + + return key, value, nil +} + +// Decode extracts a certificate and private key from pfxData. This function +// assumes that there is only one certificate and only one private key in the +// pfxData; if there are more use ToPEM instead. +func Decode(pfxData []byte, password string) (privateKey interface{}, certificate *x509.Certificate, err error) { + encodedPassword, err := bmpString(password) + if err != nil { + return nil, nil, err + } + + bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword) + if err != nil { + return nil, nil, err + } + + if len(bags) != 2 { + err = errors.New("pkcs12: expected exactly two safe bags in the PFX PDU") + return + } + + for _, bag := range bags { + switch { + case bag.Id.Equal(oidCertBag): + if certificate != nil { + err = errors.New("pkcs12: expected exactly one certificate bag") + } + + certsData, err := decodeCertBag(bag.Value.Bytes) + if err != nil { + return nil, nil, err + } + certs, err := x509.ParseCertificates(certsData) + if err != nil { + return nil, nil, err + } + if len(certs) != 1 { + err = errors.New("pkcs12: expected exactly one certificate in the certBag") + return nil, nil, err + } + certificate = certs[0] + + case bag.Id.Equal(oidPKCS8ShroundedKeyBag): + if privateKey != nil { + err = errors.New("pkcs12: expected exactly one key bag") + return nil, nil, err + } + + if privateKey, err = decodePkcs8ShroudedKeyBag(bag.Value.Bytes, encodedPassword); err != nil { + return nil, nil, err + } + } + } + + if certificate == nil { + return nil, nil, errors.New("pkcs12: certificate missing") + } + if privateKey == nil { + return nil, nil, errors.New("pkcs12: private key missing") + } + + return +} + +func getSafeContents(p12Data, password []byte) (bags []safeBag, updatedPassword []byte, err error) { + pfx := new(pfxPdu) + if err := unmarshal(p12Data, pfx); err != nil { + return nil, nil, errors.New("pkcs12: error reading P12 data: " + err.Error()) + } + + if pfx.Version != 3 { + return nil, nil, NotImplementedError("can only decode v3 PFX PDU's") + } + + if !pfx.AuthSafe.ContentType.Equal(oidDataContentType) { + return nil, nil, NotImplementedError("only password-protected PFX is implemented") + } + + // unmarshal the explicit bytes in the content for type 'data' + if err := unmarshal(pfx.AuthSafe.Content.Bytes, &pfx.AuthSafe.Content); err != nil { + return nil, nil, err + } + + if len(pfx.MacData.Mac.Algorithm.Algorithm) == 0 { + return nil, nil, errors.New("pkcs12: no MAC in data") + } + + if err := verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password); err != nil { + if err == ErrIncorrectPassword && len(password) == 2 && password[0] == 0 && password[1] == 0 { + // some implementations use an empty byte array + // for the empty string password try one more + // time with empty-empty password + password = nil + err = verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password) + } + if err != nil { + return nil, nil, err + } + } + + var authenticatedSafe []contentInfo + if err := unmarshal(pfx.AuthSafe.Content.Bytes, &authenticatedSafe); err != nil { + return nil, nil, err + } + + if len(authenticatedSafe) != 2 { + return nil, nil, NotImplementedError("expected exactly two items in the authenticated safe") + } + + for _, ci := range authenticatedSafe { + var data []byte + + switch { + case ci.ContentType.Equal(oidDataContentType): + if err := unmarshal(ci.Content.Bytes, &data); err != nil { + return nil, nil, err + } + case ci.ContentType.Equal(oidEncryptedDataContentType): + var encryptedData encryptedData + if err := unmarshal(ci.Content.Bytes, &encryptedData); err != nil { + return nil, nil, err + } + if encryptedData.Version != 0 { + return nil, nil, NotImplementedError("only version 0 of EncryptedData is supported") + } + if data, err = pbDecrypt(encryptedData.EncryptedContentInfo, password); err != nil { + return nil, nil, err + } + default: + return nil, nil, NotImplementedError("only data and encryptedData content types are supported in authenticated safe") + } + + var safeContents []safeBag + if err := unmarshal(data, &safeContents); err != nil { + return nil, nil, err + } + bags = append(bags, safeContents...) + } + + return bags, password, nil +} diff --git a/vendor/golang.org/x/crypto/pkcs12/safebags.go b/vendor/golang.org/x/crypto/pkcs12/safebags.go new file mode 100644 index 0000000000..def1f7b98d --- /dev/null +++ b/vendor/golang.org/x/crypto/pkcs12/safebags.go @@ -0,0 +1,57 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "crypto/x509" + "encoding/asn1" + "errors" +) + +var ( + // see https://tools.ietf.org/html/rfc7292#appendix-D + oidCertTypeX509Certificate = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 22, 1}) + oidPKCS8ShroundedKeyBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 2}) + oidCertBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 3}) +) + +type certBag struct { + Id asn1.ObjectIdentifier + Data []byte `asn1:"tag:0,explicit"` +} + +func decodePkcs8ShroudedKeyBag(asn1Data, password []byte) (privateKey interface{}, err error) { + pkinfo := new(encryptedPrivateKeyInfo) + if err = unmarshal(asn1Data, pkinfo); err != nil { + return nil, errors.New("pkcs12: error decoding PKCS#8 shrouded key bag: " + err.Error()) + } + + pkData, err := pbDecrypt(pkinfo, password) + if err != nil { + return nil, errors.New("pkcs12: error decrypting PKCS#8 shrouded key bag: " + err.Error()) + } + + ret := new(asn1.RawValue) + if err = unmarshal(pkData, ret); err != nil { + return nil, errors.New("pkcs12: error unmarshaling decrypted private key: " + err.Error()) + } + + if privateKey, err = x509.ParsePKCS8PrivateKey(pkData); err != nil { + return nil, errors.New("pkcs12: error parsing PKCS#8 private key: " + err.Error()) + } + + return privateKey, nil +} + +func decodeCertBag(asn1Data []byte) (x509Certificates []byte, err error) { + bag := new(certBag) + if err := unmarshal(asn1Data, bag); err != nil { + return nil, errors.New("pkcs12: error decoding cert bag: " + err.Error()) + } + if !bag.Id.Equal(oidCertTypeX509Certificate) { + return nil, NotImplementedError("only X509 certificates are supported") + } + return bag.Data, nil +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/.gitattributes b/vendor/software.sslmate.com/src/go-pkcs12/.gitattributes new file mode 100644 index 0000000000..d2f212e5da --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/.gitattributes @@ -0,0 +1,10 @@ +# Treat all files in this repo as binary, with no git magic updating +# line endings. Windows users contributing to Go will need to use a +# modern version of git and editors capable of LF line endings. +# +# We'll prevent accidental CRLF line endings from entering the repo +# via the git-review gofmt checks. +# +# See golang.org/issue/9281 + +* -text diff --git a/vendor/software.sslmate.com/src/go-pkcs12/.gitignore b/vendor/software.sslmate.com/src/go-pkcs12/.gitignore new file mode 100644 index 0000000000..8339fd61d3 --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/.gitignore @@ -0,0 +1,2 @@ +# Add no patterns to .hgignore except for files generated by the build. +last-change diff --git a/vendor/software.sslmate.com/src/go-pkcs12/LICENSE b/vendor/software.sslmate.com/src/go-pkcs12/LICENSE new file mode 100644 index 0000000000..bcecd3d970 --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2015, 2018, 2019 Opsmate, Inc. All rights reserved. +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/software.sslmate.com/src/go-pkcs12/README.md b/vendor/software.sslmate.com/src/go-pkcs12/README.md new file mode 100644 index 0000000000..011af5fb31 --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/README.md @@ -0,0 +1,38 @@ +# package pkcs12 + +[![GoDoc](https://godoc.org/software.sslmate.com/src/go-pkcs12?status.svg)](https://godoc.org/software.sslmate.com/src/go-pkcs12) + + import "software.sslmate.com/src/go-pkcs12" + +Package pkcs12 implements some of PKCS#12 (also known as P12 or PFX). +It is intended for decoding DER-encoded P12/PFX files for use with the `crypto/tls` +package, and for encoding P12/PFX files for use by legacy applications which +do not support newer formats. Since PKCS#12 uses weak encryption +primitives, it SHOULD NOT be used for new applications. + +Note that only DER-encoded PKCS#12 files are supported, even though PKCS#12 +allows BER encoding. This is becuase encoding/asn1 only supports DER. + +This package is forked from `golang.org/x/crypto/pkcs12`, which is frozen. +The implementation is distilled from https://tools.ietf.org/html/rfc7292 +and referenced documents. + +This repository holds supplementary Go cryptography libraries. + +## Import Path + +Note that although the source code and issue tracker for this package are hosted +on GitHub, the import path is: + + software.sslmate.com/src/go-pkcs12 + +Please be sure to use this path when you `go get` and `import` this package. + +## Download/Install + +The easiest way to install is to run `go get -u software.sslmate.com/src/go-pkcs12`. You +can also manually git clone the repository to `$GOPATH/src/software.sslmate.com/src/go-pkcs12`. + +## Report Issues / Send Patches + +Open an issue or PR at https://github.com/SSLMate/go-pkcs12 diff --git a/vendor/software.sslmate.com/src/go-pkcs12/bmp-string.go b/vendor/software.sslmate.com/src/go-pkcs12/bmp-string.go new file mode 100644 index 0000000000..233b8b62cc --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/bmp-string.go @@ -0,0 +1,50 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "errors" + "unicode/utf16" +) + +// bmpString returns s encoded in UCS-2 with a zero terminator. +func bmpString(s string) ([]byte, error) { + // References: + // https://tools.ietf.org/html/rfc7292#appendix-B.1 + // https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane + // - non-BMP characters are encoded in UTF 16 by using a surrogate pair of 16-bit codes + // EncodeRune returns 0xfffd if the rune does not need special encoding + // - the above RFC provides the info that BMPStrings are NULL terminated. + + ret := make([]byte, 0, 2*len(s)+2) + + for _, r := range s { + if t, _ := utf16.EncodeRune(r); t != 0xfffd { + return nil, errors.New("pkcs12: string contains characters that cannot be encoded in UCS-2") + } + ret = append(ret, byte(r/256), byte(r%256)) + } + + return append(ret, 0, 0), nil +} + +func decodeBMPString(bmpString []byte) (string, error) { + if len(bmpString)%2 != 0 { + return "", errors.New("pkcs12: odd-length BMP string") + } + + // strip terminator if present + if l := len(bmpString); l >= 2 && bmpString[l-1] == 0 && bmpString[l-2] == 0 { + bmpString = bmpString[:l-2] + } + + s := make([]uint16, 0, len(bmpString)/2) + for len(bmpString) > 0 { + s = append(s, uint16(bmpString[0])<<8+uint16(bmpString[1])) + bmpString = bmpString[2:] + } + + return string(utf16.Decode(s)), nil +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/crypto.go b/vendor/software.sslmate.com/src/go-pkcs12/crypto.go new file mode 100644 index 0000000000..70425f8307 --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/crypto.go @@ -0,0 +1,265 @@ +// Copyright 2015, 2018, 2019 Opsmate, Inc. All rights reserved. +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/des" + "crypto/sha1" + "crypto/sha256" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "hash" + + "golang.org/x/crypto/pbkdf2" + "software.sslmate.com/src/go-pkcs12/internal/rc2" +) + +var ( + oidPBEWithSHAAnd3KeyTripleDESCBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3}) + oidPBEWithSHAAnd40BitRC2CBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 6}) + oidPBES2 = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 5, 13}) + oidPBKDF2 = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 5, 12}) + oidHmacWithSHA1 = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 2, 7}) + oidHmacWithSHA256 = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 2, 9}) + oidAES256CBC = asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 1, 42}) +) + +// pbeCipher is an abstraction of a PKCS#12 cipher. +type pbeCipher interface { + // create returns a cipher.Block given a key. + create(key []byte) (cipher.Block, error) + // deriveKey returns a key derived from the given password and salt. + deriveKey(salt, password []byte, iterations int) []byte + // deriveKey returns an IV derived from the given password and salt. + deriveIV(salt, password []byte, iterations int) []byte +} + +type shaWithTripleDESCBC struct{} + +func (shaWithTripleDESCBC) create(key []byte) (cipher.Block, error) { + return des.NewTripleDESCipher(key) +} + +func (shaWithTripleDESCBC) deriveKey(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 24) +} + +func (shaWithTripleDESCBC) deriveIV(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8) +} + +type shaWith40BitRC2CBC struct{} + +func (shaWith40BitRC2CBC) create(key []byte) (cipher.Block, error) { + return rc2.New(key, len(key)*8) +} + +func (shaWith40BitRC2CBC) deriveKey(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 5) +} + +func (shaWith40BitRC2CBC) deriveIV(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8) +} + +type pbeParams struct { + Salt []byte + Iterations int +} + +func pbeCipherFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.Block, []byte, error) { + var cipherType pbeCipher + + switch { + case algorithm.Algorithm.Equal(oidPBEWithSHAAnd3KeyTripleDESCBC): + cipherType = shaWithTripleDESCBC{} + case algorithm.Algorithm.Equal(oidPBEWithSHAAnd40BitRC2CBC): + cipherType = shaWith40BitRC2CBC{} + case algorithm.Algorithm.Equal(oidPBES2): + // rfc7292#appendix-B.1 (the original PKCS#12 PBE) requires passwords formatted as BMPStrings. + // However, rfc8018#section-3 recommends that the password for PBES2 follow ASCII or UTF-8. + // This is also what Windows expects. + // Therefore, we convert the password to UTF-8. + originalPassword, err := decodeBMPString(password) + if err != nil { + return nil, nil, err + } + utf8Password := []byte(originalPassword) + return pbes2CipherFor(algorithm, utf8Password) + default: + return nil, nil, NotImplementedError("algorithm " + algorithm.Algorithm.String() + " is not supported") + } + + var params pbeParams + if err := unmarshal(algorithm.Parameters.FullBytes, ¶ms); err != nil { + return nil, nil, err + } + + key := cipherType.deriveKey(params.Salt, password, params.Iterations) + iv := cipherType.deriveIV(params.Salt, password, params.Iterations) + + block, err := cipherType.create(key) + if err != nil { + return nil, nil, err + } + + return block, iv, nil +} + +func pbDecrypterFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.BlockMode, int, error) { + block, iv, err := pbeCipherFor(algorithm, password) + if err != nil { + return nil, 0, err + } + + return cipher.NewCBCDecrypter(block, iv), block.BlockSize(), nil +} + +func pbDecrypt(info decryptable, password []byte) (decrypted []byte, err error) { + cbc, blockSize, err := pbDecrypterFor(info.Algorithm(), password) + if err != nil { + return nil, err + } + + encrypted := info.Data() + if len(encrypted) == 0 { + return nil, errors.New("pkcs12: empty encrypted data") + } + if len(encrypted)%blockSize != 0 { + return nil, errors.New("pkcs12: input is not a multiple of the block size") + } + decrypted = make([]byte, len(encrypted)) + cbc.CryptBlocks(decrypted, encrypted) + + psLen := int(decrypted[len(decrypted)-1]) + if psLen == 0 || psLen > blockSize { + return nil, ErrDecryption + } + + if len(decrypted) < psLen { + return nil, ErrDecryption + } + ps := decrypted[len(decrypted)-psLen:] + decrypted = decrypted[:len(decrypted)-psLen] + if bytes.Compare(ps, bytes.Repeat([]byte{byte(psLen)}, psLen)) != 0 { + return nil, ErrDecryption + } + + return +} + +// PBES2-params ::= SEQUENCE { +// keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}}, +// encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} +// } +type pbes2Params struct { + Kdf pkix.AlgorithmIdentifier + EncryptionScheme pkix.AlgorithmIdentifier +} + +// PBKDF2-params ::= SEQUENCE { +// salt CHOICE { +// specified OCTET STRING, +// otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}} +// }, +// iterationCount INTEGER (1..MAX), +// keyLength INTEGER (1..MAX) OPTIONAL, +// prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT +// algid-hmacWithSHA1 +// } +type pbkdf2Params struct { + Salt asn1.RawValue + Iterations int + KeyLength int `asn1:"optional"` + Prf pkix.AlgorithmIdentifier +} + +func pbes2CipherFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.Block, []byte, error) { + var params pbes2Params + if err := unmarshal(algorithm.Parameters.FullBytes, ¶ms); err != nil { + return nil, nil, err + } + + if !params.Kdf.Algorithm.Equal(oidPBKDF2) { + return nil, nil, NotImplementedError("kdf algorithm " + params.Kdf.Algorithm.String() + " is not supported") + } + + var kdfParams pbkdf2Params + if err := unmarshal(params.Kdf.Parameters.FullBytes, &kdfParams); err != nil { + return nil, nil, err + } + if kdfParams.Salt.Tag != asn1.TagOctetString { + return nil, nil, errors.New("pkcs12: only octet string salts are supported for pbkdf2") + } + + var prf func() hash.Hash + switch { + case kdfParams.Prf.Algorithm.Equal(oidHmacWithSHA256): + prf = sha256.New + case kdfParams.Prf.Algorithm.Equal(oidHmacWithSHA1): + prf = sha1.New + case kdfParams.Prf.Algorithm.Equal(asn1.ObjectIdentifier([]int{})): + prf = sha1.New + } + + key := pbkdf2.Key(password, kdfParams.Salt.Bytes, kdfParams.Iterations, 32, prf) + iv := params.EncryptionScheme.Parameters.Bytes + + var block cipher.Block + switch { + case params.EncryptionScheme.Algorithm.Equal(oidAES256CBC): + b, err := aes.NewCipher(key) + if err != nil { + return nil, nil, err + } + block = b + default: + return nil, nil, NotImplementedError("pbes2 algorithm " + params.EncryptionScheme.Algorithm.String() + " is not supported") + } + return block, iv, nil +} + +// decryptable abstracts an object that contains ciphertext. +type decryptable interface { + Algorithm() pkix.AlgorithmIdentifier + Data() []byte +} + +func pbEncrypterFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.BlockMode, int, error) { + block, iv, err := pbeCipherFor(algorithm, password) + if err != nil { + return nil, 0, err + } + + return cipher.NewCBCEncrypter(block, iv), block.BlockSize(), nil +} + +func pbEncrypt(info encryptable, decrypted []byte, password []byte) error { + cbc, blockSize, err := pbEncrypterFor(info.Algorithm(), password) + if err != nil { + return err + } + + psLen := blockSize - len(decrypted)%blockSize + encrypted := make([]byte, len(decrypted)+psLen) + copy(encrypted[:len(decrypted)], decrypted) + copy(encrypted[len(decrypted):], bytes.Repeat([]byte{byte(psLen)}, psLen)) + cbc.CryptBlocks(encrypted, encrypted) + + info.SetData(encrypted) + + return nil +} + +// encryptable abstracts a object that contains ciphertext. +type encryptable interface { + Algorithm() pkix.AlgorithmIdentifier + SetData([]byte) +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/errors.go b/vendor/software.sslmate.com/src/go-pkcs12/errors.go new file mode 100644 index 0000000000..7377ce6fb2 --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/errors.go @@ -0,0 +1,23 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import "errors" + +var ( + // ErrDecryption represents a failure to decrypt the input. + ErrDecryption = errors.New("pkcs12: decryption error, incorrect padding") + + // ErrIncorrectPassword is returned when an incorrect password is detected. + // Usually, P12/PFX data is signed to be able to verify the password. + ErrIncorrectPassword = errors.New("pkcs12: decryption password incorrect") +) + +// NotImplementedError indicates that the input is not currently supported. +type NotImplementedError string + +func (e NotImplementedError) Error() string { + return "pkcs12: " + string(e) +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/internal/rc2/rc2.go b/vendor/software.sslmate.com/src/go-pkcs12/internal/rc2/rc2.go new file mode 100644 index 0000000000..7499e3fb69 --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/internal/rc2/rc2.go @@ -0,0 +1,271 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package rc2 implements the RC2 cipher +/* +https://www.ietf.org/rfc/rfc2268.txt +http://people.csail.mit.edu/rivest/pubs/KRRR98.pdf + +This code is licensed under the MIT license. +*/ +package rc2 + +import ( + "crypto/cipher" + "encoding/binary" +) + +// The rc2 block size in bytes +const BlockSize = 8 + +type rc2Cipher struct { + k [64]uint16 +} + +// New returns a new rc2 cipher with the given key and effective key length t1 +func New(key []byte, t1 int) (cipher.Block, error) { + // TODO(dgryski): error checking for key length + return &rc2Cipher{ + k: expandKey(key, t1), + }, nil +} + +func (*rc2Cipher) BlockSize() int { return BlockSize } + +var piTable = [256]byte{ + 0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d, + 0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2, + 0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32, + 0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82, + 0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc, + 0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26, + 0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03, + 0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7, + 0x08, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a, + 0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec, + 0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39, + 0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31, + 0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9, + 0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9, + 0x0d, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e, + 0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad, +} + +func expandKey(key []byte, t1 int) [64]uint16 { + + l := make([]byte, 128) + copy(l, key) + + var t = len(key) + var t8 = (t1 + 7) / 8 + var tm = byte(255 % uint(1<<(8+uint(t1)-8*uint(t8)))) + + for i := len(key); i < 128; i++ { + l[i] = piTable[l[i-1]+l[uint8(i-t)]] + } + + l[128-t8] = piTable[l[128-t8]&tm] + + for i := 127 - t8; i >= 0; i-- { + l[i] = piTable[l[i+1]^l[i+t8]] + } + + var k [64]uint16 + + for i := range k { + k[i] = uint16(l[2*i]) + uint16(l[2*i+1])*256 + } + + return k +} + +func rotl16(x uint16, b uint) uint16 { + return (x >> (16 - b)) | (x << b) +} + +func (c *rc2Cipher) Encrypt(dst, src []byte) { + + r0 := binary.LittleEndian.Uint16(src[0:]) + r1 := binary.LittleEndian.Uint16(src[2:]) + r2 := binary.LittleEndian.Uint16(src[4:]) + r3 := binary.LittleEndian.Uint16(src[6:]) + + var j int + + for j <= 16 { + // mix r0 + r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) + r0 = rotl16(r0, 1) + j++ + + // mix r1 + r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2) + r1 = rotl16(r1, 2) + j++ + + // mix r2 + r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3) + r2 = rotl16(r2, 3) + j++ + + // mix r3 + r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) + r3 = rotl16(r3, 5) + j++ + + } + + r0 = r0 + c.k[r3&63] + r1 = r1 + c.k[r0&63] + r2 = r2 + c.k[r1&63] + r3 = r3 + c.k[r2&63] + + for j <= 40 { + // mix r0 + r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) + r0 = rotl16(r0, 1) + j++ + + // mix r1 + r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2) + r1 = rotl16(r1, 2) + j++ + + // mix r2 + r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3) + r2 = rotl16(r2, 3) + j++ + + // mix r3 + r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) + r3 = rotl16(r3, 5) + j++ + + } + + r0 = r0 + c.k[r3&63] + r1 = r1 + c.k[r0&63] + r2 = r2 + c.k[r1&63] + r3 = r3 + c.k[r2&63] + + for j <= 60 { + // mix r0 + r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) + r0 = rotl16(r0, 1) + j++ + + // mix r1 + r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2) + r1 = rotl16(r1, 2) + j++ + + // mix r2 + r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3) + r2 = rotl16(r2, 3) + j++ + + // mix r3 + r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) + r3 = rotl16(r3, 5) + j++ + } + + binary.LittleEndian.PutUint16(dst[0:], r0) + binary.LittleEndian.PutUint16(dst[2:], r1) + binary.LittleEndian.PutUint16(dst[4:], r2) + binary.LittleEndian.PutUint16(dst[6:], r3) +} + +func (c *rc2Cipher) Decrypt(dst, src []byte) { + + r0 := binary.LittleEndian.Uint16(src[0:]) + r1 := binary.LittleEndian.Uint16(src[2:]) + r2 := binary.LittleEndian.Uint16(src[4:]) + r3 := binary.LittleEndian.Uint16(src[6:]) + + j := 63 + + for j >= 44 { + // unmix r3 + r3 = rotl16(r3, 16-5) + r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0) + j-- + + // unmix r2 + r2 = rotl16(r2, 16-3) + r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3) + j-- + + // unmix r1 + r1 = rotl16(r1, 16-2) + r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2) + j-- + + // unmix r0 + r0 = rotl16(r0, 16-1) + r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) + j-- + } + + r3 = r3 - c.k[r2&63] + r2 = r2 - c.k[r1&63] + r1 = r1 - c.k[r0&63] + r0 = r0 - c.k[r3&63] + + for j >= 20 { + // unmix r3 + r3 = rotl16(r3, 16-5) + r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0) + j-- + + // unmix r2 + r2 = rotl16(r2, 16-3) + r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3) + j-- + + // unmix r1 + r1 = rotl16(r1, 16-2) + r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2) + j-- + + // unmix r0 + r0 = rotl16(r0, 16-1) + r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) + j-- + + } + + r3 = r3 - c.k[r2&63] + r2 = r2 - c.k[r1&63] + r1 = r1 - c.k[r0&63] + r0 = r0 - c.k[r3&63] + + for j >= 0 { + // unmix r3 + r3 = rotl16(r3, 16-5) + r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0) + j-- + + // unmix r2 + r2 = rotl16(r2, 16-3) + r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3) + j-- + + // unmix r1 + r1 = rotl16(r1, 16-2) + r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2) + j-- + + // unmix r0 + r0 = rotl16(r0, 16-1) + r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) + j-- + + } + + binary.LittleEndian.PutUint16(dst[0:], r0) + binary.LittleEndian.PutUint16(dst[2:], r1) + binary.LittleEndian.PutUint16(dst[4:], r2) + binary.LittleEndian.PutUint16(dst[6:], r3) +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/mac.go b/vendor/software.sslmate.com/src/go-pkcs12/mac.go new file mode 100644 index 0000000000..b8a3439fd1 --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/mac.go @@ -0,0 +1,70 @@ +// Copyright 2015, 2018, 2019 Opsmate, Inc. All rights reserved. +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "crypto/hmac" + "crypto/sha1" + "crypto/sha256" + "crypto/x509/pkix" + "encoding/asn1" + "hash" +) + +type macData struct { + Mac digestInfo + MacSalt []byte + Iterations int `asn1:"optional,default:1"` +} + +// from PKCS#7: +type digestInfo struct { + Algorithm pkix.AlgorithmIdentifier + Digest []byte +} + +var ( + oidSHA1 = asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}) + oidSHA256 = asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 1}) +) + +func verifyMac(macData *macData, message, password []byte) error { + var hFn func() hash.Hash + var key []byte + switch { + case macData.Mac.Algorithm.Algorithm.Equal(oidSHA1): + hFn = sha1.New + key = pbkdf(sha1Sum, 20, 64, macData.MacSalt, password, macData.Iterations, 3, 20) + case macData.Mac.Algorithm.Algorithm.Equal(oidSHA256): + hFn = sha256.New + key = pbkdf(sha256Sum, 32, 64, macData.MacSalt, password, macData.Iterations, 3, 32) + default: + return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String()) + } + + mac := hmac.New(hFn, key) + mac.Write(message) + expectedMAC := mac.Sum(nil) + + if !hmac.Equal(macData.Mac.Digest, expectedMAC) { + return ErrIncorrectPassword + } + return nil +} + +func computeMac(macData *macData, message, password []byte) error { + if !macData.Mac.Algorithm.Algorithm.Equal(oidSHA1) { + return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String()) + } + + key := pbkdf(sha1Sum, 20, 64, macData.MacSalt, password, macData.Iterations, 3, 20) + + mac := hmac.New(sha1.New, key) + mac.Write(message) + macData.Mac.Digest = mac.Sum(nil) + + return nil +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/pbkdf.go b/vendor/software.sslmate.com/src/go-pkcs12/pbkdf.go new file mode 100644 index 0000000000..e6e0c6209a --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/pbkdf.go @@ -0,0 +1,177 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "bytes" + "crypto/sha1" + "crypto/sha256" + "math/big" +) + +var ( + one = big.NewInt(1) +) + +// sha1Sum returns the SHA-1 hash of in. +func sha1Sum(in []byte) []byte { + sum := sha1.Sum(in) + return sum[:] +} + +// sha256Sum returns the SHA-256 hash of in. +func sha256Sum(in []byte) []byte { + sum := sha256.Sum256(in) + return sum[:] +} + +// fillWithRepeats returns v*ceiling(len(pattern) / v) bytes consisting of +// repeats of pattern. +func fillWithRepeats(pattern []byte, v int) []byte { + if len(pattern) == 0 { + return nil + } + outputLen := v * ((len(pattern) + v - 1) / v) + return bytes.Repeat(pattern, (outputLen+len(pattern)-1)/len(pattern))[:outputLen] +} + +func pbkdf(hash func([]byte) []byte, u, v int, salt, password []byte, r int, ID byte, size int) (key []byte) { + // implementation of https://tools.ietf.org/html/rfc7292#appendix-B.2 , RFC text verbatim in comments + + // Let H be a hash function built around a compression function f: + + // Z_2^u x Z_2^v -> Z_2^u + + // (that is, H has a chaining variable and output of length u bits, and + // the message input to the compression function of H is v bits). The + // values for u and v are as follows: + + // HASH FUNCTION VALUE u VALUE v + // MD2, MD5 128 512 + // SHA-1 160 512 + // SHA-224 224 512 + // SHA-256 256 512 + // SHA-384 384 1024 + // SHA-512 512 1024 + // SHA-512/224 224 1024 + // SHA-512/256 256 1024 + + // Furthermore, let r be the iteration count. + + // We assume here that u and v are both multiples of 8, as are the + // lengths of the password and salt strings (which we denote by p and s, + // respectively) and the number n of pseudorandom bits required. In + // addition, u and v are of course non-zero. + + // For information on security considerations for MD5 [19], see [25] and + // [1], and on those for MD2, see [18]. + + // The following procedure can be used to produce pseudorandom bits for + // a particular "purpose" that is identified by a byte called "ID". + // This standard specifies 3 different values for the ID byte: + + // 1. If ID=1, then the pseudorandom bits being produced are to be used + // as key material for performing encryption or decryption. + + // 2. If ID=2, then the pseudorandom bits being produced are to be used + // as an IV (Initial Value) for encryption or decryption. + + // 3. If ID=3, then the pseudorandom bits being produced are to be used + // as an integrity key for MACing. + + // 1. Construct a string, D (the "diversifier"), by concatenating v/8 + // copies of ID. + var D []byte + for i := 0; i < v; i++ { + D = append(D, ID) + } + + // 2. Concatenate copies of the salt together to create a string S of + // length v(ceiling(s/v)) bits (the final copy of the salt may be + // truncated to create S). Note that if the salt is the empty + // string, then so is S. + + S := fillWithRepeats(salt, v) + + // 3. Concatenate copies of the password together to create a string P + // of length v(ceiling(p/v)) bits (the final copy of the password + // may be truncated to create P). Note that if the password is the + // empty string, then so is P. + + P := fillWithRepeats(password, v) + + // 4. Set I=S||P to be the concatenation of S and P. + I := append(S, P...) + + // 5. Set c=ceiling(n/u). + c := (size + u - 1) / u + + // 6. For i=1, 2, ..., c, do the following: + A := make([]byte, c*u) + var IjBuf []byte + for i := 0; i < c; i++ { + // A. Set A2=H^r(D||I). (i.e., the r-th hash of D||1, + // H(H(H(... H(D||I)))) + Ai := hash(append(D, I...)) + for j := 1; j < r; j++ { + Ai = hash(Ai) + } + copy(A[i*u:], Ai[:]) + + if i < c-1 { // skip on last iteration + // B. Concatenate copies of Ai to create a string B of length v + // bits (the final copy of Ai may be truncated to create B). + var B []byte + for len(B) < v { + B = append(B, Ai[:]...) + } + B = B[:v] + + // C. Treating I as a concatenation I_0, I_1, ..., I_(k-1) of v-bit + // blocks, where k=ceiling(s/v)+ceiling(p/v), modify I by + // setting I_j=(I_j+B+1) mod 2^v for each j. + { + Bbi := new(big.Int).SetBytes(B) + Ij := new(big.Int) + + for j := 0; j < len(I)/v; j++ { + Ij.SetBytes(I[j*v : (j+1)*v]) + Ij.Add(Ij, Bbi) + Ij.Add(Ij, one) + Ijb := Ij.Bytes() + // We expect Ijb to be exactly v bytes, + // if it is longer or shorter we must + // adjust it accordingly. + if len(Ijb) > v { + Ijb = Ijb[len(Ijb)-v:] + } + if len(Ijb) < v { + if IjBuf == nil { + IjBuf = make([]byte, v) + } + bytesShort := v - len(Ijb) + for i := 0; i < bytesShort; i++ { + IjBuf[i] = 0 + } + copy(IjBuf[bytesShort:], Ijb) + Ijb = IjBuf + } + copy(I[j*v:(j+1)*v], Ijb) + } + } + } + } + // 7. Concatenate A_1, A_2, ..., A_c together to form a pseudorandom + // bit string, A. + + // 8. Use the first n bits of A as the output of this entire process. + return A[:size] + + // If the above process is being used to generate a DES key, the process + // should be used to create 64 random bits, and the key's parity bits + // should be set after the 64 bits have been produced. Similar concerns + // hold for 2-key and 3-key triple-DES keys, for CDMF keys, and for any + // similar keys with parity bits "built into them". +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/pkcs12.go b/vendor/software.sslmate.com/src/go-pkcs12/pkcs12.go new file mode 100644 index 0000000000..f0c0ae7fbd --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/pkcs12.go @@ -0,0 +1,686 @@ +// Copyright 2015, 2018, 2019 Opsmate, Inc. All rights reserved. +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package pkcs12 implements some of PKCS#12 (also known as P12 or PFX). +// It is intended for decoding DER-encoded P12/PFX files for use with the crypto/tls +// package, and for encoding P12/PFX files for use by legacy applications which +// do not support newer formats. Since PKCS#12 uses weak encryption +// primitives, it SHOULD NOT be used for new applications. +// +// Note that only DER-encoded PKCS#12 files are supported, even though PKCS#12 +// allows BER encoding. This is becuase encoding/asn1 only supports DER. +// +// This package is forked from golang.org/x/crypto/pkcs12, which is frozen. +// The implementation is distilled from https://tools.ietf.org/html/rfc7292 +// and referenced documents. +package pkcs12 // import "software.sslmate.com/src/go-pkcs12" + +import ( + "crypto/ecdsa" + "crypto/rsa" + "crypto/sha1" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/hex" + "encoding/pem" + "errors" + "io" +) + +// DefaultPassword is the string "changeit", a commonly-used password for +// PKCS#12 files. Due to the weak encryption used by PKCS#12, it is +// RECOMMENDED that you use DefaultPassword when encoding PKCS#12 files, +// and protect the PKCS#12 files using other means. +const DefaultPassword = "changeit" + +var ( + oidDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 1}) + oidEncryptedDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 6}) + + oidFriendlyName = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 20}) + oidLocalKeyID = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 21}) + oidMicrosoftCSPName = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 311, 17, 1}) + + oidJavaTrustStore = asn1.ObjectIdentifier([]int{2, 16, 840, 1, 113894, 746875, 1, 1}) + oidAnyExtendedKeyUsage = asn1.ObjectIdentifier([]int{2, 5, 29, 37, 0}) +) + +type pfxPdu struct { + Version int + AuthSafe contentInfo + MacData macData `asn1:"optional"` +} + +type contentInfo struct { + ContentType asn1.ObjectIdentifier + Content asn1.RawValue `asn1:"tag:0,explicit,optional"` +} + +type encryptedData struct { + Version int + EncryptedContentInfo encryptedContentInfo +} + +type encryptedContentInfo struct { + ContentType asn1.ObjectIdentifier + ContentEncryptionAlgorithm pkix.AlgorithmIdentifier + EncryptedContent []byte `asn1:"tag:0,optional"` +} + +func (i encryptedContentInfo) Algorithm() pkix.AlgorithmIdentifier { + return i.ContentEncryptionAlgorithm +} + +func (i encryptedContentInfo) Data() []byte { return i.EncryptedContent } + +func (i *encryptedContentInfo) SetData(data []byte) { i.EncryptedContent = data } + +type safeBag struct { + Id asn1.ObjectIdentifier + Value asn1.RawValue `asn1:"tag:0,explicit"` + Attributes []pkcs12Attribute `asn1:"set,optional"` +} + +func (bag *safeBag) hasAttribute(id asn1.ObjectIdentifier) bool { + for _, attr := range bag.Attributes { + if attr.Id.Equal(id) { + return true + } + } + return false +} + +type pkcs12Attribute struct { + Id asn1.ObjectIdentifier + Value asn1.RawValue `asn1:"set"` +} + +type encryptedPrivateKeyInfo struct { + AlgorithmIdentifier pkix.AlgorithmIdentifier + EncryptedData []byte +} + +func (i encryptedPrivateKeyInfo) Algorithm() pkix.AlgorithmIdentifier { + return i.AlgorithmIdentifier +} + +func (i encryptedPrivateKeyInfo) Data() []byte { + return i.EncryptedData +} + +func (i *encryptedPrivateKeyInfo) SetData(data []byte) { + i.EncryptedData = data +} + +// PEM block types +const ( + certificateType = "CERTIFICATE" + privateKeyType = "PRIVATE KEY" +) + +// unmarshal calls asn1.Unmarshal, but also returns an error if there is any +// trailing data after unmarshaling. +func unmarshal(in []byte, out interface{}) error { + trailing, err := asn1.Unmarshal(in, out) + if err != nil { + return err + } + if len(trailing) != 0 { + return errors.New("pkcs12: trailing data found") + } + return nil +} + +// ToPEM converts all "safe bags" contained in pfxData to PEM blocks. +// DO NOT USE THIS FUNCTION. ToPEM creates invalid PEM blocks; private keys +// are encoded as raw RSA or EC private keys rather than PKCS#8 despite being +// labeled "PRIVATE KEY". To decode a PKCS#12 file, use DecodeChain instead, +// and use the encoding/pem package to convert to PEM if necessary. +func ToPEM(pfxData []byte, password string) ([]*pem.Block, error) { + encodedPassword, err := bmpString(password) + if err != nil { + return nil, ErrIncorrectPassword + } + + bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword, 2) + + if err != nil { + return nil, err + } + + blocks := make([]*pem.Block, 0, len(bags)) + for _, bag := range bags { + block, err := convertBag(&bag, encodedPassword) + if err != nil { + return nil, err + } + blocks = append(blocks, block) + } + + return blocks, nil +} + +func convertBag(bag *safeBag, password []byte) (*pem.Block, error) { + block := &pem.Block{ + Headers: make(map[string]string), + } + + for _, attribute := range bag.Attributes { + k, v, err := convertAttribute(&attribute) + if err != nil { + return nil, err + } + block.Headers[k] = v + } + + switch { + case bag.Id.Equal(oidCertBag): + block.Type = certificateType + certsData, err := decodeCertBag(bag.Value.Bytes) + if err != nil { + return nil, err + } + block.Bytes = certsData + case bag.Id.Equal(oidPKCS8ShroundedKeyBag): + block.Type = privateKeyType + + key, err := decodePkcs8ShroudedKeyBag(bag.Value.Bytes, password) + if err != nil { + return nil, err + } + + switch key := key.(type) { + case *rsa.PrivateKey: + block.Bytes = x509.MarshalPKCS1PrivateKey(key) + case *ecdsa.PrivateKey: + block.Bytes, err = x509.MarshalECPrivateKey(key) + if err != nil { + return nil, err + } + default: + return nil, errors.New("found unknown private key type in PKCS#8 wrapping") + } + default: + return nil, errors.New("don't know how to convert a safe bag of type " + bag.Id.String()) + } + return block, nil +} + +func convertAttribute(attribute *pkcs12Attribute) (key, value string, err error) { + isString := false + + switch { + case attribute.Id.Equal(oidFriendlyName): + key = "friendlyName" + isString = true + case attribute.Id.Equal(oidLocalKeyID): + key = "localKeyId" + case attribute.Id.Equal(oidMicrosoftCSPName): + // This key is chosen to match OpenSSL. + key = "Microsoft CSP Name" + isString = true + default: + return "", "", errors.New("pkcs12: unknown attribute with OID " + attribute.Id.String()) + } + + if isString { + if err := unmarshal(attribute.Value.Bytes, &attribute.Value); err != nil { + return "", "", err + } + if value, err = decodeBMPString(attribute.Value.Bytes); err != nil { + return "", "", err + } + } else { + var id []byte + if err := unmarshal(attribute.Value.Bytes, &id); err != nil { + return "", "", err + } + value = hex.EncodeToString(id) + } + + return key, value, nil +} + +// Decode extracts a certificate and private key from pfxData, which must be a DER-encoded PKCS#12 file. This function +// assumes that there is only one certificate and only one private key in the +// pfxData. Since PKCS#12 files often contain more than one certificate, you +// probably want to use DecodeChain instead. +func Decode(pfxData []byte, password string) (privateKey interface{}, certificate *x509.Certificate, err error) { + var caCerts []*x509.Certificate + privateKey, certificate, caCerts, err = DecodeChain(pfxData, password) + if len(caCerts) != 0 { + err = errors.New("pkcs12: expected exactly two safe bags in the PFX PDU") + } + return +} + +// DecodeChain extracts a certificate, a CA certificate chain, and private key +// from pfxData, which must be a DER-encoded PKCS#12 file. This function assumes that there is at least one certificate +// and only one private key in the pfxData. The first certificate is assumed to +// be the leaf certificate, and subsequent certificates, if any, are assumed to +// comprise the CA certificate chain. +func DecodeChain(pfxData []byte, password string) (privateKey interface{}, certificate *x509.Certificate, caCerts []*x509.Certificate, err error) { + encodedPassword, err := bmpString(password) + if err != nil { + return nil, nil, nil, err + } + + bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword, 2) + if err != nil { + return nil, nil, nil, err + } + + for _, bag := range bags { + switch { + case bag.Id.Equal(oidCertBag): + certsData, err := decodeCertBag(bag.Value.Bytes) + if err != nil { + return nil, nil, nil, err + } + certs, err := x509.ParseCertificates(certsData) + if err != nil { + return nil, nil, nil, err + } + if len(certs) != 1 { + err = errors.New("pkcs12: expected exactly one certificate in the certBag") + return nil, nil, nil, err + } + if certificate == nil { + certificate = certs[0] + } else { + caCerts = append(caCerts, certs[0]) + } + + case bag.Id.Equal(oidPKCS8ShroundedKeyBag): + if privateKey != nil { + err = errors.New("pkcs12: expected exactly one key bag") + return nil, nil, nil, err + } + + if privateKey, err = decodePkcs8ShroudedKeyBag(bag.Value.Bytes, encodedPassword); err != nil { + return nil, nil, nil, err + } + } + } + + if certificate == nil { + return nil, nil, nil, errors.New("pkcs12: certificate missing") + } + if privateKey == nil { + return nil, nil, nil, errors.New("pkcs12: private key missing") + } + + return +} + +// DecodeTrustStore extracts the certificates from pfxData, which must be a DER-encoded +// PKCS#12 file containing exclusively certificates with attribute 2.16.840.1.113894.746875.1.1, +// which is used by Java to designate a trust anchor. +func DecodeTrustStore(pfxData []byte, password string) (certs []*x509.Certificate, err error) { + encodedPassword, err := bmpString(password) + if err != nil { + return nil, err + } + + bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword, 1) + if err != nil { + return nil, err + } + + for _, bag := range bags { + switch { + case bag.Id.Equal(oidCertBag): + if !bag.hasAttribute(oidJavaTrustStore) { + return nil, errors.New("pkcs12: trust store contains a certificate that is not marked as trusted") + } + certsData, err := decodeCertBag(bag.Value.Bytes) + if err != nil { + return nil, err + } + parsedCerts, err := x509.ParseCertificates(certsData) + if err != nil { + return nil, err + } + + if len(parsedCerts) != 1 { + err = errors.New("pkcs12: expected exactly one certificate in the certBag") + return nil, err + } + + certs = append(certs, parsedCerts[0]) + + default: + return nil, errors.New("pkcs12: expected only certificate bags") + } + } + + return +} + +func getSafeContents(p12Data, password []byte, expectedItems int) (bags []safeBag, updatedPassword []byte, err error) { + pfx := new(pfxPdu) + if err := unmarshal(p12Data, pfx); err != nil { + return nil, nil, errors.New("pkcs12: error reading P12 data: " + err.Error()) + } + + if pfx.Version != 3 { + return nil, nil, NotImplementedError("can only decode v3 PFX PDU's") + } + + if !pfx.AuthSafe.ContentType.Equal(oidDataContentType) { + return nil, nil, NotImplementedError("only password-protected PFX is implemented") + } + + // unmarshal the explicit bytes in the content for type 'data' + if err := unmarshal(pfx.AuthSafe.Content.Bytes, &pfx.AuthSafe.Content); err != nil { + return nil, nil, err + } + + if len(pfx.MacData.Mac.Algorithm.Algorithm) == 0 { + return nil, nil, errors.New("pkcs12: no MAC in data") + } + + if err := verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password); err != nil { + if err == ErrIncorrectPassword && len(password) == 2 && password[0] == 0 && password[1] == 0 { + // some implementations use an empty byte array + // for the empty string password try one more + // time with empty-empty password + password = nil + err = verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password) + } + if err != nil { + return nil, nil, err + } + } + + var authenticatedSafe []contentInfo + if err := unmarshal(pfx.AuthSafe.Content.Bytes, &authenticatedSafe); err != nil { + return nil, nil, err + } + + if len(authenticatedSafe) != expectedItems { + return nil, nil, NotImplementedError("expected exactly two items in the authenticated safe") + } + + for _, ci := range authenticatedSafe { + var data []byte + + switch { + case ci.ContentType.Equal(oidDataContentType): + if err := unmarshal(ci.Content.Bytes, &data); err != nil { + return nil, nil, err + } + case ci.ContentType.Equal(oidEncryptedDataContentType): + var encryptedData encryptedData + if err := unmarshal(ci.Content.Bytes, &encryptedData); err != nil { + return nil, nil, err + } + if encryptedData.Version != 0 { + return nil, nil, NotImplementedError("only version 0 of EncryptedData is supported") + } + if data, err = pbDecrypt(encryptedData.EncryptedContentInfo, password); err != nil { + return nil, nil, err + } + default: + return nil, nil, NotImplementedError("only data and encryptedData content types are supported in authenticated safe") + } + + var safeContents []safeBag + if err := unmarshal(data, &safeContents); err != nil { + return nil, nil, err + } + bags = append(bags, safeContents...) + } + + return bags, password, nil +} + +// Encode produces pfxData containing one private key (privateKey), an +// end-entity certificate (certificate), and any number of CA certificates +// (caCerts). +// +// The private key is encrypted with the provided password, but due to the +// weak encryption primitives used by PKCS#12, it is RECOMMENDED that you +// specify a hard-coded password (such as pkcs12.DefaultPassword) and protect +// the resulting pfxData using other means. +// +// The rand argument is used to provide entropy for the encryption, and +// can be set to rand.Reader from the crypto/rand package. +// +// Encode emulates the behavior of OpenSSL's PKCS12_create: it creates two +// SafeContents: one that's encrypted with RC2 and contains the certificates, +// and another that is unencrypted and contains the private key shrouded with +// 3DES The private key bag and the end-entity certificate bag have the +// LocalKeyId attribute set to the SHA-1 fingerprint of the end-entity +// certificate. +func Encode(rand io.Reader, privateKey interface{}, certificate *x509.Certificate, caCerts []*x509.Certificate, password string) (pfxData []byte, err error) { + encodedPassword, err := bmpString(password) + if err != nil { + return nil, err + } + + var pfx pfxPdu + pfx.Version = 3 + + var certFingerprint = sha1.Sum(certificate.Raw) + var localKeyIdAttr pkcs12Attribute + localKeyIdAttr.Id = oidLocalKeyID + localKeyIdAttr.Value.Class = 0 + localKeyIdAttr.Value.Tag = 17 + localKeyIdAttr.Value.IsCompound = true + if localKeyIdAttr.Value.Bytes, err = asn1.Marshal(certFingerprint[:]); err != nil { + return nil, err + } + + var certBags []safeBag + var certBag *safeBag + if certBag, err = makeCertBag(certificate.Raw, []pkcs12Attribute{localKeyIdAttr}); err != nil { + return nil, err + } + certBags = append(certBags, *certBag) + + for _, cert := range caCerts { + if certBag, err = makeCertBag(cert.Raw, []pkcs12Attribute{}); err != nil { + return nil, err + } + certBags = append(certBags, *certBag) + } + + var keyBag safeBag + keyBag.Id = oidPKCS8ShroundedKeyBag + keyBag.Value.Class = 2 + keyBag.Value.Tag = 0 + keyBag.Value.IsCompound = true + if keyBag.Value.Bytes, err = encodePkcs8ShroudedKeyBag(rand, privateKey, encodedPassword); err != nil { + return nil, err + } + keyBag.Attributes = append(keyBag.Attributes, localKeyIdAttr) + + // Construct an authenticated safe with two SafeContents. + // The first SafeContents is encrypted and contains the cert bags. + // The second SafeContents is unencrypted and contains the shrouded key bag. + var authenticatedSafe [2]contentInfo + if authenticatedSafe[0], err = makeSafeContents(rand, certBags, encodedPassword); err != nil { + return nil, err + } + if authenticatedSafe[1], err = makeSafeContents(rand, []safeBag{keyBag}, nil); err != nil { + return nil, err + } + + var authenticatedSafeBytes []byte + if authenticatedSafeBytes, err = asn1.Marshal(authenticatedSafe[:]); err != nil { + return nil, err + } + + // compute the MAC + pfx.MacData.Mac.Algorithm.Algorithm = oidSHA1 + pfx.MacData.MacSalt = make([]byte, 8) + if _, err = rand.Read(pfx.MacData.MacSalt); err != nil { + return nil, err + } + pfx.MacData.Iterations = 1 + if err = computeMac(&pfx.MacData, authenticatedSafeBytes, encodedPassword); err != nil { + return nil, err + } + + pfx.AuthSafe.ContentType = oidDataContentType + pfx.AuthSafe.Content.Class = 2 + pfx.AuthSafe.Content.Tag = 0 + pfx.AuthSafe.Content.IsCompound = true + if pfx.AuthSafe.Content.Bytes, err = asn1.Marshal(authenticatedSafeBytes); err != nil { + return nil, err + } + + if pfxData, err = asn1.Marshal(pfx); err != nil { + return nil, errors.New("pkcs12: error writing P12 data: " + err.Error()) + } + return +} + +// EncodeTrustStore produces pfxData containing any number of CA certificates +// (certs) to be trusted. The certificates will be marked with a special OID that +// allow it to be used as a Java TrustStore in Java 1.8 and newer. +// +// Due to the weak encryption primitives used by PKCS#12, it is RECOMMENDED that +// you specify a hard-coded password (such as pkcs12.DefaultPassword) and protect +// the resulting pfxData using other means. +// +// The rand argument is used to provide entropy for the encryption, and +// can be set to rand.Reader from the crypto/rand package. +// +// EncodeTrustStore creates a single SafeContents that's encrypted with RC2 +// and contains the certificates. +func EncodeTrustStore(rand io.Reader, certs []*x509.Certificate, password string) (pfxData []byte, err error) { + encodedPassword, err := bmpString(password) + if err != nil { + return nil, err + } + + var pfx pfxPdu + pfx.Version = 3 + + var certAttributes []pkcs12Attribute + + extKeyUsageOidBytes, err := asn1.Marshal(oidAnyExtendedKeyUsage) + if err != nil { + return nil, err + } + + // the oidJavaTrustStore attribute contains the EKUs for which + // this trust anchor will be valid + certAttributes = append(certAttributes, pkcs12Attribute{ + Id: oidJavaTrustStore, + Value: asn1.RawValue{ + Class: 0, + Tag: 17, + IsCompound: true, + Bytes: extKeyUsageOidBytes, + }, + }) + + var certBags []safeBag + for _, cert := range certs { + certBag, err := makeCertBag(cert.Raw, certAttributes) + if err != nil { + return nil, err + } + certBags = append(certBags, *certBag) + } + + // Construct an authenticated safe with one SafeContent. + // The SafeContents is encrypted and contains the cert bags. + var authenticatedSafe [1]contentInfo + if authenticatedSafe[0], err = makeSafeContents(rand, certBags, encodedPassword); err != nil { + return nil, err + } + + var authenticatedSafeBytes []byte + if authenticatedSafeBytes, err = asn1.Marshal(authenticatedSafe[:]); err != nil { + return nil, err + } + + // compute the MAC + pfx.MacData.Mac.Algorithm.Algorithm = oidSHA1 + pfx.MacData.MacSalt = make([]byte, 8) + if _, err = rand.Read(pfx.MacData.MacSalt); err != nil { + return nil, err + } + pfx.MacData.Iterations = 1 + if err = computeMac(&pfx.MacData, authenticatedSafeBytes, encodedPassword); err != nil { + return nil, err + } + + pfx.AuthSafe.ContentType = oidDataContentType + pfx.AuthSafe.Content.Class = 2 + pfx.AuthSafe.Content.Tag = 0 + pfx.AuthSafe.Content.IsCompound = true + if pfx.AuthSafe.Content.Bytes, err = asn1.Marshal(authenticatedSafeBytes); err != nil { + return nil, err + } + + if pfxData, err = asn1.Marshal(pfx); err != nil { + return nil, errors.New("pkcs12: error writing P12 data: " + err.Error()) + } + return +} + +func makeCertBag(certBytes []byte, attributes []pkcs12Attribute) (certBag *safeBag, err error) { + certBag = new(safeBag) + certBag.Id = oidCertBag + certBag.Value.Class = 2 + certBag.Value.Tag = 0 + certBag.Value.IsCompound = true + if certBag.Value.Bytes, err = encodeCertBag(certBytes); err != nil { + return nil, err + } + certBag.Attributes = attributes + return +} + +func makeSafeContents(rand io.Reader, bags []safeBag, password []byte) (ci contentInfo, err error) { + var data []byte + if data, err = asn1.Marshal(bags); err != nil { + return + } + + if password == nil { + ci.ContentType = oidDataContentType + ci.Content.Class = 2 + ci.Content.Tag = 0 + ci.Content.IsCompound = true + if ci.Content.Bytes, err = asn1.Marshal(data); err != nil { + return + } + } else { + randomSalt := make([]byte, 8) + if _, err = rand.Read(randomSalt); err != nil { + return + } + + var algo pkix.AlgorithmIdentifier + algo.Algorithm = oidPBEWithSHAAnd40BitRC2CBC + if algo.Parameters.FullBytes, err = asn1.Marshal(pbeParams{Salt: randomSalt, Iterations: 2048}); err != nil { + return + } + + var encryptedData encryptedData + encryptedData.Version = 0 + encryptedData.EncryptedContentInfo.ContentType = oidDataContentType + encryptedData.EncryptedContentInfo.ContentEncryptionAlgorithm = algo + if err = pbEncrypt(&encryptedData.EncryptedContentInfo, data, password); err != nil { + return + } + + ci.ContentType = oidEncryptedDataContentType + ci.Content.Class = 2 + ci.Content.Tag = 0 + ci.Content.IsCompound = true + if ci.Content.Bytes, err = asn1.Marshal(encryptedData); err != nil { + return + } + } + return +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/safebags.go b/vendor/software.sslmate.com/src/go-pkcs12/safebags.go new file mode 100644 index 0000000000..be83a49768 --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/safebags.go @@ -0,0 +1,99 @@ +// Copyright 2015, 2018, 2019 Opsmate, Inc. All rights reserved. +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "crypto/x509" + "encoding/asn1" + "errors" + "io" +) + +var ( + // see https://tools.ietf.org/html/rfc7292#appendix-D + oidCertTypeX509Certificate = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 22, 1}) + oidPKCS8ShroundedKeyBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 2}) + oidCertBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 3}) +) + +type certBag struct { + Id asn1.ObjectIdentifier + Data []byte `asn1:"tag:0,explicit"` +} + +func decodePkcs8ShroudedKeyBag(asn1Data, password []byte) (privateKey interface{}, err error) { + pkinfo := new(encryptedPrivateKeyInfo) + if err = unmarshal(asn1Data, pkinfo); err != nil { + return nil, errors.New("pkcs12: error decoding PKCS#8 shrouded key bag: " + err.Error()) + } + + pkData, err := pbDecrypt(pkinfo, password) + if err != nil { + return nil, errors.New("pkcs12: error decrypting PKCS#8 shrouded key bag: " + err.Error()) + } + + ret := new(asn1.RawValue) + if err = unmarshal(pkData, ret); err != nil { + return nil, errors.New("pkcs12: error unmarshaling decrypted private key: " + err.Error()) + } + + if privateKey, err = x509.ParsePKCS8PrivateKey(pkData); err != nil { + return nil, errors.New("pkcs12: error parsing PKCS#8 private key: " + err.Error()) + } + + return privateKey, nil +} + +func encodePkcs8ShroudedKeyBag(rand io.Reader, privateKey interface{}, password []byte) (asn1Data []byte, err error) { + var pkData []byte + if pkData, err = x509.MarshalPKCS8PrivateKey(privateKey); err != nil { + return nil, errors.New("pkcs12: error encoding PKCS#8 private key: " + err.Error()) + } + + randomSalt := make([]byte, 8) + if _, err = rand.Read(randomSalt); err != nil { + return nil, errors.New("pkcs12: error reading random salt: " + err.Error()) + } + var paramBytes []byte + if paramBytes, err = asn1.Marshal(pbeParams{Salt: randomSalt, Iterations: 2048}); err != nil { + return nil, errors.New("pkcs12: error encoding params: " + err.Error()) + } + + var pkinfo encryptedPrivateKeyInfo + pkinfo.AlgorithmIdentifier.Algorithm = oidPBEWithSHAAnd3KeyTripleDESCBC + pkinfo.AlgorithmIdentifier.Parameters.FullBytes = paramBytes + + if err = pbEncrypt(&pkinfo, pkData, password); err != nil { + return nil, errors.New("pkcs12: error encrypting PKCS#8 shrouded key bag: " + err.Error()) + } + + if asn1Data, err = asn1.Marshal(pkinfo); err != nil { + return nil, errors.New("pkcs12: error encoding PKCS#8 shrouded key bag: " + err.Error()) + } + + return asn1Data, nil +} + +func decodeCertBag(asn1Data []byte) (x509Certificates []byte, err error) { + bag := new(certBag) + if err := unmarshal(asn1Data, bag); err != nil { + return nil, errors.New("pkcs12: error decoding cert bag: " + err.Error()) + } + if !bag.Id.Equal(oidCertTypeX509Certificate) { + return nil, NotImplementedError("only X509 certificates are supported") + } + return bag.Data, nil +} + +func encodeCertBag(x509Certificates []byte) (asn1Data []byte, err error) { + var bag certBag + bag.Id = oidCertTypeX509Certificate + bag.Data = x509Certificates + if asn1Data, err = asn1.Marshal(bag); err != nil { + return nil, errors.New("pkcs12: error encoding cert bag: " + err.Error()) + } + return asn1Data, nil +} From 7db3fd213821b6cd4323fe66898def4a10b241eb Mon Sep 17 00:00:00 2001 From: alegal Date: Mon, 2 Nov 2020 15:27:36 -0800 Subject: [PATCH 2/8] correct small typo --- server/tls/tlscertificate_retriever_disabled.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/tls/tlscertificate_retriever_disabled.go b/server/tls/tlscertificate_retriever_disabled.go index 3783b8b4fd..3b27d185ce 100644 --- a/server/tls/tlscertificate_retriever_disabled.go +++ b/server/tls/tlscertificate_retriever_disabled.go @@ -12,7 +12,7 @@ import ( // NewTlsCertificateRetriever should not be called // Linux currently uses tls file certificate retriever // this indicates the caller has not set the Tls Certificate Path in the server settings -func NewTlsCertificateRetriever(settings TlsCertificateSettings) (TlsCertificateRetriever, error) { +func NewTlsCertificateRetriever(settings TlsSettings) (TlsCertificateRetriever, error) { if settings.TlsCertificateFilePath == ""{ return nil, fmt.Errorf("TLS certificate file path not set") } From 0fedbe375de885d6c7b38e0b88e9896f1c637553 Mon Sep 17 00:00:00 2001 From: alegal Date: Tue, 3 Nov 2020 12:59:48 -0800 Subject: [PATCH 3/8] address feedback --- cns/configuration/configuration.go | 12 ++-- cns/service.go | 20 +++--- cns/service/main.go | 6 +- common/listener.go | 68 ++++++------------- .../custom_certtostore_windows.go | 41 ++++++----- server/tls/tls_certificate_retriever.go | 2 + server/tls/tlscertificate_retriever.go | 4 +- .../tls/tlscertificate_retriever_disabled.go | 9 +-- server/tls/tlscertificate_retriever_file.go | 18 ++--- .../tls/tlscertificate_retriever_file_test.go | 40 ++++------- .../tls/tlscertificate_retriever_windows.go | 41 +++++------ 11 files changed, 103 insertions(+), 158 deletions(-) diff --git a/cns/configuration/configuration.go b/cns/configuration/configuration.go index 66cd07fb14..500245b581 100644 --- a/cns/configuration/configuration.go +++ b/cns/configuration/configuration.go @@ -17,13 +17,13 @@ const ( ) type CNSConfig struct { - TelemetrySettings TelemetrySettings - ManagedSettings ManagedSettings - ChannelMode string - UseHttps bool - TlsSubjectName string + TelemetrySettings TelemetrySettings + ManagedSettings ManagedSettings + ChannelMode string + UseHttps bool + TlsSubjectName string TlsCertificateFilePath string - TlsEndpoint string + TlsEndpoint string } type TelemetrySettings struct { diff --git a/cns/service.go b/cns/service.go index 9b899aaebf..674870e5c4 100644 --- a/cns/service.go +++ b/cns/service.go @@ -63,27 +63,23 @@ func (service *Service) Initialize(config *common.ServiceConfig) error { if err != nil { return err } - // Create the listener. listener, err := acn.NewListener(u) if err != nil { return err } - - if(config.TlsSettings.TlsEndpoint != ""){ + if config.TlsSettings.TlsEndpoint != "" { // Start the listener and HTTP and HTTPS server. - err = listener.StartTLS(config.ErrChan, config.TlsSettings) - if err != nil { - return err - } - } else{ - // Start the listener. - err = listener.Start(config.ErrChan) - if err != nil { + if err = listener.StartTLS(config.ErrChan, config.TlsSettings); err != nil { return err } } - + // Start the listener. + // continue to listen on the normal endpoint for http traffic, this will be supported + // for sometime until partners migrate fully to https + if err = listener.Start(config.ErrChan); err != nil { + return err + } config.Listener = listener } diff --git a/cns/service/main.go b/cns/service/main.go index afc0458a72..8e7fdee2e9 100644 --- a/cns/service/main.go +++ b/cns/service/main.go @@ -425,11 +425,11 @@ func main() { // Start CNS. if httpRestService != nil { - if(cnsconfig.UseHttps){ + if cnsconfig.UseHttps { config.TlsSettings = localtls.TlsSettings{ TlsCertificateSubjectName: cnsconfig.TlsSubjectName, - TlsCertificateFilePath: cnsconfig.TlsCertificateFilePath, - TlsEndpoint: cnsconfig.TlsEndpoint, + TlsCertificateFilePath: cnsconfig.TlsCertificateFilePath, + TlsEndpoint: cnsconfig.TlsEndpoint, } } diff --git a/common/listener.go b/common/listener.go index cd55ba822a..ee78ec1868 100644 --- a/common/listener.go +++ b/common/listener.go @@ -7,25 +7,25 @@ import ( "crypto/tls" "encoding/json" "fmt" + localtls "github.com/Azure/azure-container-networking/server/tls" "net" "net/http" "net/url" "os" - localtls "github.com/Azure/azure-container-networking/server/tls" "github.com/Azure/azure-container-networking/log" ) // Listener represents an HTTP listener. type Listener struct { - URL *url.URL - protocol string - localAddress string - endpoints []string - active bool - l net.Listener - securelistener net.Listener - mux *http.ServeMux + URL *url.URL + protocol string + localAddress string + endpoints []string + active bool + l net.Listener + securelistener net.Listener + mux *http.ServeMux } // NewListener creates a new Listener. @@ -41,34 +41,27 @@ func NewListener(u *url.URL) (*Listener, error) { return &listener, nil } -func GetTlsConfig(tlsSettings localtls.TlsSettings) (*tls.Config, error){ +func GetTlsConfig(tlsSettings localtls.TlsSettings) (*tls.Config, error) { tlsCertRetriever, err := localtls.GetTlsCertificateRetriever(tlsSettings) if err != nil { - return nil, fmt.Errorf("Failed to get certificate retriever", err) + return nil, fmt.Errorf("Failed to get certificate retriever %+v", err) } - leafCertificate, err := tlsCertRetriever.GetCertificate() - if err != nil { - return nil, fmt.Errorf("Failed to get certificate", err) + return nil, fmt.Errorf("Failed to get certificate %+v", err) } - if leafCertificate == nil { - return nil, fmt.Errorf("Certificate retrival returned empty", err) + return nil, fmt.Errorf("Certificate retrival returned empty %+v", err) } - privateKey, err := tlsCertRetriever.GetPrivateKey() - if err != nil { - return nil, fmt.Errorf("Failed to get certificate private key", err) + return nil, fmt.Errorf("Failed to get certificate private key %+v", err) } - tlsCert := tls.Certificate{ Certificate: [][]byte{leafCertificate.Raw}, PrivateKey: privateKey, Leaf: leafCertificate, } - TlsConfig := &tls.Config{ MaxVersion: tls.VersionTLS12, MinVersion: tls.VersionTLS12, @@ -78,51 +71,34 @@ func GetTlsConfig(tlsSettings localtls.TlsSettings) (*tls.Config, error){ } return TlsConfig, nil } + // Start creates the listener socket and starts the HTTPS server. func (listener *Listener) StartTLS(errChan chan error, tlsSettings localtls.TlsSettings) error { - var err error - var tlsConfig *tls.Config - - tlsConfig, err = GetTlsConfig(tlsSettings) - + tlsConfig, err := GetTlsConfig(tlsSettings) + if err != nil { + log.Printf("[Listener] Failed to compose Tls Configuration with errror: %+v", err) + return err + } server := http.Server{ TLSConfig: tlsConfig, - Handler: listener.mux, + Handler: listener.mux, } // listen on a seperate endpoint for secure tls connections listener.securelistener, err = net.Listen(listener.protocol, tlsSettings.TlsEndpoint) - if err != nil { log.Printf("[Listener] Failed to listen on TlsEndpoint: %+v", err) return err } log.Printf("[Listener] Started listening on tls endpoint %s.", tlsSettings.TlsEndpoint) - // continue to listen on the normal endpoint for http traffic, this will be supported - // for sometime until partners migrate fully to https - listener.l, err = net.Listen(listener.protocol, listener.localAddress) - - if err != nil { - log.Printf("[Listener] Failed to listen: %+v", err) - return err - } - - log.Printf("[Listener] Started listening on %s.", listener.localAddress) - // Launch goroutine for servicing https requests go func() { errChan <- server.ServeTLS(listener.securelistener, "", "") }() - // Launch goroutine for servicing http requests - go func() { - errChan <- http.Serve(listener.l, listener.mux) - }() - listener.active = true return nil - } // Start creates the listener socket and starts the HTTP server. @@ -162,7 +138,7 @@ func (listener *Listener) Stop() { // Stop servicing requests. listener.l.Close() - if(listener.securelistener != nil){ + if listener.securelistener != nil { // Stop servicing requests on secure listener listener.securelistener.Close() } diff --git a/server/tls/customCertToStore/custom_certtostore_windows.go b/server/tls/customCertToStore/custom_certtostore_windows.go index e6e6c55e66..931ed226a4 100644 --- a/server/tls/customCertToStore/custom_certtostore_windows.go +++ b/server/tls/customCertToStore/custom_certtostore_windows.go @@ -4,7 +4,6 @@ // and modified to our needs per the below license permits // https://github.com/google/certtostore - // Copyright 2017 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -42,19 +41,19 @@ import ( const ( // wincrypt.h constants - acquireCached = 0x1 // CRYPT_ACQUIRE_CACHE_FLAG - acquireSilent = 0x40 // CRYPT_ACQUIRE_SILENT_FLAG - acquireOnlyNCryptKey = 0x40000 // CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG - encodingX509ASN = 1 // X509_ASN_ENCODING - encodingPKCS7 = 65536 // PKCS_7_ASN_ENCODING - certStoreProvSystem = 10 // CERT_STORE_PROV_SYSTEM - certStoreLocalMachine = uint32(certStoreLocalMachineID << compareShift) // CERT_SYSTEM_STORE_LOCAL_MACHINE - certStoreLocalMachineID = 2 // CERT_SYSTEM_STORE_LOCAL_MACHINE_ID - compareNameStrW = 8 // CERT_COMPARE_NAME_STR_A - compareShift = 16 // CERT_COMPARE_SHIFT - ncryptKeySpec = 0xFFFFFFFF // CERT_NCRYPT_KEY_SPEC - infoSubjectFlag = 7 //CERT_INFO_SUBJECT_FLAG - findSubjectStr = compareNameStrW< Date: Wed, 4 Nov 2020 14:23:56 -0800 Subject: [PATCH 4/8] address feedback --- cns/configuration/cns_config.json | 8 ++--- cns/configuration/configuration.go | 14 ++++---- cns/service.go | 2 +- cns/service/main.go | 8 ++--- common/listener.go | 8 ++--- .../custom_certtostore_windows.go | 34 +++++++------------ server/tls/tlscertificate_retriever.go | 8 ++--- server/tls/tlscertificate_retriever_file.go | 14 ++++---- .../tls/tlscertificate_retriever_windows.go | 9 +++-- 9 files changed, 48 insertions(+), 57 deletions(-) diff --git a/cns/configuration/cns_config.json b/cns/configuration/cns_config.json index 6b61507c48..9fa17aaf80 100644 --- a/cns/configuration/cns_config.json +++ b/cns/configuration/cns_config.json @@ -15,8 +15,8 @@ "NodeSyncIntervalInSeconds": 30 }, "ChannelMode": "Direct", - "UseHttps" : false, - "TlsSubjectName" : "", - "TlsCertificateFilePath" : "", - "TlsEndpoint" : "localhost:10091" + "UseHTTPS" : false, + "TLSSubjectName" : "", + "TLSCertificatePath" : "", + "TLSEndpoint" : "localhost:10091" } diff --git a/cns/configuration/configuration.go b/cns/configuration/configuration.go index 500245b581..8ef4c60c3e 100644 --- a/cns/configuration/configuration.go +++ b/cns/configuration/configuration.go @@ -17,13 +17,13 @@ const ( ) type CNSConfig struct { - TelemetrySettings TelemetrySettings - ManagedSettings ManagedSettings - ChannelMode string - UseHttps bool - TlsSubjectName string - TlsCertificateFilePath string - TlsEndpoint string + TelemetrySettings TelemetrySettings + ManagedSettings ManagedSettings + ChannelMode string + UseHTTPS bool + TLSSubjectName string + TLSCertificatePath string + TLSEndpoint string } type TelemetrySettings struct { diff --git a/cns/service.go b/cns/service.go index 674870e5c4..7798a30db0 100644 --- a/cns/service.go +++ b/cns/service.go @@ -68,7 +68,7 @@ func (service *Service) Initialize(config *common.ServiceConfig) error { if err != nil { return err } - if config.TlsSettings.TlsEndpoint != "" { + if config.TlsSettings.TLSEndpoint != "" { // Start the listener and HTTP and HTTPS server. if err = listener.StartTLS(config.ErrChan, config.TlsSettings); err != nil { return err diff --git a/cns/service/main.go b/cns/service/main.go index 8e7fdee2e9..a07570c4a2 100644 --- a/cns/service/main.go +++ b/cns/service/main.go @@ -425,11 +425,11 @@ func main() { // Start CNS. if httpRestService != nil { - if cnsconfig.UseHttps { + if cnsconfig.UseHTTPS { config.TlsSettings = localtls.TlsSettings{ - TlsCertificateSubjectName: cnsconfig.TlsSubjectName, - TlsCertificateFilePath: cnsconfig.TlsCertificateFilePath, - TlsEndpoint: cnsconfig.TlsEndpoint, + TLSSubjectName: cnsconfig.TLSSubjectName, + TLSCertificatePath: cnsconfig.TLSCertificatePath, + TLSEndpoint: cnsconfig.TLSEndpoint, } } diff --git a/common/listener.go b/common/listener.go index ee78ec1868..bb66f95af7 100644 --- a/common/listener.go +++ b/common/listener.go @@ -62,14 +62,14 @@ func GetTlsConfig(tlsSettings localtls.TlsSettings) (*tls.Config, error) { PrivateKey: privateKey, Leaf: leafCertificate, } - TlsConfig := &tls.Config{ + tlsConfig := &tls.Config{ MaxVersion: tls.VersionTLS12, MinVersion: tls.VersionTLS12, Certificates: []tls.Certificate{ tlsCert, }, } - return TlsConfig, nil + return tlsConfig, nil } // Start creates the listener socket and starts the HTTPS server. @@ -85,12 +85,12 @@ func (listener *Listener) StartTLS(errChan chan error, tlsSettings localtls.TlsS } // listen on a seperate endpoint for secure tls connections - listener.securelistener, err = net.Listen(listener.protocol, tlsSettings.TlsEndpoint) + listener.securelistener, err = net.Listen(listener.protocol, tlsSettings.TLSEndpoint) if err != nil { log.Printf("[Listener] Failed to listen on TlsEndpoint: %+v", err) return err } - log.Printf("[Listener] Started listening on tls endpoint %s.", tlsSettings.TlsEndpoint) + log.Printf("[Listener] Started listening on tls endpoint %s.", tlsSettings.TLSEndpoint) // Launch goroutine for servicing https requests go func() { diff --git a/server/tls/customCertToStore/custom_certtostore_windows.go b/server/tls/customCertToStore/custom_certtostore_windows.go index 931ed226a4..f16815fef5 100644 --- a/server/tls/customCertToStore/custom_certtostore_windows.go +++ b/server/tls/customCertToStore/custom_certtostore_windows.go @@ -185,7 +185,6 @@ func intendedKeyUsage(enc uint32, cert *windows.CertContext) (usage uint16) { // WinCertStore is a CertStorage implementation for the Windows Certificate Store. type WinCertStore struct { - CStore windows.Handle Prov uintptr ProvName string issuers []string @@ -205,27 +204,12 @@ func OpenWinCertStore(provider, container string, issuers, intermediateIssuers [ return nil, fmt.Errorf("unable to open crypto provider or provider not available: %v", err) } - var certStore windows.Handle - if openStoreWithHandle { - // Open a handle to the system cert store - certStore, err = windows.CertOpenStore( - certStoreProvSystem, - 0, - 0, - certStoreLocalMachine, - uintptr(unsafe.Pointer(my))) - if err != nil { - return nil, fmt.Errorf("CertOpenStore returned: %v", err) - } - } - wcs := &WinCertStore{ Prov: cngProv, ProvName: provider, issuers: issuers, intermediateIssuers: intermediateIssuers, container: container, - CStore: certStore, } return wcs, nil @@ -246,12 +230,24 @@ func (w *WinCertStore) CertBySubjectName(subjectName string) (*x509.Certificate, var certContext *windows.CertContext var cert *x509.Certificate + // Open a handle to the system cert store + certStore, err := windows.CertOpenStore( + certStoreProvSystem, + 0, + 0, + certStoreLocalMachine, + uintptr(unsafe.Pointer(my))) + defer windows.CertCloseStore(certStore, 0) + if err != nil { + return nil, nil , fmt.Errorf("CertOpenStore returned: %v", err) + } + searchString, err := windows.UTF16PtrFromString(subjectName) if err != nil { return nil, nil, err } - certContext, err = findCert(w.CStore, encodingX509ASN|encodingPKCS7, 0, findSubjectStr, searchString, certContext) + certContext, err = findCert(certStore, encodingX509ASN|encodingPKCS7, 0, findSubjectStr, searchString, certContext) if err != nil { return nil, nil, err @@ -261,10 +257,6 @@ func (w *WinCertStore) CertBySubjectName(subjectName string) (*x509.Certificate, if err != nil { return nil, nil, err } - - if cert == nil { - return nil, nil, nil - } return cert, certContext, nil } diff --git a/server/tls/tlscertificate_retriever.go b/server/tls/tlscertificate_retriever.go index ec11aa3c59..aae958d57c 100644 --- a/server/tls/tlscertificate_retriever.go +++ b/server/tls/tlscertificate_retriever.go @@ -4,13 +4,13 @@ package tls // TlsCertificateSettins - Details related to the TLS certificate. type TlsSettings struct { - TlsCertificateSubjectName string - TlsCertificateFilePath string - TlsEndpoint string + TLSSubjectName string + TLSCertificatePath string + TLSEndpoint string } func GetTlsCertificateRetriever(settings TlsSettings) (TlsCertificateRetriever, error) { - if settings.TlsCertificateFilePath != ""{ + if settings.TLSCertificatePath != "" { return NewFileTlsCertificateRetriever(settings) } // if Windows build flag is set, the below will return a windows implementation diff --git a/server/tls/tlscertificate_retriever_file.go b/server/tls/tlscertificate_retriever_file.go index 8cc359ee85..b71b568213 100644 --- a/server/tls/tlscertificate_retriever_file.go +++ b/server/tls/tlscertificate_retriever_file.go @@ -12,7 +12,7 @@ import ( ) const ( - CertLabel = "CERTIFICATE" + CertLabel = "CERTIFICATE" PrivateKeyLabel = "PRIVATE KEY" ) @@ -27,9 +27,9 @@ func (fcert *filetlsCertificateRetriever) GetCertificate() (*x509.Certificate, e if block.Type == CertLabel { cert, err := x509.ParseCertificate(block.Bytes) if err != nil { - return nil, fmt.Errorf("Failed to parse certificate at location %s with error %+v",fcert.settings.TlsCertificateFilePath, err) + return nil, fmt.Errorf("Failed to parse certificate at location %s with error %+v", fcert.settings.TLSCertificatePath, err) } - if cert.IsCA != true { + if !cert.IsCA { return cert, nil } } @@ -48,18 +48,18 @@ func (fcert *filetlsCertificateRetriever) GetPrivateKey() (crypto.PrivateKey, er return pk, nil } } - return nil, fmt.Errorf("No private key found in certificate bundle located at %s", fcert.settings.TlsCertificateFilePath) + return nil, fmt.Errorf("No private key found in certificate bundle located at %s", fcert.settings.TLSCertificatePath) } // readPemFile reads a pfx certificate converts it to PEM func (fcert *filetlsCertificateRetriever) readPemFile() error { - content, err := ioutil.ReadFile(fcert.settings.TlsCertificateFilePath) + content, err := ioutil.ReadFile(fcert.settings.TLSCertificatePath) if err != nil { - return fmt.Errorf("Error reading file from path %s with error: %+v ",fcert.settings.TlsCertificateFilePath, err) + return fmt.Errorf("Error reading file from path %s with error: %+v ", fcert.settings.TLSCertificatePath, err) } pemBlock, err := pkcs12.ToPEM(content, "") if err != nil { - return fmt.Errorf("Could not convert pfx located at %s to PEM format", fcert.settings.TlsCertificateFilePath) + return fmt.Errorf("Could not convert pfx located at %s to PEM format", fcert.settings.TLSCertificatePath) } fcert.pemBlock = pemBlock return nil diff --git a/server/tls/tlscertificate_retriever_windows.go b/server/tls/tlscertificate_retriever_windows.go index b31a89a811..895c54feff 100644 --- a/server/tls/tlscertificate_retriever_windows.go +++ b/server/tls/tlscertificate_retriever_windows.go @@ -6,7 +6,7 @@ import ( "crypto" "crypto/x509" "fmt" - certtostore "github.com/Azure/azure-container-networking/server/tls/customCertToStore" + certtostore "github.com/Azure/azure-container-networking/server/tls/customcerttostore" "golang.org/x/sys/windows" ) @@ -20,12 +20,12 @@ type windowsTlsCertificateRetriever struct { // it depends on the TlsCertificateSubjectName being set // in the server settings to retrieve the cert func (wtls *windowsTlsCertificateRetriever) GetCertificate() (*x509.Certificate, error) { - if wtls.settings.TlsCertificateSubjectName == "" { + if wtls.settings.TLSSubjectName == "" { return nil, fmt.Errorf("Certificate subject name is empty in the settings") } - cert, certContext, err := wtls.certStore.CertBySubjectName(wtls.settings.TlsCertificateSubjectName) + cert, certContext, err := wtls.certStore.CertBySubjectName(wtls.settings.TLSSubjectName) if err != nil { - return nil, fmt.Errorf("Retrieving certificate with subject name %s from cert store returned error %+v",wtls.settings.TlsCertificateSubjectName, err) + return nil, fmt.Errorf("Retrieving certificate with subject name %s from cert store returned error %+v",wtls.settings.TLSSubjectName, err) } if cert == nil { return nil, fmt.Errorf("Call to cert store succeeded but gave a empty certificate") @@ -52,7 +52,6 @@ func (wtls *windowsTlsCertificateRetriever) GetPrivateKey() (crypto.PrivateKey, } // Open cert store opens the cert store -// its the responsbility of the caller to close the cert store handle func (wtls *windowsTlsCertificateRetriever) openCertStore() error { certStore, err := certtostore.OpenWinCertStore(certtostore.ProviderMSSoftware, "0", nil, nil, false, true) if err != nil { From 9e5cd12c76e913beeec9d29f45377f766318f1f3 Mon Sep 17 00:00:00 2001 From: alegal Date: Wed, 4 Nov 2020 14:43:05 -0800 Subject: [PATCH 5/8] rename file --- .../custom_certtostore_windows.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename server/tls/{customCertToStore => customcerttostore}/custom_certtostore_windows.go (100%) diff --git a/server/tls/customCertToStore/custom_certtostore_windows.go b/server/tls/customcerttostore/custom_certtostore_windows.go similarity index 100% rename from server/tls/customCertToStore/custom_certtostore_windows.go rename to server/tls/customcerttostore/custom_certtostore_windows.go From 581b7aea55320f9dd410deb63b1015d5ba2125fa Mon Sep 17 00:00:00 2001 From: alegal Date: Wed, 4 Nov 2020 17:04:20 -0800 Subject: [PATCH 6/8] small fix --- server/tls/tlscertificate_retriever_disabled.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/tls/tlscertificate_retriever_disabled.go b/server/tls/tlscertificate_retriever_disabled.go index 718e97bc14..6d2dcc4bf1 100644 --- a/server/tls/tlscertificate_retriever_disabled.go +++ b/server/tls/tlscertificate_retriever_disabled.go @@ -15,7 +15,7 @@ import ( // Linux currently uses tls file certificate retriever // this indicates the caller has not set the Tls Certificate Path in the server settings func NewTlsCertificateRetriever(settings TlsSettings) (TlsCertificateRetriever, error) { - if settings.TlsCertificateFilePath == "" { + if settings.TLSCertificatePath == "" { return nil, fmt.Errorf("TLS certificate file path not set") } return nil, fmt.Errorf("Not implemented, only windows and linux is supported") From 1748252982fe4e3a6ec679e887d1abd979a2b5eb Mon Sep 17 00:00:00 2001 From: alegal Date: Wed, 4 Nov 2020 17:05:20 -0800 Subject: [PATCH 7/8] small fix --- server/tls/tlscertificate_retriever_file_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/tls/tlscertificate_retriever_file_test.go b/server/tls/tlscertificate_retriever_file_test.go index 0b0c400f7d..a07b20c3cb 100644 --- a/server/tls/tlscertificate_retriever_file_test.go +++ b/server/tls/tlscertificate_retriever_file_test.go @@ -31,8 +31,8 @@ func TestPfxConsumption(t *testing.T) { defer os.Remove(pfxLocation) config := TlsSettings{ - TlsCertificateFilePath: pfxLocation, - TlsCertificateSubjectName: commonName, + TLSCertificatePath: pfxLocation, + TLSSubjectName: commonName, } fileCertRetriever, err := NewFileTlsCertificateRetriever(config) if err != nil { From b5b9ee24175a0ef5c30c4fd59897f3267e82cc28 Mon Sep 17 00:00:00 2001 From: alegal Date: Thu, 5 Nov 2020 13:00:09 -0800 Subject: [PATCH 8/8] fixing issue with cert not found --- server/tls/customcerttostore/custom_certtostore_windows.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/tls/customcerttostore/custom_certtostore_windows.go b/server/tls/customcerttostore/custom_certtostore_windows.go index f16815fef5..7067b8588d 100644 --- a/server/tls/customcerttostore/custom_certtostore_windows.go +++ b/server/tls/customcerttostore/custom_certtostore_windows.go @@ -253,6 +253,10 @@ func (w *WinCertStore) CertBySubjectName(subjectName string) (*x509.Certificate, return nil, nil, err } + if certContext == nil{ + return nil, nil, fmt.Errorf("Certificate not found") + } + cert, err = certContextToX509(certContext) if err != nil { return nil, nil, err