From e3fcf0c31db9742ed61bcf783e37ee119ed19d42 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Mon, 24 Jul 2023 09:51:43 +0200 Subject: [PATCH] feat: add OpenTelemetry span for password hash comparison (#3383) --- hash/hash_comparator.go | 18 ++++++++++++++++++ hash/hasher_argon2.go | 16 ++++++++++------ hash/hasher_bcrypt.go | 18 +++++++++--------- hash/hasher_pbkdf2.go | 7 ++++++- 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/hash/hash_comparator.go b/hash/hash_comparator.go index 0a0bcf9b9383..0200e08cad22 100644 --- a/hash/hash_comparator.go +++ b/hash/hash_comparator.go @@ -19,6 +19,8 @@ import ( "strings" "github.com/pkg/errors" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "golang.org/x/crypto/argon2" "golang.org/x/crypto/bcrypt" "golang.org/x/crypto/pbkdf2" @@ -52,32 +54,48 @@ func NewCryptDecoder() *crypt.Decoder { var CryptDecoder = NewCryptDecoder() func Compare(ctx context.Context, password []byte, hash []byte) error { + ctx, span := otel.GetTracerProvider().Tracer(tracingComponent).Start(ctx, "hash.Compare") + defer span.End() + switch { case IsMD5CryptHash(hash): + span.SetAttributes(attribute.String("hash.type", "md5crypt")) return CompareMD5Crypt(ctx, password, hash) case IsBcryptHash(hash): + span.SetAttributes(attribute.String("hash.type", "bcrypt")) return CompareBcrypt(ctx, password, hash) case IsSHA256CryptHash(hash): + span.SetAttributes(attribute.String("hash.type", "sha256")) return CompareSHA256Crypt(ctx, password, hash) case IsSHA512CryptHash(hash): + span.SetAttributes(attribute.String("hash.type", "sha512")) return CompareSHA512Crypt(ctx, password, hash) case IsArgon2idHash(hash): + span.SetAttributes(attribute.String("hash.type", "argon2id")) return CompareArgon2id(ctx, password, hash) case IsArgon2iHash(hash): + span.SetAttributes(attribute.String("hash.type", "argon2i")) return CompareArgon2i(ctx, password, hash) case IsPbkdf2Hash(hash): + span.SetAttributes(attribute.String("hash.type", "pbkdf2")) return ComparePbkdf2(ctx, password, hash) case IsScryptHash(hash): + span.SetAttributes(attribute.String("hash.type", "scrypt")) return CompareScrypt(ctx, password, hash) case IsSSHAHash(hash): + span.SetAttributes(attribute.String("hash.type", "ssha")) return CompareSSHA(ctx, password, hash) case IsSHAHash(hash): + span.SetAttributes(attribute.String("hash.type", "sha")) return CompareSHA(ctx, password, hash) case IsFirebaseScryptHash(hash): + span.SetAttributes(attribute.String("hash.type", "firebasescrypt")) return CompareFirebaseScrypt(ctx, password, hash) case IsMD5Hash(hash): + span.SetAttributes(attribute.String("hash.type", "md5")) return CompareMD5(ctx, password, hash) default: + span.SetAttributes(attribute.String("hash.type", "unknown")) return errors.WithStack(ErrUnknownHashAlgorithm) } } diff --git a/hash/hasher_argon2.go b/hash/hasher_argon2.go index c784f3fab6ac..6775641bb36e 100644 --- a/hash/hasher_argon2.go +++ b/hash/hasher_argon2.go @@ -14,6 +14,7 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" "github.com/pkg/errors" "golang.org/x/crypto/argon2" @@ -44,12 +45,15 @@ func toKB(mem bytesize.ByteSize) uint32 { } func (h *Argon2) Generate(ctx context.Context, password []byte) ([]byte, error) { - ctx, span := otel.GetTracerProvider().Tracer(tracingComponent).Start(ctx, "hash.Argon2.Generate") + conf := h.c.Config().HasherArgon2(ctx) + + _, span := otel.GetTracerProvider().Tracer(tracingComponent).Start(ctx, "hash.Generate", trace.WithAttributes( + attribute.String("hash.type", "argon2id"), + attribute.String("hash.config", fmt.Sprintf("%#v", conf)), + )) defer span.End() - p := h.c.Config().HasherArgon2(ctx) - span.SetAttributes(attribute.String("argon2.config", fmt.Sprintf("#%v", p))) - salt := make([]byte, p.SaltLength) + salt := make([]byte, conf.SaltLength) if _, err := rand.Read(salt); err != nil { return nil, err } @@ -57,13 +61,13 @@ func (h *Argon2) Generate(ctx context.Context, password []byte) ([]byte, error) // Pass the plaintext password, salt and parameters to the argon2.IDKey // function. This will generate a hash of the password using the Argon2id // variant. - hash := argon2.IDKey(password, salt, p.Iterations, toKB(p.Memory), p.Parallelism, p.KeyLength) + hash := argon2.IDKey(password, salt, conf.Iterations, toKB(conf.Memory), conf.Parallelism, conf.KeyLength) var b bytes.Buffer if _, err := fmt.Fprintf( &b, "$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s", - argon2.Version, toKB(p.Memory), p.Iterations, p.Parallelism, + argon2.Version, toKB(conf.Memory), conf.Iterations, conf.Parallelism, base64.RawStdEncoding.EncodeToString(salt), base64.RawStdEncoding.EncodeToString(hash), ); err != nil { diff --git a/hash/hasher_bcrypt.go b/hash/hasher_bcrypt.go index 644c0ad252de..330936a44343 100644 --- a/hash/hasher_bcrypt.go +++ b/hash/hasher_bcrypt.go @@ -5,10 +5,11 @@ package hash import ( "context" + "fmt" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" "github.com/ory/kratos/schema" @@ -30,21 +31,20 @@ func NewHasherBcrypt(c BcryptConfiguration) *Bcrypt { } func (h *Bcrypt) Generate(ctx context.Context, password []byte) ([]byte, error) { - ctx, span := otel.GetTracerProvider().Tracer(tracingComponent).Start(ctx, "hash.Bcrypt.Generate") + conf := h.c.Config().HasherBcrypt(ctx) + + _, span := otel.GetTracerProvider().Tracer(tracingComponent).Start(ctx, "hash.Generate", trace.WithAttributes( + attribute.String("hash.type", "bcrypt"), + attribute.String("hash.config", fmt.Sprintf("%#v", conf)), + )) defer span.End() if err := validateBcryptPasswordLength(password); err != nil { - span.RecordError(err) - span.SetStatus(codes.Error, err.Error()) return nil, err } - cost := int(h.c.Config().HasherBcrypt(ctx).Cost) - span.SetAttributes(attribute.Int("bcrypt.cost", cost)) - hash, err := bcrypt.GenerateFromPassword(password, cost) + hash, err := bcrypt.GenerateFromPassword(password, int(conf.Cost)) if err != nil { - span.RecordError(err) - span.SetStatus(codes.Error, err.Error()) return nil, err } diff --git a/hash/hasher_pbkdf2.go b/hash/hasher_pbkdf2.go index 38c26bba6e06..7550661d9f0a 100644 --- a/hash/hasher_pbkdf2.go +++ b/hash/hasher_pbkdf2.go @@ -16,7 +16,9 @@ import ( "github.com/pkg/errors" "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" "golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/sha3" ) @@ -29,7 +31,10 @@ type Pbkdf2 struct { } func (h *Pbkdf2) Generate(ctx context.Context, password []byte) ([]byte, error) { - _, span := otel.GetTracerProvider().Tracer("").Start(ctx, "hash.Pbkdf2.Generate") + _, span := otel.GetTracerProvider().Tracer(tracingComponent).Start(ctx, "hash.Generate", trace.WithAttributes( + attribute.String("hash.type", "pbkdf2"), + attribute.String("hash.config", fmt.Sprintf("%#v", h)), + )) defer span.End() salt := make([]byte, h.SaltLength)