-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add VALIDATE calls #13
base: master
Are you sure you want to change the base?
Conversation
This allows authenticating with a password
Awesome PR @ViViDboarder, thanks a lot! Review will be done ~Monday. |
I actually have no idea what happened here. This was working for me before and then suddenly wasn't and required this change.
For some reason I started getting 8 digit codes where I was supposed to get 6. I didn't change anything so I wonder if it has something to do with drivers or a dependency. To resolve it, I implemented a similar solution to |
Any chance of a review in the next week or so? I wrote a cli tool and an Alfred Workflow that depends on this. I can build and distribute binaries using |
Yeah, sorry about that. I'll do the review today / in the afternoon. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks again for the PR, very nice work! Apart from the individual comments I have two three high-level questions:
- Why the KDF in
select.go
? - Can you contribute changes to
README
as well (you madeVALIDATE
support a reality after all 👍 ) - Can you contribute additional unit tests (please note that current suite will delete existing OATH tokens from your test key)?
Thanks again for the work & sorry for not getting back earlier!
@@ -4,13 +4,13 @@ import "fmt" | |||
|
|||
const ( | |||
// HmacSha1 describes a HMAC with SHA-1 | |||
HmacSha1 Algorithm = 0x01 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure about this - this is already a public constant. What good does the additional constant do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good point. I had done this initially to minimize the changes, but a single constant should be sufficient.
@@ -0,0 +1,49 @@ | |||
package ykoath |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer the constants to be private tbh - they do help readability but trigger linting advice when undocumented and public.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I don't believe the API that this package exposes requires someone to access these either. I'll rename them.
calculate.go
Outdated
codes = append(codes, touchRequired) | ||
|
||
case 0x76: | ||
case TAG_RESPONSE: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you elaborate the scenario in which we need to handle a 0x75
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if there is a legitimate case for this, however it is included as a possible response in the spec. The result (once properly wrapping errors, as suggested) will be the same as not handling it, except with an error message that will be somewhat more helpful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, understood.
func (o *OATH) Validate(s *Select, key []byte) (bool, error) { | ||
algo, err := s.Hash() | ||
if err != nil { | ||
return false, err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here another errors.Wrapf
+ constant would be preferred.
return false, err | ||
} | ||
|
||
mac := hmac.New(algo, key) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm. If you really want to introduce a Hash
function, you could potentially use the Sum
/ Sum256
/ Sum512
functions from standard and return a slices on the array returned by them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I follow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant you could use the "sum" forms such as https://golang.org/pkg/crypto/sha256/#Sum256 - but I guess your way is more generic. Let's ignore this ...
|
||
randChallenge := make([]byte, 8) | ||
_, err = rand.Read(randChallenge) | ||
if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same error remark as above.
challenge := write(TAG_CHALLENGE, challengeSum) | ||
_, err = o.send(0x00, INST_VALIDATE, 0x00, 0x00, response, challenge) | ||
if err != nil { | ||
return false, err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same error remark as above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the feedback. Looks like a lot of cleanup on the constants and errors. I'll also update documentation to reflect the usage of the new functions within select.go
.
Edit: Ugh. I guess that I didn't do responses properly. They show as replies above, but are duplicated here.
@@ -4,13 +4,13 @@ import "fmt" | |||
|
|||
const ( | |||
// HmacSha1 describes a HMAC with SHA-1 | |||
HmacSha1 Algorithm = 0x01 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good point. I had done this initially to minimize the changes, but a single constant should be sufficient.
calculate.go
Outdated
codes = append(codes, touchRequired) | ||
|
||
case 0x76: | ||
case TAG_RESPONSE: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if there is a legitimate case for this, however it is included as a possible response in the spec. The result (once properly wrapping errors, as suggested) will be the same as not handling it, except with an error message that will be somewhat more helpful.
code := binary.BigEndian.Uint32(value[1:]) | ||
// Limit code to a maximum number of digits | ||
code = code % uint32(math.Pow(10.0, float64(digits))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had some TOTP that were supposed to be 6 digits (this was the value of digits
), and they were returned as 8 digits. This will trim them to length of whatever the the value of digits
is. This logic is the same as in yubikey-manager
code.go
Outdated
@@ -13,12 +13,15 @@ func (c code) Error() string { | |||
|
|||
if bytes.Equal(c, []byte{0x6a, 0x80}) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only included the constants listed at the top of https://developers.yubico.com/OATH/YKOATH_Protocol.html
Although this 0x6a80
is not listed as a constant there, it appears to be consistent throughout the documentation. I'll take a look through and include those and make a note along with the documentation link.
code.go
Outdated
@@ -13,12 +13,15 @@ func (c code) Error() string { | |||
|
|||
if bytes.Equal(c, []byte{0x6a, 0x80}) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On a similar note, rather than returning a string, I could make this return an error
for each of these. What do you think?
@@ -0,0 +1,49 @@ | |||
package ykoath |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I don't believe the API that this package exposes requires someone to access these either. I'll rename them.
return false, err | ||
} | ||
|
||
mac := hmac.New(algo, key) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I follow.
Rename constants add documentation
Hey @ViViDboarder - are the remaining issues clear? Should we proceed with this PR? |
Thanks @yawn. I'm still a little confused on errors, which I think are the only outstanding items. The way I understand error handling in Go right now (it seems to change frequently) is that a class should do something like this: package example
import (
"errors"
"fmt"
)
var (
ErrSomethingNotFound = errors.New("thing not found")
)
func CalledError() error {
err := maybeHasError()
if err != nil {
return fmt.Errorf("error in this thing: %w", err)
}
return nil
}
func FindThing(p string) error {
var b bool
b = isThingFound(p)
if !b {
return fmt.Errorf("error finding %p: %w", p, ErrSomethingNotFound)
}
return nil
} This allows some using the module and function to handle errors from package main
import (
"errors"
"fmt"
"example"
)
func main() {
err = example.FindThing("foo")
if errors.Is(err, example.ErrSomethingNotFound) {
fmt.Println("Thing wasn't found")
} else {
panic(err)
}
} I can definitely do this, but it would be inconsistent with some of the others. I can refactor those as well, but maybe in a different patch. |
As an alternative: just add the tests and I'll do the minor / cosmetic changes in your PR? Maybe easier? |
That works for me. Regarding the tests, I can definitely write some, but I do not have a spare Yubikey to test with. It also looks like I'll need to implement the |
I can totally live without |
3c0dfa3
to
201009e
Compare
My Yubikey Neo is password protected and requires a call to validate the password before listing or performing other actions.
In order to make the magic numbers more readable I added all the ones I found on the Yubico site to
constants.go
.Other smaller changes:
CALCULATE
asCalculateOne
andCALCULATE_ALL
asCalculateAll
codes.go
This is used as follows: