-
Notifications
You must be signed in to change notification settings - Fork 146
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
Generic signature API #143
Conversation
Another reason is that I'm quickly prototyping integration into Go stdlib to find possible issues with the API. I found one already: we can't use |
42fc6ae
to
1e2cb7f
Compare
29ed92c
to
0ca32e0
Compare
I'll check this tomorrow ;) |
I think it is looking good. Maybe we should be more clear on some names and all, though |
sign/api.go
Outdated
// XXX add a (compile time?) lookup table | ||
name = strings.ToLower(name) | ||
for _, scheme := range schemes { | ||
if strings.ToLower(scheme.Name()) == name { |
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.
Maybe we don't need to repeat here the 'ToLower'.. but maybe it will be a safe security measure...
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.
If we would remove this ToLower()
then we will never match any scheme that has an upper case character in the return of its scheme.Name()
.
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.
Yes, but perhaps the scheme.Name()
should always be enforced to be lower case... what do you think?
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.
sign/eddilithium3.go
Outdated
func (s *edDilithium3Scheme) Sign(sk PrivateKey, message []byte, | ||
opts *SignatureOpts) []byte { | ||
sig := make([]byte, eddilithium3.SignatureSize) | ||
if opts != nil && opts.Context != "" { |
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.
What if only opts.Context != ""
?
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 don't understand. For opts.Context
to make sense, we must have opts != 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.
if any of the options is not used, then is ignored.
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 don't understand — could you rephrase perhaps?
Sure, which? |
I just added a generic interface for four signatures, there are some methods marked with TODO. |
Let me thinking of a nice naming convention and propose something over here tomorrow. |
I don't see a way to solve this without making something else ugly.
I've changed that behaviour: provided a context where none is supported causes an error. |
I've also removed the |
@armfazh @claucece I've addressed all comments. Before starting with final polish (i.e. better documentation & examples), I think we still need to discuss some API issues:
|
Agree on
I don't have a strong opinion on this. Feel free to bring back that code.
That variant would be another named scheme
I added that the Hash field to make the structure compatible with the crypto.SignerOpts. So, we can receive a crypto.SignerOpts, and internally convert it to a different type.
I also didn't like it at all. But, that's the way I found for avoiding circle dependencies. Renaming sign/api to sign/schemes can alleviate a bit its usage. |
"github.com/cloudflare/circl/sign" | ||
) | ||
|
||
var Scheme sign.Scheme = &scheme{} |
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 don't understand why to take the address of a zero-sized value. You can implement the interface with scheme
and not with *scheme
.
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.
Under the hood an instance of an interface is always a pair of a pointer and a concrete type, so instantiating it with a pointer is closer to what really happens. The assembly generated btw is exactly the same.
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.
The difference is on the assembly calls.
call AX
vs call "".(*Impl2).A(SB)
one call is with a register and the other is with a pointer.
I am not convinced why to use pointer when they are not needed.
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.
Oops, I simplified my example too much.
Creating an instance of an interface with either a empty struct or a pointer to an empty struct results in the pointer runtime.zerobase
. (The code of alloc1
and alloc2
is the same. This time I checked properly.)
The generic way to run a function on an interface involves a virtual call (call ax
). So interestingly the Go compiler optimised the call with the pointer receiver, but not the call with the value receiver. I don't see a reason why it couldn't optimise the one with the value receiver as well and perhaps it will in the future.
So using pointers or not does not actually change anything when using empty structs (and the calling code doesn't know the types.) And if it does it optimizes the pointer case better. That's not the reason I use pointers though — as I said I prefer putting them here as it's closer to what Go actually does under the hood: an instance of an interface is always a pointer.
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.
So interestingly the Go compiler optimised the call with the pointer receiver, but not the call with the value receiver.
I want to learn more about this.
So using pointers or not does not actually change anything when using empty structs.
Agree.
Done. Updated this branch, Go fork and cfssl fork.
The type of |
I had the same intuition, but here is the trick. Even if we announce to receive a |
Yes!
Mmm... It will be better case insensitive.
There are two: the ctx and the ph. The schemes should be there on the ed25519 API. |
Exactly! Then, the "Golang-way" API does not change ;) |
@bwesterb check what I did with the ed448 and ed25519 API, specially the usage of this: https://github.com/cloudflare/circl/blob/master/sign/ed25519/ed25519.go#L69 |
Yes, that's for a method on the private key, but here we're dealing with a top level function. (For the |
Mmm.. ok.
Sure! |
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.
It seems ready to go.
Co-authored-by: Armando Faz <[email protected]>
Thank you, I merged it. I created an issue ( #147 ) to track desired improvements to this API. |
Used to easily add support for new (PQ) signature schemes to our fork of Go and cfssl.