From 174671af708884dd534b662b5bdc8c89e1714bf8 Mon Sep 17 00:00:00 2001 From: Sergey Yedrikov Date: Wed, 3 Mar 2021 23:50:15 -0500 Subject: [PATCH] Bug 1904380 - Forwarding logs to Kafka using Chained certificates fails with error "state=error: certificate verify failed (unable to get local issuer certificate) - Upgraded Kafka to 2.7.0 and pushed images to quay.io - Switched the kafka logforwarding test to run over TLS - The Kafka broker certificate chain is Server - Intermediate CA - Root CA - Added Java Key Store generation functions - Removed Kafka consumer output to stdio to avoid recursive logs --- go.mod | 1 + go.sum | 5 + .../fluent/fluent_secure_test.go | 6 +- .../kafka/forward_to_kafka_test.go | 19 +- test/helpers/certificate/certificate.go | 12 +- test/helpers/certificate/certificate_jks.go | 54 +++ test/helpers/certificate/certificate_test.go | 10 +- test/helpers/kafka.go | 23 ++ test/helpers/kafka/broker.go | 53 ++- test/helpers/kafka/config.go | 19 +- test/helpers/kafka/constants.go | 4 + test/helpers/kafka/consumer.go | 32 +- test/helpers/kafka/zookeeper.go | 4 +- .../keystore-go/v4/.gitignore | 19 + .../keystore-go/v4/.gitpod.Dockerfile | 5 + .../keystore-go/v4/.gitpod.yml | 5 + .../keystore-go/v4/.golangci.yaml | 30 ++ .../pavel-v-chernykh/keystore-go/v4/LICENSE | 21 ++ .../pavel-v-chernykh/keystore-go/v4/Makefile | 31 ++ .../pavel-v-chernykh/keystore-go/v4/README.md | 90 +++++ .../pavel-v-chernykh/keystore-go/v4/common.go | 44 +++ .../keystore-go/v4/decoder.go | 224 ++++++++++++ .../keystore-go/v4/encoder.go | 177 +++++++++ .../pavel-v-chernykh/keystore-go/v4/go.mod | 3 + .../keystore-go/v4/keyprotector.go | 173 +++++++++ .../keystore-go/v4/keystore.go | 335 ++++++++++++++++++ vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go | 77 ++++ vendor/golang.org/x/crypto/scrypt/scrypt.go | 213 +++++++++++ vendor/modules.txt | 5 + 29 files changed, 1659 insertions(+), 35 deletions(-) create mode 100644 test/helpers/certificate/certificate_jks.go create mode 100644 vendor/github.com/pavel-v-chernykh/keystore-go/v4/.gitignore create mode 100644 vendor/github.com/pavel-v-chernykh/keystore-go/v4/.gitpod.Dockerfile create mode 100644 vendor/github.com/pavel-v-chernykh/keystore-go/v4/.gitpod.yml create mode 100644 vendor/github.com/pavel-v-chernykh/keystore-go/v4/.golangci.yaml create mode 100644 vendor/github.com/pavel-v-chernykh/keystore-go/v4/LICENSE create mode 100644 vendor/github.com/pavel-v-chernykh/keystore-go/v4/Makefile create mode 100644 vendor/github.com/pavel-v-chernykh/keystore-go/v4/README.md create mode 100644 vendor/github.com/pavel-v-chernykh/keystore-go/v4/common.go create mode 100644 vendor/github.com/pavel-v-chernykh/keystore-go/v4/decoder.go create mode 100644 vendor/github.com/pavel-v-chernykh/keystore-go/v4/encoder.go create mode 100644 vendor/github.com/pavel-v-chernykh/keystore-go/v4/go.mod create mode 100644 vendor/github.com/pavel-v-chernykh/keystore-go/v4/keyprotector.go create mode 100644 vendor/github.com/pavel-v-chernykh/keystore-go/v4/keystore.go create mode 100644 vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go create mode 100644 vendor/golang.org/x/crypto/scrypt/scrypt.go diff --git a/go.mod b/go.mod index 1d7537b8cb..1771bb3149 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( github.com/openshift/api v0.0.0-20200602204738-768b7001fe69 github.com/openshift/elasticsearch-operator v0.0.0-20200722044541-14fae5dcddfd github.com/operator-framework/operator-sdk v0.18.1 + github.com/pavel-v-chernykh/keystore-go/v4 v4.1.0 github.com/prometheus/procfs v0.0.10 // indirect github.com/rogpeppe/go-internal v1.5.2 // indirect github.com/spf13/pflag v1.0.5 diff --git a/go.sum b/go.sum index 11512cf194..d400a59824 100644 --- a/go.sum +++ b/go.sum @@ -758,6 +758,10 @@ github.com/otiai10/mint v1.3.1 h1:BCmzIS3n71sGfHB5NMNDB3lHYPz8fWSkCAErHed//qc= github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pavel-v-chernykh/keystore-go v1.0.0 h1:q7+IYDgDRi7uutqeqndr838MAw5t8Gp1i5GpXwqMTxs= +github.com/pavel-v-chernykh/keystore-go v2.1.0+incompatible h1:Jd6xfriVlJ6hWPvYOE0Ni0QWcNTLRehfGPFxr3eSL80= +github.com/pavel-v-chernykh/keystore-go/v4 v4.1.0 h1:xKxUVGoB9VJU+lgQLPN0KURjw+XCVVSpHfQEeyxk3zo= +github.com/pavel-v-chernykh/keystore-go/v4 v4.1.0/go.mod h1:2ejgys4qY+iNVW1IittZhyRYA6MNv8TgM6VHqojbB9g= github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -1005,6 +1009,7 @@ golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/test/e2e/logforwarding/fluent/fluent_secure_test.go b/test/e2e/logforwarding/fluent/fluent_secure_test.go index 94b3b3c49b..6994e79e24 100644 --- a/test/e2e/logforwarding/fluent/fluent_secure_test.go +++ b/test/e2e/logforwarding/fluent/fluent_secure_test.go @@ -31,9 +31,9 @@ var _ = Describe("[ClusterLogForwarder]", func() { f = NewFixture(c.NS.Name, secureMessage) // Receiver acts as TLS server. - privateCA = certificate.NewCA(nil) - serverCert = certificate.NewCert(privateCA, f.Receiver.Host()) // Receiver is server. - clientCert = certificate.NewCert(privateCA) + privateCA = certificate.NewCA(nil, "Root CA") + serverCert = certificate.NewCert(privateCA, "Server", f.Receiver.Host()) // Receiver is server. + clientCert = certificate.NewCert(privateCA, "Client") sharedKey = "top-secret" // The Receiver Sources act as TLS servers. for i, s := range []*fluentd.Source{ diff --git a/test/e2e/logforwarding/kafka/forward_to_kafka_test.go b/test/e2e/logforwarding/kafka/forward_to_kafka_test.go index 42ccf1ab8f..44361ea1a9 100644 --- a/test/e2e/logforwarding/kafka/forward_to_kafka_test.go +++ b/test/e2e/logforwarding/kafka/forward_to_kafka_test.go @@ -10,7 +10,6 @@ import ( "github.com/ViaQ/logerr/log" loggingv1 "github.com/openshift/cluster-logging-operator/pkg/apis/logging/v1" - v1 "github.com/openshift/cluster-logging-operator/pkg/apis/logging/v1" "github.com/openshift/cluster-logging-operator/test/helpers" "github.com/openshift/cluster-logging-operator/test/helpers/kafka" apps "k8s.io/api/apps/v1" @@ -63,6 +62,9 @@ var _ = Describe("[ClusterLogForwarder] Forwards logs", func() { e2e.LogStores[app.Name].ClusterLocalEndpoint(), kafka.DefaultTopic, ), + Secret: &loggingv1.OutputSecretSpec{ + Name: kafka.DeploymentName, + }, }, }, Pipelines: []loggingv1.PipelineSpec{ @@ -152,30 +154,39 @@ var _ = Describe("[ClusterLogForwarder] Forwards logs", func() { Type: loggingv1.OutputTypeKafka, URL: fmt.Sprintf("tls://%s", e2e.LogStores[app.Name].ClusterLocalEndpoint()), OutputTypeSpec: loggingv1.OutputTypeSpec{ - Kafka: &v1.Kafka{ + Kafka: &loggingv1.Kafka{ Topic: kafka.AppLogsTopic, }, }, + Secret: &loggingv1.OutputSecretSpec{ + Name: kafka.DeploymentName, + }, }, { Name: fmt.Sprintf("%s-audit-out", kafka.DeploymentName), Type: loggingv1.OutputTypeKafka, URL: fmt.Sprintf("tls://%s", e2e.LogStores[app.Name].ClusterLocalEndpoint()), OutputTypeSpec: loggingv1.OutputTypeSpec{ - Kafka: &v1.Kafka{ + Kafka: &loggingv1.Kafka{ Topic: kafka.AuditLogsTopic, }, }, + Secret: &loggingv1.OutputSecretSpec{ + Name: kafka.DeploymentName, + }, }, { Name: fmt.Sprintf("%s-infra-out", kafka.DeploymentName), Type: loggingv1.OutputTypeKafka, URL: fmt.Sprintf("tls://%s", e2e.LogStores[app.Name].ClusterLocalEndpoint()), OutputTypeSpec: loggingv1.OutputTypeSpec{ - Kafka: &v1.Kafka{ + Kafka: &loggingv1.Kafka{ Topic: kafka.InfraLogsTopic, }, }, + Secret: &loggingv1.OutputSecretSpec{ + Name: kafka.DeploymentName, + }, }, }, Pipelines: []loggingv1.PipelineSpec{ diff --git a/test/helpers/certificate/certificate.go b/test/helpers/certificate/certificate.go index 5810a48fd8..609340b20d 100644 --- a/test/helpers/certificate/certificate.go +++ b/test/helpers/certificate/certificate.go @@ -95,11 +95,11 @@ func (ck *CertKey) StartServer(handler http.Handler, clientCA *CertKey) *httptes } // NewCA creates a new dummy CA cert signed by signer, or self-signed if signer is nil. -func NewCA(signer *CertKey) *CertKey { +func NewCA(signer *CertKey, org string) *CertKey { return New(&x509.Certificate{ SerialNumber: big.NewInt(1234), Subject: pkix.Name{ - Organization: []string{"Testing, INC."}, + Organization: []string{org}, // Country: []string{"US"}, // Province: []string{""}, // Locality: []string{"San Francisco"}, @@ -118,7 +118,7 @@ func NewCA(signer *CertKey) *CertKey { // NewCert creates a dummy server cert signed by signer, or self-signed if signer is nil. // The addrs list can contain strings (DNS names) or net.IP addresses, if addrs // is empty will use "localhost", v4 and v6 loopback -func NewCert(signer *CertKey, addrs ...interface{}) *CertKey { +func NewCert(signer *CertKey, org string, addrs ...interface{}) *CertKey { var ( dns []string ips []net.IP @@ -138,7 +138,7 @@ func NewCert(signer *CertKey, addrs ...interface{}) *CertKey { } return New(&x509.Certificate{ SerialNumber: big.NewInt(1234), - Subject: pkix.Name{Organization: []string{"Testing, INC."}}, + Subject: pkix.Name{Organization: []string{org}}, NotBefore: time.Now(), NotAfter: time.Now().AddDate(10, 0, 0), SubjectKeyId: []byte{1, 2, 3, 4, 6}, @@ -151,7 +151,7 @@ func NewCert(signer *CertKey, addrs ...interface{}) *CertKey { } // NewServer creates a dummy client cert signed by signer, or self-signed if signer is nil. -func NewClient(signer *CertKey) *CertKey { +func NewClient(signer *CertKey, org string) *CertKey { // Its the same as a server cert but with no DNS/IP addrs. - return NewCert(signer) + return NewCert(signer, org) } diff --git a/test/helpers/certificate/certificate_jks.go b/test/helpers/certificate/certificate_jks.go new file mode 100644 index 0000000000..8e46461c33 --- /dev/null +++ b/test/helpers/certificate/certificate_jks.go @@ -0,0 +1,54 @@ +package certificate + +import ( + "bytes" + "crypto/x509" + "strconv" + "time" + + "github.com/openshift/cluster-logging-operator/test" + "github.com/pavel-v-chernykh/keystore-go/v4" +) + +func JKSKeyStore(certKey *CertKey, password string) []byte { + ks := keystore.New() + keyPKCS8Bytes, err := x509.MarshalPKCS8PrivateKey(certKey.PrivateKey) + test.Must(err) + pke := keystore.PrivateKeyEntry{ + CreationTime: time.Now(), + PrivateKey: keyPKCS8Bytes, + CertificateChain: []keystore.Certificate{ + { + Type: "X509", + Content: certKey.DERBytes, + }, + }, + } + err = ks.SetPrivateKeyEntry("alias", pke, []byte(password)) + test.Must(err) + var buf bytes.Buffer + err = ks.Store(&buf, []byte(password)) + test.Must(err) + return buf.Bytes() +} + +func JKSTrustStore(certKeys []*CertKey, password string) []byte { + ks := keystore.New() + for i := range certKeys { + err := ks.SetTrustedCertificateEntry( + strconv.Itoa(i), + keystore.TrustedCertificateEntry{ + CreationTime: time.Now(), + Certificate: keystore.Certificate{ + Type: "X509", + Content: certKeys[i].DERBytes, + }, + }, + ) + test.Must(err) + } + var buf bytes.Buffer + err := ks.Store(&buf, []byte(password)) + test.Must(err) + return buf.Bytes() +} diff --git a/test/helpers/certificate/certificate_test.go b/test/helpers/certificate/certificate_test.go index 4b9458406e..7e9ee564b1 100644 --- a/test/helpers/certificate/certificate_test.go +++ b/test/helpers/certificate/certificate_test.go @@ -16,8 +16,8 @@ import ( var _ = Describe("Certificate", func() { It("enables client-server TLS connection with local CA", func() { - ca := certificate.NewCA(nil) // Self-signed CA - cert := certificate.NewCert(ca, "localhost", net.IPv4(127, 0, 0, 1), net.IPv6loopback) + ca := certificate.NewCA(nil, "Root CA") // Self-signed CA + cert := certificate.NewCert(ca, "Server", "localhost", net.IPv4(127, 0, 0, 1), net.IPv6loopback) server := cert.StartServer(http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "success!") }), nil) defer server.Close() @@ -34,13 +34,13 @@ var _ = Describe("Certificate", func() { }) It("enables TLS mutual authentication", func() { - ca := certificate.NewCA(nil) // Self-signed CA - serverCert := certificate.NewCert(ca) + ca := certificate.NewCA(nil, "Root CA") // Self-signed CA + serverCert := certificate.NewCert(ca, "Server") server := serverCert.StartServer(http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "success!") }), ca) defer server.Close() - clientCert := certificate.NewClient(ca) + clientCert := certificate.NewClient(ca, "Client") client := ca.Client(clientCert) resp, err := client.Get(server.URL) ExpectOK(err) diff --git a/test/helpers/kafka.go b/test/helpers/kafka.go index 98efe4f6f6..92be94219e 100644 --- a/test/helpers/kafka.go +++ b/test/helpers/kafka.go @@ -176,6 +176,10 @@ func (tc *E2ETestFramework) createKafkaBroker() (*apps.StatefulSet, error) { return nil, err } + if err := tc.createKafkaBrokerSecret(); err != nil { + return nil, err + } + if err := tc.createKafkaBrokerService(); err != nil { return nil, err } @@ -357,6 +361,25 @@ func (tc *E2ETestFramework) createKafkaBrokerConfigMap() error { return nil } +func (tc *E2ETestFramework) createKafkaBrokerSecret() error { + s := kafka.NewBrokerSecret(OpenshiftLoggingNS) + + tc.AddCleanup(func() error { + var zerograce int64 + opts := metav1.DeleteOptions{ + GracePeriodSeconds: &zerograce, + } + return tc.KubeClient.CoreV1().Secrets(OpenshiftLoggingNS).Delete(context.TODO(), s.GetName(), opts) + }) + + opts := metav1.CreateOptions{} + if _, err := tc.KubeClient.CoreV1().Secrets(OpenshiftLoggingNS).Create(context.TODO(), s, opts); err != nil { + return err + } + + return nil +} + func (tc *E2ETestFramework) createZookeeperConfigMap() error { cm := kafka.NewZookeeperConfigMap(OpenshiftLoggingNS) diff --git a/test/helpers/kafka/broker.go b/test/helpers/kafka/broker.go index 285ec18bb5..6f9ff91e6f 100644 --- a/test/helpers/kafka/broker.go +++ b/test/helpers/kafka/broker.go @@ -1,10 +1,13 @@ package kafka import ( + "bytes" "fmt" + "strconv" "github.com/openshift/cluster-logging-operator/pkg/factory" "github.com/openshift/cluster-logging-operator/pkg/k8shandler" + "github.com/openshift/cluster-logging-operator/test/helpers/certificate" apps "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" @@ -20,7 +23,7 @@ const ( kafkaBrokerProvider = "openshift" kafkaNodeReader = "kafka-node-reader" kafkaNodeReaderBinding = "kafka-node-reader-binding" - kafkaInsidePort = 9092 + kafkaInsidePort = 9093 kafkaOutsidePort = 9094 kafkaJMXPort = 5555 ) @@ -84,7 +87,7 @@ func NewBrokerStatefuleSet(namespace string) *apps.StatefulSet { InitContainers: []v1.Container{ { Name: "init-config", - Image: "solsson/kafka-initutils@sha256:f6d9850c6c3ad5ecc35e717308fddb47daffbde18eb93e98e031128fe8b899ef", + Image: KafkaInitUtilsImage, Env: []v1.EnvVar{ { Name: "NODE_NAME", @@ -138,7 +141,7 @@ func NewBrokerStatefuleSet(namespace string) *apps.StatefulSet { Containers: []v1.Container{ { Name: kafkaBrokerContainerName, - Image: "solsson/kafka:2.4.1", + Image: KafkaImage, Env: []v1.EnvVar{ { Name: "CLASSPATH", @@ -150,7 +153,7 @@ func NewBrokerStatefuleSet(namespace string) *apps.StatefulSet { }, { Name: "JMX_PORT", - Value: "5555", + Value: strconv.Itoa(kafkaJMXPort), }, }, Ports: []v1.ContainerPort{ @@ -159,7 +162,7 @@ func NewBrokerStatefuleSet(namespace string) *apps.StatefulSet { ContainerPort: kafkaInsidePort, }, { - Name: "outide", + Name: "outside", ContainerPort: kafkaOutsidePort, }, { @@ -204,6 +207,10 @@ func NewBrokerStatefuleSet(namespace string) *apps.StatefulSet { Name: "brokerconfig", MountPath: "/etc/kafka-configmap", }, + { + Name: "brokercerts", + MountPath: "/etc/kafka-certs", + }, { Name: "config", MountPath: "/etc/kafka", @@ -234,6 +241,14 @@ func NewBrokerStatefuleSet(namespace string) *apps.StatefulSet { }, }, }, + { + Name: "brokercerts", + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: DeploymentName, + }, + }, + }, { Name: "brokerlogs", VolumeSource: v1.VolumeSource{ @@ -262,8 +277,12 @@ func NewBrokerStatefuleSet(namespace string) *apps.StatefulSet { func NewBrokerService(namespace string) *v1.Service { ports := []v1.ServicePort{ { - Name: "server", - Port: kafkaInsidePort, + Name: "plaintext", + Port: 9092, + }, + { + Name: "ssl", + Port: 9093, }, } return factory.NewService(DeploymentName, namespace, kafkaBrokerComponent, ports) @@ -313,7 +332,27 @@ func NewBrokerConfigMap(namespace string) *v1.ConfigMap { data := map[string]string{ "init.sh": initKafkaScript, "server.properties": serverProperties, + "client.properties": clientProperties, "log4j.properties": log4jProperties, } return k8shandler.NewConfigMap(DeploymentName, namespace, data) } + +func NewBrokerSecret(namespace string) *v1.Secret { + rootCA := certificate.NewCA(nil, "Root CA") + intermediateCA := certificate.NewCA(rootCA, "Intermediate CA") + serverCert := certificate.NewCert(intermediateCA, "Server", fmt.Sprintf("%s.%s.svc.cluster.local", DeploymentName, namespace)) + + data := map[string][]byte{ + "server.jks": certificate.JKSKeyStore(serverCert, "server"), + "ca-bundle.jks": certificate.JKSTrustStore([]*certificate.CertKey{rootCA, intermediateCA}, "ca-bundle"), + "ca-bundle.crt": bytes.Join([][]byte{rootCA.CertificatePEM(), intermediateCA.CertificatePEM()}, []byte{}), + } + + secret := k8shandler.NewSecret( + DeploymentName, + namespace, + data, + ) + return secret +} diff --git a/test/helpers/kafka/config.go b/test/helpers/kafka/config.go index 9fe8788f1b..56aa5df828 100644 --- a/test/helpers/kafka/config.go +++ b/test/helpers/kafka/config.go @@ -23,10 +23,7 @@ const ( fi [ -z "$ADVERTISE_ADDR" ] && echo "ADVERTISE_ADDR is empty, will advertise detected DNS name" - OUTSIDE_HOST=$(kubectl get node "$NODE_NAME" -o jsonpath='{.status.addresses[?(@.type=="InternalIP")].address}') - OUTSIDE_PORT=$((32400 + ${KAFKA_BROKER_ID})) - SEDS+=("s|#init#advertised.listeners=PLAINTEXT://#init#|advertised.listeners=PLAINTEXT://${ADVERTISE_ADDR}:9092,OUTSIDE://${OUTSIDE_HOST}:${OUTSIDE_PORT}|") - ANNOTATIONS="$ANNOTATIONS kafka-listener-outside-host=$OUTSIDE_HOST kafka-listener-outside-port=$OUTSIDE_PORT" + SEDS+=("s|#init#advertised.listeners=PLAINTEXT://#init#|advertised.listeners=PLAINTEXT://${ADVERTISE_ADDR}:9092,SSL://${ADVERTISE_ADDR}:9093|") if [ ! -z "$LABELS" ]; then kubectl -n $POD_NAMESPACE label pod $POD_NAME $LABELS || echo "Failed to label $POD_NAMESPACE.$POD_NAME - RBAC issue?" @@ -37,6 +34,15 @@ const ( } printf '%s\n' "${SEDS[@]}" | sed -f - /etc/kafka-configmap/server.properties > /etc/kafka/server.properties.tmp [ $? -eq 0 ] && mv /etc/kafka/server.properties.tmp /etc/kafka/server.properties + + rm -rf /var/lib/kafka/data/* + ` + + clientProperties = ` + security.protocol=SSL + ssl.truststore.location=/etc/kafka-certs/ca-bundle.jks + ssl.truststore.type=JKS + ssl.truststore.password=ca-bundle ` serverProperties = ` @@ -80,7 +86,10 @@ const ( # EXAMPLE: # listeners = PLAINTEXT://your.host.name:9092 #listeners=PLAINTEXT://:9092 - listeners=PLAINTEXT://:9092,OUTSIDE://:9094 + listeners=PLAINTEXT://:9092,SSL://:9093 + ssl.keystore.type=JKS + ssl.keystore.location=/etc/kafka-certs/server.jks + ssl.keystore.password=server # Hostname and port the broker will advertise to producers and consumers. If not set, # it uses the value for "listeners" if configured. Otherwise, it will use the value diff --git a/test/helpers/kafka/constants.go b/test/helpers/kafka/constants.go index c6f7cf9cf6..08d70f4c0d 100644 --- a/test/helpers/kafka/constants.go +++ b/test/helpers/kafka/constants.go @@ -13,6 +13,10 @@ const ( InfraLogsTopic = "clo-infra-topic" DeploymentName = "kafka" ConsumerDeploymentName = "kafka-consumer" + kafkaImageRepoOrg = "quay.io/openshift-logging/" + kafkaImageTag = "2.7.0" + KafkaImage = kafkaImageRepoOrg + "kafka:" + kafkaImageTag + KafkaInitUtilsImage = kafkaImageRepoOrg + "kafka-initutils:" + kafkaImageTag ) var ( diff --git a/test/helpers/kafka/consumer.go b/test/helpers/kafka/consumer.go index 6504e371ac..eb2cfe9500 100644 --- a/test/helpers/kafka/consumer.go +++ b/test/helpers/kafka/consumer.go @@ -15,7 +15,7 @@ func NewKafkaConsumerDeployment(namespace, topic string) *apps.Deployment { InitContainers: []v1.Container{ { Name: "topic-create", - Image: "solsson/kafka-cli@sha256:9fa3306e9f5d18283d10e01f7c115d8321eedc682f262aff784bd0126e1f2221", + Image: KafkaImage, Command: []string{ "./bin/kafka-topics.sh", "--zookeeper", @@ -40,17 +40,25 @@ func NewKafkaConsumerDeployment(namespace, topic string) *apps.Deployment { Containers: []v1.Container{ { Name: name, - Image: "solsson/kafka:2.4.1", + Image: KafkaImage, Command: []string{ "/bin/bash", "-ce", fmt.Sprintf( - `./bin/kafka-console-consumer.sh --bootstrap-server %s --topic %s --from-beginning | tee /shared/consumed.logs`, + `./bin/kafka-console-consumer.sh --bootstrap-server %s --topic %s --from-beginning --consumer.config /etc/kafka-configmap/client.properties > /shared/consumed.logs`, ClusterLocalEndpoint(namespace), topic, ), }, VolumeMounts: []v1.VolumeMount{ + { + Name: "brokerconfig", + MountPath: "/etc/kafka-configmap", + }, + { + Name: "brokercerts", + MountPath: "/etc/kafka-certs", + }, { Name: "shared", MountPath: "/shared", @@ -59,6 +67,24 @@ func NewKafkaConsumerDeployment(namespace, topic string) *apps.Deployment { }, }, Volumes: []v1.Volume{ + { + Name: "brokerconfig", + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: DeploymentName, + }, + }, + }, + }, + { + Name: "brokercerts", + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: DeploymentName, + }, + }, + }, { Name: "shared", VolumeSource: v1.VolumeSource{ diff --git a/test/helpers/kafka/zookeeper.go b/test/helpers/kafka/zookeeper.go index 56ae6f5ffd..1a5b6d6b45 100644 --- a/test/helpers/kafka/zookeeper.go +++ b/test/helpers/kafka/zookeeper.go @@ -77,7 +77,7 @@ func NewZookeeperStatefuleSet(namespace string) *apps.StatefulSet { InitContainers: []v1.Container{ { Name: "init-config", - Image: "solsson/kafka-initutils@sha256:f6d9850c6c3ad5ecc35e717308fddb47daffbde18eb93e98e031128fe8b899ef", + Image: KafkaInitUtilsImage, Command: []string{ "/bin/bash", "/etc/kafka-configmap/init.sh", @@ -101,7 +101,7 @@ func NewZookeeperStatefuleSet(namespace string) *apps.StatefulSet { Containers: []v1.Container{ { Name: zookeeperDeploymentName, - Image: "solsson/kafka:2.4.1", + Image: KafkaImage, Env: []v1.EnvVar{ { diff --git a/vendor/github.com/pavel-v-chernykh/keystore-go/v4/.gitignore b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/.gitignore new file mode 100644 index 0000000000..ce35871a73 --- /dev/null +++ b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/.gitignore @@ -0,0 +1,19 @@ +*.o +*.a +*.so +_obj +_test +*.[568vq] +[568vq].out +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* +_testmain.go +*.exe +*.test +*.prof +*.iml +.idea +coverage.out diff --git a/vendor/github.com/pavel-v-chernykh/keystore-go/v4/.gitpod.Dockerfile b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/.gitpod.Dockerfile new file mode 100644 index 0000000000..a6dc304e58 --- /dev/null +++ b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/.gitpod.Dockerfile @@ -0,0 +1,5 @@ +FROM gitpod/workspace-full + +RUN brew update && brew install golangci-lint + +# More information: https://www.gitpod.io/docs/config-docker/ diff --git a/vendor/github.com/pavel-v-chernykh/keystore-go/v4/.gitpod.yml b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/.gitpod.yml new file mode 100644 index 0000000000..04f980928f --- /dev/null +++ b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/.gitpod.yml @@ -0,0 +1,5 @@ +image: + file: .gitpod.Dockerfile + +tasks: + - command: make diff --git a/vendor/github.com/pavel-v-chernykh/keystore-go/v4/.golangci.yaml b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/.golangci.yaml new file mode 100644 index 0000000000..c01fff5c54 --- /dev/null +++ b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/.golangci.yaml @@ -0,0 +1,30 @@ +modules-download-mode: readonly + +linters: + enable-all: true + disable: + - gochecknoglobals + - funlen + - goerr113 + - gofumpt + - exhaustivestruct + +linters-settings: + gomnd: + settings: + mnd: + checks: case,condition,return + +issues: + exclude-rules: + - path: _test\.go + linters: + - testpackage + - maligned + - dupl + - linters: + - gosec + text: "G401: " + - linters: + - gosec + text: "G505: " diff --git a/vendor/github.com/pavel-v-chernykh/keystore-go/v4/LICENSE b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/LICENSE new file mode 100644 index 0000000000..b6f8c3a145 --- /dev/null +++ b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Pavel Chernykh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/pavel-v-chernykh/keystore-go/v4/Makefile b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/Makefile new file mode 100644 index 0000000000..424b9d1d1c --- /dev/null +++ b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/Makefile @@ -0,0 +1,31 @@ +fmt: + go fmt github.com/pavel-v-chernykh/keystore-go/v4/... + +lint: + golangci-lint run -c .golangci.yaml + +lint-examples: + cd examples/compare && golangci-lint run -c ../../.golangci.yaml + cd examples/keypass && golangci-lint run -c ../../.golangci.yaml + cd examples/pem && golangci-lint run -c ../../.golangci.yaml + cd examples/truststore && golangci-lint run -c ../../.golangci.yaml + +run-examples: + cd examples/compare && go run main.go + cd examples/keypass && go run main.go + cd examples/pem && go run main.go + cd examples/truststore && go run main.go "/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/security/cacerts" "changeit" + +test: + go test -cover -count=1 -v ./... + +test-coverprofile: + go test -coverprofile=coverage.out -cover -count=1 -v ./... + +cover: + go tool cover -html=coverage.out + +all: fmt lint test + +.PHONY: fmt lint test all +.DEFAULT_GOAL := all diff --git a/vendor/github.com/pavel-v-chernykh/keystore-go/v4/README.md b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/README.md new file mode 100644 index 0000000000..435106b39a --- /dev/null +++ b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/README.md @@ -0,0 +1,90 @@ +[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/pavel-v-chernykh/keystore-go) + +# Keystore +A go (golang) implementation of Java [KeyStore][1] encoder/decoder + +Take into account that JKS assumes that private keys are PKCS8 encoded. + +### Example + +```go +package main + +import ( + "log" + "os" + "reflect" + + "github.com/pavel-v-chernykh/keystore-go/v4" +) + +func readKeyStore(filename string, password []byte) keystore.KeyStore { + f, err := os.Open(filename) + if err != nil { + log.Fatal(err) + } + + defer func() { + if err := f.Close(); err != nil { + log.Fatal(err) + } + }() + + keyStore := keystore.New() + if err := keyStore.Load(f, password); err != nil { + log.Fatal(err) // nolint: gocritic + } + + return keyStore +} + +func writeKeyStore(keyStore keystore.KeyStore, filename string, password []byte) { + f, err := os.Create(filename) + if err != nil { + log.Fatal(err) + } + + defer func() { + if err := f.Close(); err != nil { + log.Fatal(err) + } + }() + + err = keyStore.Store(f, password) + if err != nil { + log.Fatal(err) // nolint: gocritic + } +} + +func zeroing(s []byte) { + for i := 0; i < len(s); i++ { + s[i] = 0 + } +} + +func main() { + password := []byte{'p', 'a', 's', 's', 'w', 'o', 'r', 'd'} + defer zeroing(password) + ks1 := readKeyStore("keystore.jks", password) + + writeKeyStore(ks1, "keystore2.jks", password) + + ks2 := readKeyStore("keystore2.jks", password) + + log.Printf("is equal: %v\n", reflect.DeepEqual(ks1, ks2)) +} +``` + +For more examples explore [examples](examples) dir + +## Development + +1. Install [go][2] +2. Install [golangci-lint][3] +3. Clone the repo `git clone git@github.com:pavel-v-chernykh/keystore-go.git` +4. Go to the project dir `cd keystore-go` +5. Run `make` to format, test and lint + +[1]: https://docs.oracle.com/javase/7/docs/technotes/guides/security/crypto/CryptoSpec.html#KeyManagement +[2]: https://golang.org +[3]: https://github.com/golangci/golangci-lint diff --git a/vendor/github.com/pavel-v-chernykh/keystore-go/v4/common.go b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/common.go new file mode 100644 index 0000000000..2052c6b177 --- /dev/null +++ b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/common.go @@ -0,0 +1,44 @@ +package keystore + +import ( + "encoding/binary" + "time" +) + +const magic uint32 = 0xfeedfeed +const ( + version01 uint32 = 1 + version02 uint32 = 2 +) +const ( + privateKeyTag uint32 = 1 + trustedCertificateTag uint32 = 2 +) +const bufSize = 1024 + +var byteOrder = binary.BigEndian + +var whitenerMessage = []byte("Mighty Aphrodite") + +func passwordBytes(password []byte) []byte { + result := make([]byte, 0, len(password)*2) + for _, b := range password { + result = append(result, 0, b) + } + + return result +} + +func zeroing(buf []byte) { + for i := 0; i < len(buf); i++ { + buf[i] = 0 + } +} + +func millisecondsToTime(ms int64) time.Time { + return time.Unix(0, ms*int64(time.Millisecond)) +} + +func timeToMilliseconds(t time.Time) int64 { + return t.UnixNano() / int64(time.Millisecond) +} diff --git a/vendor/github.com/pavel-v-chernykh/keystore-go/v4/decoder.go b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/decoder.go new file mode 100644 index 0000000000..0be7ab408c --- /dev/null +++ b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/decoder.go @@ -0,0 +1,224 @@ +package keystore + +import ( + "errors" + "fmt" + "hash" + "io" +) + +const defaultCertificateType = "X509" + +type keyStoreDecoder struct { + r io.Reader + b [bufSize]byte + md hash.Hash +} + +func (ksd *keyStoreDecoder) readUint16() (uint16, error) { + const blockSize = 2 + + if _, err := io.ReadFull(ksd.r, ksd.b[:blockSize]); err != nil { + return 0, fmt.Errorf("read uint16: %w", err) + } + + if _, err := ksd.md.Write(ksd.b[:blockSize]); err != nil { + return 0, fmt.Errorf("update digest: %w", err) + } + + return byteOrder.Uint16(ksd.b[:blockSize]), nil +} + +func (ksd *keyStoreDecoder) readUint32() (uint32, error) { + const blockSize = 4 + + if _, err := io.ReadFull(ksd.r, ksd.b[:blockSize]); err != nil { + return 0, fmt.Errorf("read uint32: %w", err) + } + + if _, err := ksd.md.Write(ksd.b[:blockSize]); err != nil { + return 0, fmt.Errorf("update digest: %w", err) + } + + return byteOrder.Uint32(ksd.b[:blockSize]), nil +} + +func (ksd *keyStoreDecoder) readUint64() (uint64, error) { + const blockSize = 8 + + if _, err := io.ReadFull(ksd.r, ksd.b[:blockSize]); err != nil { + return 0, fmt.Errorf("read uint64: %w", err) + } + + if _, err := ksd.md.Write(ksd.b[:blockSize]); err != nil { + return 0, fmt.Errorf("update digest: %w", err) + } + + return byteOrder.Uint64(ksd.b[:blockSize]), nil +} + +func (ksd *keyStoreDecoder) readBytes(num uint32) ([]byte, error) { + var result []byte + + for lenToRead := num; lenToRead > 0; { + blockSize := lenToRead + if blockSize > bufSize { + blockSize = bufSize + } + + if _, err := io.ReadFull(ksd.r, ksd.b[:blockSize]); err != nil { + return result, fmt.Errorf("read %d bytes: %w", num, err) + } + + result = append(result, ksd.b[:blockSize]...) + lenToRead -= blockSize + } + + if _, err := ksd.md.Write(result); err != nil { + return nil, fmt.Errorf("update digest: %w", err) + } + + return result, nil +} + +func (ksd *keyStoreDecoder) readString() (string, error) { + strLen, err := ksd.readUint16() + if err != nil { + return "", fmt.Errorf("read length: %w", err) + } + + strBody, err := ksd.readBytes(uint32(strLen)) + if err != nil { + return "", fmt.Errorf("read body: %w", err) + } + + return string(strBody), nil +} + +func (ksd *keyStoreDecoder) readCertificate(version uint32) (Certificate, error) { + var certType string + + switch version { + case version01: + certType = defaultCertificateType + case version02: + readCertType, err := ksd.readString() + if err != nil { + return Certificate{}, fmt.Errorf("read type: %w", err) + } + + certType = readCertType + default: + return Certificate{}, errors.New("got unknown version") + } + + certLen, err := ksd.readUint32() + if err != nil { + return Certificate{}, fmt.Errorf("read length: %w", err) + } + + certContent, err := ksd.readBytes(certLen) + if err != nil { + return Certificate{}, fmt.Errorf("read content: %w", err) + } + + certificate := Certificate{ + Type: certType, + Content: certContent, + } + + return certificate, nil +} + +func (ksd *keyStoreDecoder) readPrivateKeyEntry(version uint32) (PrivateKeyEntry, error) { + creationTimeStamp, err := ksd.readUint64() + if err != nil { + return PrivateKeyEntry{}, fmt.Errorf("read creation timestamp: %w", err) + } + + length, err := ksd.readUint32() + if err != nil { + return PrivateKeyEntry{}, fmt.Errorf("read length: %w", err) + } + + encryptedPrivateKey, err := ksd.readBytes(length) + if err != nil { + return PrivateKeyEntry{}, fmt.Errorf("read encrypted private key: %w", err) + } + + certNum, err := ksd.readUint32() + if err != nil { + return PrivateKeyEntry{}, fmt.Errorf("read number of certificates: %w", err) + } + + chain := make([]Certificate, 0, certNum) + + for i := uint32(0); i < certNum; i++ { + cert, err := ksd.readCertificate(version) + if err != nil { + return PrivateKeyEntry{}, fmt.Errorf("read %d certificate: %w", i, err) + } + + chain = append(chain, cert) + } + + creationDateTime := millisecondsToTime(int64(creationTimeStamp)) + privateKeyEntry := PrivateKeyEntry{ + encryptedPrivateKey: encryptedPrivateKey, + CreationTime: creationDateTime, + CertificateChain: chain, + } + + return privateKeyEntry, nil +} + +func (ksd *keyStoreDecoder) readTrustedCertificateEntry(version uint32) (TrustedCertificateEntry, error) { + creationTimeStamp, err := ksd.readUint64() + if err != nil { + return TrustedCertificateEntry{}, fmt.Errorf("read creation timestamp: %w", err) + } + + certificate, err := ksd.readCertificate(version) + if err != nil { + return TrustedCertificateEntry{}, fmt.Errorf("read certificate: %w", err) + } + + creationDateTime := millisecondsToTime(int64(creationTimeStamp)) + trustedCertificateEntry := TrustedCertificateEntry{ + CreationTime: creationDateTime, + Certificate: certificate, + } + + return trustedCertificateEntry, nil +} + +func (ksd *keyStoreDecoder) readEntry(version uint32) (string, interface{}, error) { + tag, err := ksd.readUint32() + if err != nil { + return "", nil, fmt.Errorf("read tag: %w", err) + } + + alias, err := ksd.readString() + if err != nil { + return "", nil, fmt.Errorf("read alias: %w", err) + } + + switch tag { + case privateKeyTag: + entry, err := ksd.readPrivateKeyEntry(version) + if err != nil { + return "", nil, fmt.Errorf("read private key entry: %w", err) + } + + return alias, entry, nil + case trustedCertificateTag: + entry, err := ksd.readTrustedCertificateEntry(version) + if err != nil { + return "", nil, fmt.Errorf("read trusted certificate entry: %w", err) + } + + return alias, entry, nil + default: + return "", nil, errors.New("got unknown entry tag") + } +} diff --git a/vendor/github.com/pavel-v-chernykh/keystore-go/v4/encoder.go b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/encoder.go new file mode 100644 index 0000000000..975f427cf4 --- /dev/null +++ b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/encoder.go @@ -0,0 +1,177 @@ +package keystore + +import ( + "fmt" + "hash" + "io" + "math" +) + +type keyStoreEncoder struct { + w io.Writer + b [bufSize]byte + md hash.Hash +} + +func (kse *keyStoreEncoder) writeUint16(value uint16) error { + const blockSize = 2 + + byteOrder.PutUint16(kse.b[:blockSize], value) + + if _, err := kse.w.Write(kse.b[:blockSize]); err != nil { + return fmt.Errorf("write uint16: %w", err) + } + + if _, err := kse.md.Write(kse.b[:blockSize]); err != nil { + return fmt.Errorf("update digest: %w", err) + } + + return nil +} + +func (kse *keyStoreEncoder) writeUint32(value uint32) error { + const blockSize = 4 + + byteOrder.PutUint32(kse.b[:blockSize], value) + + if _, err := kse.w.Write(kse.b[:blockSize]); err != nil { + return fmt.Errorf("write uint32: %w", err) + } + + if _, err := kse.md.Write(kse.b[:blockSize]); err != nil { + return fmt.Errorf("update digest: %w", err) + } + + return nil +} + +func (kse *keyStoreEncoder) writeUint64(value uint64) error { + const blockSize = 8 + + byteOrder.PutUint64(kse.b[:blockSize], value) + + if _, err := kse.w.Write(kse.b[:blockSize]); err != nil { + return fmt.Errorf("write uint64: %w", err) + } + + if _, err := kse.md.Write(kse.b[:blockSize]); err != nil { + return fmt.Errorf("update digest: %w", err) + } + + return nil +} + +func (kse *keyStoreEncoder) writeBytes(value []byte) error { + if _, err := kse.w.Write(value); err != nil { + return fmt.Errorf("write %d bytes: %w", len(value), err) + } + + if _, err := kse.md.Write(value); err != nil { + return fmt.Errorf("update digest: %w", err) + } + + return nil +} + +func (kse *keyStoreEncoder) writeString(value string) error { + strLen := len(value) + if strLen > math.MaxUint16 { + return fmt.Errorf("got string %d bytes long, max length is %d", strLen, math.MaxUint16) + } + + if err := kse.writeUint16(uint16(strLen)); err != nil { + return fmt.Errorf("write length: %w", err) + } + + if err := kse.writeBytes([]byte(value)); err != nil { + return fmt.Errorf("write body: %w", err) + } + + return nil +} + +func (kse *keyStoreEncoder) writeCertificate(cert Certificate) error { + if err := kse.writeString(cert.Type); err != nil { + return fmt.Errorf("write type: %w", err) + } + + certLen := uint64(len(cert.Content)) + if certLen > math.MaxUint32 { + return fmt.Errorf("got certificate %d bytes long, max length is %d", certLen, uint64(math.MaxUint32)) + } + + if err := kse.writeUint32(uint32(certLen)); err != nil { + return fmt.Errorf("write length: %w", err) + } + + if err := kse.writeBytes(cert.Content); err != nil { + return fmt.Errorf("write content: %w", err) + } + + return nil +} + +func (kse *keyStoreEncoder) writePrivateKeyEntry(alias string, pke PrivateKeyEntry) error { + if err := kse.writeUint32(privateKeyTag); err != nil { + return fmt.Errorf("write tag: %w", err) + } + + if err := kse.writeString(alias); err != nil { + return fmt.Errorf("write alias: %w", err) + } + + if err := kse.writeUint64(uint64(timeToMilliseconds(pke.CreationTime))); err != nil { + return fmt.Errorf("write creation timestamp: %w", err) + } + + length := uint64(len(pke.encryptedPrivateKey)) + if length > math.MaxUint32 { + return fmt.Errorf("got encrypted content %d bytes long, max length is %d", length, uint64(math.MaxUint32)) + } + + if err := kse.writeUint32(uint32(length)); err != nil { + return fmt.Errorf("filed to write length: %w", err) + } + + if err := kse.writeBytes(pke.encryptedPrivateKey); err != nil { + return fmt.Errorf("write content: %w", err) + } + + certNum := uint64(len(pke.CertificateChain)) + if certNum > math.MaxUint32 { + return fmt.Errorf("got certificate chain %d entries long, max number of entries is %d", + certNum, uint64(math.MaxUint32)) + } + + if err := kse.writeUint32(uint32(certNum)); err != nil { + return fmt.Errorf("write number of certificates: %w", err) + } + + for i, cert := range pke.CertificateChain { + if err := kse.writeCertificate(cert); err != nil { + return fmt.Errorf("write %d certificate: %w", i, err) + } + } + + return nil +} + +func (kse *keyStoreEncoder) writeTrustedCertificateEntry(alias string, tce TrustedCertificateEntry) error { + if err := kse.writeUint32(trustedCertificateTag); err != nil { + return fmt.Errorf("write tag: %w", err) + } + + if err := kse.writeString(alias); err != nil { + return fmt.Errorf("write alias: %w", err) + } + + if err := kse.writeUint64(uint64(timeToMilliseconds(tce.CreationTime))); err != nil { + return fmt.Errorf("write creation timestamp: %w", err) + } + + if err := kse.writeCertificate(tce.Certificate); err != nil { + return fmt.Errorf("write certificate: %w", err) + } + + return nil +} diff --git a/vendor/github.com/pavel-v-chernykh/keystore-go/v4/go.mod b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/go.mod new file mode 100644 index 0000000000..44e59dab45 --- /dev/null +++ b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/go.mod @@ -0,0 +1,3 @@ +module github.com/pavel-v-chernykh/keystore-go/v4 + +go 1.14 diff --git a/vendor/github.com/pavel-v-chernykh/keystore-go/v4/keyprotector.go b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/keyprotector.go new file mode 100644 index 0000000000..bca71d207c --- /dev/null +++ b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/keyprotector.go @@ -0,0 +1,173 @@ +package keystore + +import ( + "bytes" + "crypto/sha1" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "fmt" + "io" +) + +const saltLen = 20 + +var supportedPrivateKeyAlgorithmOid = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 42, 2, 17, 1, 1}) + +type keyInfo struct { + Algo pkix.AlgorithmIdentifier + PrivateKey []byte +} + +func decrypt(data []byte, password []byte) ([]byte, error) { + var keyInfo keyInfo + + asn1Rest, err := asn1.Unmarshal(data, &keyInfo) + if err != nil { + return nil, fmt.Errorf("unmarshal encrypted key: %w", err) + } + + if len(asn1Rest) > 0 { + return nil, errors.New("got extra data in encrypted key") + } + + if !keyInfo.Algo.Algorithm.Equal(supportedPrivateKeyAlgorithmOid) { + return nil, errors.New("got unsupported private key encryption algorithm") + } + + md := sha1.New() + + passwordBytes := passwordBytes(password) + defer zeroing(passwordBytes) + + salt := make([]byte, saltLen) + copy(salt, keyInfo.PrivateKey) + encryptedKeyLen := len(keyInfo.PrivateKey) - saltLen - md.Size() + numRounds := encryptedKeyLen / md.Size() + + if encryptedKeyLen%md.Size() != 0 { + numRounds++ + } + + encryptedKey := make([]byte, encryptedKeyLen) + copy(encryptedKey, keyInfo.PrivateKey[saltLen:]) + + xorKey := make([]byte, encryptedKeyLen) + + digest := salt + + for i, xorOffset := 0, 0; i < numRounds; i++ { + if _, err := md.Write(passwordBytes); err != nil { + return nil, fmt.Errorf("update digest with password on %d round: %w", i, err) + } + + if _, err := md.Write(digest); err != nil { + return nil, fmt.Errorf("update digest with digest from previous round on %d round: %w", i, err) + } + + digest = md.Sum(nil) + md.Reset() + copy(xorKey[xorOffset:], digest) + xorOffset += md.Size() + } + + plainKey := make([]byte, encryptedKeyLen) + for i := 0; i < len(plainKey); i++ { + plainKey[i] = encryptedKey[i] ^ xorKey[i] + } + + if _, err := md.Write(passwordBytes); err != nil { + return nil, fmt.Errorf("update digest with password: %w", err) + } + + if _, err := md.Write(plainKey); err != nil { + return nil, fmt.Errorf("update digest with plain key: %w", err) + } + + digest = md.Sum(nil) + md.Reset() + + digestOffset := saltLen + encryptedKeyLen + if !bytes.Equal(digest, keyInfo.PrivateKey[digestOffset:digestOffset+len(digest)]) { + return nil, errors.New("got invalid digest") + } + + return plainKey, nil +} + +func encrypt(rand io.Reader, plainKey []byte, password []byte) ([]byte, error) { + md := sha1.New() + + passwordBytes := passwordBytes(password) + defer zeroing(passwordBytes) + + plainKeyLen := len(plainKey) + numRounds := plainKeyLen / md.Size() + + if plainKeyLen%md.Size() != 0 { + numRounds++ + } + + salt := make([]byte, saltLen) + if _, err := rand.Read(salt); err != nil { + return nil, fmt.Errorf("read random bytes: %w", err) + } + + xorKey := make([]byte, plainKeyLen) + + digest := salt + + for i, xorOffset := 0, 0; i < numRounds; i++ { + if _, err := md.Write(passwordBytes); err != nil { + return nil, fmt.Errorf("update digest with password on %d round: %w", i, err) + } + + if _, err := md.Write(digest); err != nil { + return nil, fmt.Errorf("update digest with digest from prevous round on %d round: %w", i, err) + } + + digest = md.Sum(nil) + md.Reset() + copy(xorKey[xorOffset:], digest) + xorOffset += md.Size() + } + + tmpKey := make([]byte, plainKeyLen) + for i := 0; i < plainKeyLen; i++ { + tmpKey[i] = plainKey[i] ^ xorKey[i] + } + + encryptedKey := make([]byte, saltLen+plainKeyLen+md.Size()) + encryptedKeyOffset := 0 + copy(encryptedKey[encryptedKeyOffset:], salt) + encryptedKeyOffset += saltLen + copy(encryptedKey[encryptedKeyOffset:], tmpKey) + encryptedKeyOffset += plainKeyLen + + if _, err := md.Write(passwordBytes); err != nil { + return nil, fmt.Errorf("update digest with password: %w", err) + } + + if _, err := md.Write(plainKey); err != nil { + return nil, fmt.Errorf("udpate digest with plain key: %w", err) + } + + digest = md.Sum(nil) + md.Reset() + copy(encryptedKey[encryptedKeyOffset:], digest) + + keyInfo := keyInfo{ + Algo: pkix.AlgorithmIdentifier{ + Algorithm: supportedPrivateKeyAlgorithmOid, + Parameters: asn1.RawValue{Tag: 5}, + }, + PrivateKey: encryptedKey, + } + + encodedKey, err := asn1.Marshal(keyInfo) + if err != nil { + return nil, fmt.Errorf("marshal encrypted key: %w", err) + } + + return encodedKey, nil +} diff --git a/vendor/github.com/pavel-v-chernykh/keystore-go/v4/keystore.go b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/keystore.go new file mode 100644 index 0000000000..12a981444e --- /dev/null +++ b/vendor/github.com/pavel-v-chernykh/keystore-go/v4/keystore.go @@ -0,0 +1,335 @@ +package keystore + +import ( + "bytes" + "crypto/rand" + "crypto/sha1" + "errors" + "fmt" + "io" + "sort" + "strings" + "time" +) + +var ( + ErrEntryNotFound = errors.New("entry not found") + ErrWrongEntryType = errors.New("wrong entry type") + ErrEmptyPrivateKey = errors.New("empty private key") + ErrEmptyCertificateType = errors.New("empty certificate type") + ErrEmptyCertificateContent = errors.New("empty certificate content") + ErrShortPassword = errors.New("short password") +) + +const minPasswordLen = 6 + +// KeyStore is a mapping of alias to pointer to PrivateKeyEntry or TrustedCertificateEntry. +type KeyStore struct { + m map[string]interface{} + + ordered bool + caseExact bool +} + +// PrivateKeyEntry is an entry for private keys and associated certificates. +type PrivateKeyEntry struct { + encryptedPrivateKey []byte + + CreationTime time.Time + PrivateKey []byte + CertificateChain []Certificate +} + +// TrustedCertificateEntry is an entry for certificates only. +type TrustedCertificateEntry struct { + CreationTime time.Time + Certificate Certificate +} + +// Certificate describes type of certificate. +type Certificate struct { + Type string + Content []byte +} + +type Option func(store *KeyStore) + +// WithOrderedAliases sets ordered option to true. Orders aliases alphabetically. +func WithOrderedAliases() Option { return func(ks *KeyStore) { ks.ordered = true } } + +// WithCaseExactAliases sets caseExact option to true. Preserves original case of aliases. +func WithCaseExactAliases() Option { return func(ks *KeyStore) { ks.caseExact = true } } + +// New returns new initialized instance of the KeyStore. +func New(options ...Option) KeyStore { + ks := KeyStore{m: make(map[string]interface{})} + + for _, option := range options { + option(&ks) + } + + return ks +} + +// Store signs keystore using password and writes its representation into w +// It is strongly recommended to fill password slice with zero after usage. +func (ks KeyStore) Store(w io.Writer, password []byte) error { + if len(password) < minPasswordLen { + return fmt.Errorf("password must be at least %d characters: %w", minPasswordLen, ErrShortPassword) + } + + kse := keyStoreEncoder{ + w: w, + md: sha1.New(), + } + + passwordBytes := passwordBytes(password) + defer zeroing(passwordBytes) + + if _, err := kse.md.Write(passwordBytes); err != nil { + return fmt.Errorf("update digest with password: %w", err) + } + + if _, err := kse.md.Write(whitenerMessage); err != nil { + return fmt.Errorf("update digest with whitener message: %w", err) + } + + if err := kse.writeUint32(magic); err != nil { + return fmt.Errorf("write magic: %w", err) + } + // always write latest version + if err := kse.writeUint32(version02); err != nil { + return fmt.Errorf("write version: %w", err) + } + + if err := kse.writeUint32(uint32(len(ks.m))); err != nil { + return fmt.Errorf("write number of entries: %w", err) + } + + for _, alias := range ks.Aliases() { + switch typedEntry := ks.m[alias].(type) { + case PrivateKeyEntry: + if err := kse.writePrivateKeyEntry(alias, typedEntry); err != nil { + return fmt.Errorf("write private key entry: %w", err) + } + case TrustedCertificateEntry: + if err := kse.writeTrustedCertificateEntry(alias, typedEntry); err != nil { + return fmt.Errorf("write trusted certificate entry: %w", err) + } + default: + return errors.New("got invalid entry") + } + } + + if err := kse.writeBytes(kse.md.Sum(nil)); err != nil { + return fmt.Errorf("write digest: %w", err) + } + + return nil +} + +// Load reads keystore representation from r and checks its signature. +// It is strongly recommended to fill password slice with zero after usage. +func (ks KeyStore) Load(r io.Reader, password []byte) error { + ksd := keyStoreDecoder{ + r: r, + md: sha1.New(), + } + + passwordBytes := passwordBytes(password) + defer zeroing(passwordBytes) + + if _, err := ksd.md.Write(passwordBytes); err != nil { + return fmt.Errorf("update digest with password: %w", err) + } + + if _, err := ksd.md.Write(whitenerMessage); err != nil { + return fmt.Errorf("update digest with whitener message: %w", err) + } + + readMagic, err := ksd.readUint32() + if err != nil { + return fmt.Errorf("read magic: %w", err) + } + + if readMagic != magic { + return errors.New("got invalid magic") + } + + version, err := ksd.readUint32() + if err != nil { + return fmt.Errorf("read version: %w", err) + } + + entryNum, err := ksd.readUint32() + if err != nil { + return fmt.Errorf("read number of entries: %w", err) + } + + for i := uint32(0); i < entryNum; i++ { + alias, entry, err := ksd.readEntry(version) + if err != nil { + return fmt.Errorf("read %d entry: %w", i, err) + } + + ks.m[alias] = entry + } + + computedDigest := ksd.md.Sum(nil) + + actualDigest, err := ksd.readBytes(uint32(ksd.md.Size())) + if err != nil { + return fmt.Errorf("read digest: %w", err) + } + + if !bytes.Equal(actualDigest, computedDigest) { + return errors.New("got invalid digest") + } + + return nil +} + +// SetPrivateKeyEntry adds PrivateKeyEntry into keystore by alias encrypted with password. +// It is strongly recommended to fill password slice with zero after usage. +func (ks KeyStore) SetPrivateKeyEntry(alias string, entry PrivateKeyEntry, password []byte) error { + if err := entry.validate(); err != nil { + return fmt.Errorf("validate private key entry: %w", err) + } + + if len(password) < minPasswordLen { + return fmt.Errorf("password must be at least %d characters: %w", minPasswordLen, ErrShortPassword) + } + + epk, err := encrypt(rand.Reader, entry.PrivateKey, password) + if err != nil { + return fmt.Errorf("encrypt private key: %w", err) + } + + entry.encryptedPrivateKey = epk + + ks.m[ks.convertAlias(alias)] = entry + + return nil +} + +// GetPrivateKeyEntry returns PrivateKeyEntry from the keystore by the alias decrypted with the password. +// It is strongly recommended to fill password slice with zero after usage. +func (ks KeyStore) GetPrivateKeyEntry(alias string, password []byte) (PrivateKeyEntry, error) { + e, ok := ks.m[ks.convertAlias(alias)] + if !ok { + return PrivateKeyEntry{}, ErrEntryNotFound + } + + pke, ok := e.(PrivateKeyEntry) + if !ok { + return PrivateKeyEntry{}, ErrWrongEntryType + } + + dpk, err := decrypt(pke.encryptedPrivateKey, password) + if err != nil { + return PrivateKeyEntry{}, fmt.Errorf("decrypte private key: %w", err) + } + + pke.encryptedPrivateKey = nil + pke.PrivateKey = dpk + + return pke, nil +} + +// IsPrivateKeyEntry returns true if the keystore has PrivateKeyEntry by the alias. +func (ks KeyStore) IsPrivateKeyEntry(alias string) bool { + _, ok := ks.m[ks.convertAlias(alias)].(PrivateKeyEntry) + + return ok +} + +// SetTrustedCertificateEntry adds TrustedCertificateEntry into keystore by alias. +func (ks KeyStore) SetTrustedCertificateEntry(alias string, entry TrustedCertificateEntry) error { + if err := entry.validate(); err != nil { + return fmt.Errorf("validate trusted certificate entry: %w", err) + } + + ks.m[ks.convertAlias(alias)] = entry + + return nil +} + +// GetTrustedCertificateEntry returns TrustedCertificateEntry from the keystore by the alias. +func (ks KeyStore) GetTrustedCertificateEntry(alias string) (TrustedCertificateEntry, error) { + e, ok := ks.m[ks.convertAlias(alias)] + if !ok { + return TrustedCertificateEntry{}, ErrEntryNotFound + } + + tce, ok := e.(TrustedCertificateEntry) + if !ok { + return TrustedCertificateEntry{}, ErrWrongEntryType + } + + return tce, nil +} + +// IsTrustedCertificateEntry returns true if the keystore has TrustedCertificateEntry by the alias. +func (ks KeyStore) IsTrustedCertificateEntry(alias string) bool { + _, ok := ks.m[ks.convertAlias(alias)].(TrustedCertificateEntry) + + return ok +} + +// DeleteEntry deletes entry from the keystore. +func (ks KeyStore) DeleteEntry(alias string) { + delete(ks.m, ks.convertAlias(alias)) +} + +// Aliases returns slice of all aliases from the keystore. +// Aliases returns slice of all aliases sorted alphabetically if keystore created using WithOrderedAliases option. +func (ks KeyStore) Aliases() []string { + as := make([]string, 0, len(ks.m)) + for a := range ks.m { + as = append(as, a) + } + + if ks.ordered { + sort.Strings(as) + } + + return as +} + +func (ks KeyStore) convertAlias(alias string) string { + if ks.caseExact { + return alias + } + + return strings.ToLower(alias) +} + +func (e PrivateKeyEntry) validate() error { + if len(e.PrivateKey) == 0 { + return ErrEmptyPrivateKey + } + + for i, c := range e.CertificateChain { + if err := c.validate(); err != nil { + return fmt.Errorf("validate certificate %d in chain: %w", i, err) + } + } + + return nil +} + +func (e TrustedCertificateEntry) validate() error { + return e.Certificate.validate() +} + +func (c Certificate) validate() error { + if len(c.Type) == 0 { + return ErrEmptyCertificateType + } + + if len(c.Content) == 0 { + return ErrEmptyCertificateContent + } + + return 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/scrypt/scrypt.go b/vendor/golang.org/x/crypto/scrypt/scrypt.go new file mode 100644 index 0000000000..2f81fe4148 --- /dev/null +++ b/vendor/golang.org/x/crypto/scrypt/scrypt.go @@ -0,0 +1,213 @@ +// 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 scrypt implements the scrypt key derivation function as defined in +// Colin Percival's paper "Stronger Key Derivation via Sequential Memory-Hard +// Functions" (https://www.tarsnap.com/scrypt/scrypt.pdf). +package scrypt // import "golang.org/x/crypto/scrypt" + +import ( + "crypto/sha256" + "errors" + "math/bits" + + "golang.org/x/crypto/pbkdf2" +) + +const maxInt = int(^uint(0) >> 1) + +// blockCopy copies n numbers from src into dst. +func blockCopy(dst, src []uint32, n int) { + copy(dst, src[:n]) +} + +// blockXOR XORs numbers from dst with n numbers from src. +func blockXOR(dst, src []uint32, n int) { + for i, v := range src[:n] { + dst[i] ^= v + } +} + +// salsaXOR applies Salsa20/8 to the XOR of 16 numbers from tmp and in, +// and puts the result into both tmp and out. +func salsaXOR(tmp *[16]uint32, in, out []uint32) { + w0 := tmp[0] ^ in[0] + w1 := tmp[1] ^ in[1] + w2 := tmp[2] ^ in[2] + w3 := tmp[3] ^ in[3] + w4 := tmp[4] ^ in[4] + w5 := tmp[5] ^ in[5] + w6 := tmp[6] ^ in[6] + w7 := tmp[7] ^ in[7] + w8 := tmp[8] ^ in[8] + w9 := tmp[9] ^ in[9] + w10 := tmp[10] ^ in[10] + w11 := tmp[11] ^ in[11] + w12 := tmp[12] ^ in[12] + w13 := tmp[13] ^ in[13] + w14 := tmp[14] ^ in[14] + w15 := tmp[15] ^ in[15] + + x0, x1, x2, x3, x4, x5, x6, x7, x8 := w0, w1, w2, w3, w4, w5, w6, w7, w8 + x9, x10, x11, x12, x13, x14, x15 := w9, w10, w11, w12, w13, w14, w15 + + for i := 0; i < 8; i += 2 { + x4 ^= bits.RotateLeft32(x0+x12, 7) + x8 ^= bits.RotateLeft32(x4+x0, 9) + x12 ^= bits.RotateLeft32(x8+x4, 13) + x0 ^= bits.RotateLeft32(x12+x8, 18) + + x9 ^= bits.RotateLeft32(x5+x1, 7) + x13 ^= bits.RotateLeft32(x9+x5, 9) + x1 ^= bits.RotateLeft32(x13+x9, 13) + x5 ^= bits.RotateLeft32(x1+x13, 18) + + x14 ^= bits.RotateLeft32(x10+x6, 7) + x2 ^= bits.RotateLeft32(x14+x10, 9) + x6 ^= bits.RotateLeft32(x2+x14, 13) + x10 ^= bits.RotateLeft32(x6+x2, 18) + + x3 ^= bits.RotateLeft32(x15+x11, 7) + x7 ^= bits.RotateLeft32(x3+x15, 9) + x11 ^= bits.RotateLeft32(x7+x3, 13) + x15 ^= bits.RotateLeft32(x11+x7, 18) + + x1 ^= bits.RotateLeft32(x0+x3, 7) + x2 ^= bits.RotateLeft32(x1+x0, 9) + x3 ^= bits.RotateLeft32(x2+x1, 13) + x0 ^= bits.RotateLeft32(x3+x2, 18) + + x6 ^= bits.RotateLeft32(x5+x4, 7) + x7 ^= bits.RotateLeft32(x6+x5, 9) + x4 ^= bits.RotateLeft32(x7+x6, 13) + x5 ^= bits.RotateLeft32(x4+x7, 18) + + x11 ^= bits.RotateLeft32(x10+x9, 7) + x8 ^= bits.RotateLeft32(x11+x10, 9) + x9 ^= bits.RotateLeft32(x8+x11, 13) + x10 ^= bits.RotateLeft32(x9+x8, 18) + + x12 ^= bits.RotateLeft32(x15+x14, 7) + x13 ^= bits.RotateLeft32(x12+x15, 9) + x14 ^= bits.RotateLeft32(x13+x12, 13) + x15 ^= bits.RotateLeft32(x14+x13, 18) + } + x0 += w0 + x1 += w1 + x2 += w2 + x3 += w3 + x4 += w4 + x5 += w5 + x6 += w6 + x7 += w7 + x8 += w8 + x9 += w9 + x10 += w10 + x11 += w11 + x12 += w12 + x13 += w13 + x14 += w14 + x15 += w15 + + out[0], tmp[0] = x0, x0 + out[1], tmp[1] = x1, x1 + out[2], tmp[2] = x2, x2 + out[3], tmp[3] = x3, x3 + out[4], tmp[4] = x4, x4 + out[5], tmp[5] = x5, x5 + out[6], tmp[6] = x6, x6 + out[7], tmp[7] = x7, x7 + out[8], tmp[8] = x8, x8 + out[9], tmp[9] = x9, x9 + out[10], tmp[10] = x10, x10 + out[11], tmp[11] = x11, x11 + out[12], tmp[12] = x12, x12 + out[13], tmp[13] = x13, x13 + out[14], tmp[14] = x14, x14 + out[15], tmp[15] = x15, x15 +} + +func blockMix(tmp *[16]uint32, in, out []uint32, r int) { + blockCopy(tmp[:], in[(2*r-1)*16:], 16) + for i := 0; i < 2*r; i += 2 { + salsaXOR(tmp, in[i*16:], out[i*8:]) + salsaXOR(tmp, in[i*16+16:], out[i*8+r*16:]) + } +} + +func integer(b []uint32, r int) uint64 { + j := (2*r - 1) * 16 + return uint64(b[j]) | uint64(b[j+1])<<32 +} + +func smix(b []byte, r, N int, v, xy []uint32) { + var tmp [16]uint32 + x := xy + y := xy[32*r:] + + j := 0 + for i := 0; i < 32*r; i++ { + x[i] = uint32(b[j]) | uint32(b[j+1])<<8 | uint32(b[j+2])<<16 | uint32(b[j+3])<<24 + j += 4 + } + for i := 0; i < N; i += 2 { + blockCopy(v[i*(32*r):], x, 32*r) + blockMix(&tmp, x, y, r) + + blockCopy(v[(i+1)*(32*r):], y, 32*r) + blockMix(&tmp, y, x, r) + } + for i := 0; i < N; i += 2 { + j := int(integer(x, r) & uint64(N-1)) + blockXOR(x, v[j*(32*r):], 32*r) + blockMix(&tmp, x, y, r) + + j = int(integer(y, r) & uint64(N-1)) + blockXOR(y, v[j*(32*r):], 32*r) + blockMix(&tmp, y, x, r) + } + j = 0 + for _, v := range x[:32*r] { + b[j+0] = byte(v >> 0) + b[j+1] = byte(v >> 8) + b[j+2] = byte(v >> 16) + b[j+3] = byte(v >> 24) + j += 4 + } +} + +// Key derives a key from the password, salt, and cost parameters, returning +// a byte slice of length keyLen that can be used as cryptographic key. +// +// N is a CPU/memory cost parameter, which must be a power of two greater than 1. +// r and p must satisfy r * p < 2³⁰. If the parameters do not satisfy the +// limits, the function returns a nil byte slice and an error. +// +// For example, you can get a derived key for e.g. AES-256 (which needs a +// 32-byte key) by doing: +// +// dk, err := scrypt.Key([]byte("some password"), salt, 32768, 8, 1, 32) +// +// The recommended parameters for interactive logins as of 2017 are N=32768, r=8 +// and p=1. The parameters N, r, and p should be increased as memory latency and +// CPU parallelism increases; consider setting N to the highest power of 2 you +// can derive within 100 milliseconds. Remember to get a good random salt. +func Key(password, salt []byte, N, r, p, keyLen int) ([]byte, error) { + if N <= 1 || N&(N-1) != 0 { + return nil, errors.New("scrypt: N must be > 1 and a power of 2") + } + if uint64(r)*uint64(p) >= 1<<30 || r > maxInt/128/p || r > maxInt/256 || N > maxInt/128/r { + return nil, errors.New("scrypt: parameters are too large") + } + + xy := make([]uint32, 64*r) + v := make([]uint32, 32*N*r) + b := pbkdf2.Key(password, salt, 1, p*128*r, sha256.New) + + for i := 0; i < p; i++ { + smix(b[i*128*r:], r, N, v, xy) + } + + return pbkdf2.Key(password, b, 1, keyLen, sha256.New), nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 90876e6703..f3d0725cf7 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -213,6 +213,9 @@ github.com/operator-framework/operator-sdk/pkg/leader github.com/operator-framework/operator-sdk/pkg/metrics github.com/operator-framework/operator-sdk/pkg/test github.com/operator-framework/operator-sdk/version +# github.com/pavel-v-chernykh/keystore-go/v4 v4.1.0 +## explicit +github.com/pavel-v-chernykh/keystore-go/v4 # github.com/pborman/uuid v1.2.0 github.com/pborman/uuid # github.com/pkg/errors v0.9.1 @@ -250,8 +253,10 @@ github.com/spf13/pflag # go.uber.org/zap v1.16.0 ## explicit # golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 +golang.org/x/crypto/pbkdf2 golang.org/x/crypto/pkcs12 golang.org/x/crypto/pkcs12/internal/rc2 +golang.org/x/crypto/scrypt golang.org/x/crypto/ssh/terminal # golang.org/x/mod v0.2.0 golang.org/x/mod/module