Skip to content

Comments

Create Hasher Registry#694

Merged
gdbelvin merged 7 commits intogoogle:masterfrom
gdbelvin:register2
Jun 26, 2017
Merged

Create Hasher Registry#694
gdbelvin merged 7 commits intogoogle:masterfrom
gdbelvin:register2

Conversation

@gdbelvin
Copy link
Contributor

Implements a hasher registry in the style of go's crypto package

Replaces tree.Hasher with hashers.NewLogHasher

Moves tree_hasher.go to merkle/hashers to break some import cycles.

Copy link
Contributor

@codingllama codingllama left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Travis looks unhappy, PTAL.

}

var (
maxHashers = trillian.HashStrategy(len(trillian.HashStrategy_value))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a dangerous assumption here that len(trillian.HashStrategy_value) is greater than the highest proto tag used. This works for now, but will break if we retire a value from the enum or if we simply skip tag numbers.

Copy link
Contributor Author

@gdbelvin gdbelvin Jun 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I'll replace this with a function that iterates through all the possible enums to find the max.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest a map[HashStrategy]XHasher instead. No need to worry about proto tags that way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. Done.

// RegisterLogHasher registers a hasher for use.
func RegisterLogHasher(h trillian.HashStrategy, f LogHasher) {
if h < 0 || h >= maxHashers {
panic(fmt.Sprintf("RegisterLogHasher(%v) of unknown hasher", h))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: If it's on the enum, then the value has to be known. On a related note, we probably shouldn't let people register hashers for UNKNOWN_HASH_STRATEGY.

I would probably prefer an error return to a panic as well.

Ditto for others.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. should I remove the check?

I'm following the style of golang's crypto package here. Since this function should only be called in an init method, I assumed that a panic was more ok (ish). Ie. it should only be triggered right on launch.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should check for:

  • hash == UNKNOWN_HASH
  • hasher == nil (possibly?)
  • hash previously registered (ie, warn about overrides)

Re panic or non-panic, would all callers do:

if err := RegisterLogHash(h, hasher); err != nil {
  panic(err)
}

... or something to that effect? If yes, then I guess the panic inside Register() is appropriate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • We're checking for UNKNOWN_HASH.
  • hasher == nil is ok, and will be caught when requested.
  • I'll add a check for overrides.

}

// NewLogHasher returns a LogHasher
func NewLogHasher(h trillian.HashStrategy) (LogHasher, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Technically, it's not a new hasher. GetLogHasher, maybe?

Ditto for NewMapHasher.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Get* isn't very Go style friendly... How's LogHasher()?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out LogHasher conflicts with the interface name.
Even though this isn't technically create new objects (because there's no need to), it seems like it fits in the New* category because it's like a factory method. What do you think?

)

// RegisterLogHasher registers a hasher for use.
func RegisterLogHasher(h trillian.HashStrategy, f LogHasher) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This solution assumes a hasher is a function of HashStrategy, but in fact it's a mix of HashAlgorithm and HashStrategy. We had this right on the (now deleted) trees functions.

I don't mind about the interface changes, but I'd strongly prefer if you kept trees.Hash and trees.XHasher as they were. I don't think we need a registry for hashers either; we've only got a few (hand-coded) of them anyway.

Copy link
Contributor Author

@gdbelvin gdbelvin Jun 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to ask you about this. I saw hash algorithm being incorporated into the call to rfc6962.New.

HashAlgorithm is used to determine the signature algorithm -- that's why it's a subfield of DigitallySigned. Could we consider not overloading the HashAlgorith field? In my experience, its better to include algorithms into the suite identifier, eg. RFC69626_SHA256. Would you be open to that change? Happy to create a separate PR for it. This would greatly simplify several software level checks we do to determine compatibility between various fields since we'd only put permitted values in the enum.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed offline, we'll go ahead with #698.

@gdbelvin
Copy link
Contributor Author

Let's chat about this tomorrow. @Martin2112 Would love your thoughts as well.

@Martin2112
Copy link
Contributor

Still doesn't build.

@codingllama
Copy link
Contributor

I'm not totally sold on the need for a hasher registry, specially considering that we're unlikely to have many variations. I'd probably stick to a simpler approach, eg, a switch-case hardcoded for each type.

I won't hold you back if you prefer this, but could you explain why a simpler approach won't work?

gdbelvin added 6 commits June 23, 2017 17:47
Implements a hasher registry in the style of go's crypto package

Moves tree_hasher.go to merkle/hashers to break some import cycles.
Copy link
Contributor

@codingllama codingllama left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No major comments, only a few nits and typos.

// RegisterLogHasher registers a hasher for use.
func RegisterLogHasher(h trillian.HashStrategy, f LogHasher) {
if h == trillian.HashStrategy_UNKNOWN_HASH_STRATEGY {
panic(fmt.Sprintf("RegisterLogHasher(%v) of unknown hasher", h))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Either use %s or h.String(), so the message gets the enum name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

panic(fmt.Sprintf("RegisterLogHasher(%v) of unknown hasher", h))
}
if logHashers[h] != nil {
panic(fmt.Sprintf("%v already registred as a LogHasher", h))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.

Same for others.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

panic(fmt.Sprintf("RegisterLogHasher(%v) of unknown hasher", h))
}
if logHashers[h] != nil {
panic(fmt.Sprintf("%v already registred as a LogHasher", h))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo on "registered".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

panic(fmt.Sprintf("RegisterMapHasher(%v) of unknown hasher", h))
}
if mapHashers[h] != nil {
panic(fmt.Sprintf("%v already registred as a MapHasher", h))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo on "registered".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

if f != nil {
return f, nil
}
return nil, fmt.Errorf("LogHasher(%v) is unknown hasher", h)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "is unknown hasher" -> "is an unknown hasher".

Same below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep. done

// NewMapHasher returns a new ObjectHasher based on the passed in MapHasher
func NewMapHasher(baseHasher merkle.MapHasher) merkle.MapHasher {
// GetMapHasher returns a new ObjectHasher based on the passed in MapHasher
func GetMapHasher(baseHasher hashers.MapHasher) hashers.MapHasher {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think this one was better named as NewMapHasher.

Same below (NewLogHasher).

ktestonly "github.com/google/trillian/crypto/keys/testonly"
"github.com/google/trillian/crypto/keyspb"
spb "github.com/google/trillian/crypto/sigpb"
_ "github.com/google/trillian/merkle/maphasher" // TESET_MAP_HASHER
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Typo on TEST_MAP_HASHER

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@gdbelvin
Copy link
Contributor Author

Updated. PTAL.

@gdbelvin gdbelvin merged commit 8676ec0 into google:master Jun 26, 2017
@gdbelvin gdbelvin deleted the register2 branch June 26, 2017 08:58
@gdbelvin gdbelvin mentioned this pull request Jun 26, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants