Skip to content

Commit

Permalink
feat: add OpenTelemetry span for password hash comparison (#3383)
Browse files Browse the repository at this point in the history
  • Loading branch information
alnr authored Jul 24, 2023
1 parent 04bca63 commit e3fcf0c
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 16 deletions.
18 changes: 18 additions & 0 deletions hash/hash_comparator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
}
}
Expand Down
16 changes: 10 additions & 6 deletions hash/hasher_argon2.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -44,26 +45,29 @@ 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
}

// 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 {
Expand Down
18 changes: 9 additions & 9 deletions hash/hasher_bcrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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
}

Expand Down
7 changes: 6 additions & 1 deletion hash/hasher_pbkdf2.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand All @@ -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)
Expand Down

0 comments on commit e3fcf0c

Please sign in to comment.