-
Notifications
You must be signed in to change notification settings - Fork 29
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
Cipher suite interface to replace Kyber #592
base: master
Are you sure you want to change the base?
Conversation
This adds a cipher suite interface that will be used instead of Kyber abstractions to allow any kind of asymmetric cryptography. It also implements the interface by using the Ed25519 scheme provided by the language. Related to #579
First reduced PR for #587 |
} | ||
|
||
// NewRawPublicKey returns an instance of a public key. | ||
func NewRawPublicKey(name Name, data []byte) *RawPublicKey { |
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.
As every call on RawPublicKey
do not modify it, you can use a value receiver.
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'll see with the other engineers but some guidelines tell you to use the pointer receiver everywhere when some need for consistency.
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 was my guideline - but I have been proven wrong ;) We were trying to get away from this for v3
, but then couldn't. So if you still think it's wrong to always use pointer structs in New
methods, please go ahead and open an issue for v4
to actually return the more appropriate structures.
One place where it will collide with a lot of code is the 'old' protobuf-structures, where the pointer indicates an optional value.
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.
81867ec
to
7b7d7d3
Compare
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 like this ciphersuite package, thanks for the nice job! :)
Nice unit tests too 👍
// registered using the name of the suite. | ||
// | ||
// Public keys and signatures may need to be transmitted over the network and | ||
// interfaces cannot be used as is. That is why every the different elements |
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.
// interfaces cannot be used as is. That is why every the different elements | |
// interfaces cannot be used as is. That is why every different elements |
|
||
// encodedNameLengthSize defines the size in bytes of the name length | ||
// when marshaling cipher data. | ||
const encodedNameLengthSize = 32 / 8 |
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, but why 32?
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.
Something I wanted to discuss. 8 or 16 bits integer is probably big enough for a cipher 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.
We'll see tomorrow morning
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.
Or you can use binary.Uvarint
, to pack tighter, reduce code complexitiy and have unlimited size.
size, err = w.Write([]byte(d.Name())) | ||
n += int64(size) | ||
if err != nil { | ||
return n, xerrors.Errorf("writing name: %v", 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.
Not fan of those shortened error messages... Makes our program stammers the error message instead of having a comprehensive-nicely-readable description. :/
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's only an opinion, nothing has to be changed
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.
Well the fact that we are wrapping the errors create a chain that should explain what happened. Like here a calling function would have something like marshaling cipher data: writing name: ...
so you know what went wrong.
But I'm curious to know what you would have written.
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 look where the error comes from and explain it:
"failed to write Name as bytes: %v", err
"golang.org/x/xerrors" | ||
) | ||
|
||
const errNotEd25519CipherSuite = "invalid cipher suite" |
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.
Shouldn't those be public? like this an external package can assert for this type of error
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.
No it was only to avoid repetitive string values but for the moment I don't think it's necessary. We can always change that later.
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.
btw, if you want to have it public, have it as an error
, so that errors.Is
can be used directly.
} | ||
|
||
func (s *Ed25519CipherSuite) unpackSecretKey(sk SecretKey) (*Ed25519SecretKey, error) { | ||
if data, ok := sk.(*RawSecretKey); ok { |
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.
a personal opinion that I wanted to express for a moment: I don't like those if with short statement. IMO each line of code should do one thing, otherwise that makes the code harder to read. (same for the named return)
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 think this kind of ok conversion is quite frequent in Go
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 to get used to them, too... But I saw even worse (don't remember where):
if ok := some_method; !ok {
}
return signature, nil | ||
} | ||
|
||
return nil, xerrors.New("wrong type of signature") |
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 breaks our philosophy of "fail first" that we tend to use
return nil, xerrors.Errorf("cipher suite: %v", err) | ||
} | ||
|
||
pk, err := c.PublicKey(raw) |
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't we pass only raw.Data
? At this stage we are sure to have the correct cipher suite, so the check raw.Name() != s.Name()
inside the PublicKey()
function looks superfluous. Or I am missing a case?
// Verify takes a public key, a signature and a message and performs a verification | ||
// that will return an error if the signature does not match the message. It | ||
// will also return an error if the cipher suite is unknown. | ||
func (cr *Registry) Verify(pk PublicKey, sig Signature, msg []byte) error { |
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.
just a thought, shouldn't we differentiate the case where the signature is incorrect from the one where an error occurred in the program? It feels a bit strange that the verification outputs an error while it only says that a signature is invalid.
Maybe it's better to rename the function IsValid(...)
and return a (bool, error)
. IDK
On hold during the TLS related investigation. |
} | ||
} | ||
|
||
// RegisterCipherSuite stores the cipher if it does not exist. It returns the |
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.
// RegisterCipherSuite stores the cipher if it does not exist. It returns the | |
// RegisterCipherSuite stores the cipher if it does not exist. It returns |
func (cr *Registry) RegisterCipherSuite(suite CipherSuite) CipherSuite { | ||
name := suite.Name() | ||
if suite := cr.ciphers[name]; suite != nil { | ||
// Cipher suite already registered so we return it so it can be reused. |
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.
Isn't it surprising for the caller to receive a suite different that the provided one, with no error?
This PR is celebrating it's first year tomorrow 🎂 😰 |
I propose to close this issue without merging for the following reasons:
|
This adds a cipher suite interface that will be used instead of
Kyber abstractions to allow any kind of asymmetric cryptography.
It also implements the interface by using the Ed25519 scheme
provided by the language.
Related to #579