Skip to content
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

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open

Conversation

Gilthoniel
Copy link
Contributor

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

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
@Gilthoniel Gilthoniel self-assigned this Oct 28, 2019
@Gilthoniel
Copy link
Contributor Author

First reduced PR for #587

@dedis dedis deleted a comment Oct 28, 2019
ciphersuite/ciphersuite.go Outdated Show resolved Hide resolved
ciphersuite/ciphersuite.go Show resolved Hide resolved
}

// NewRawPublicKey returns an instance of a public key.
func NewRawPublicKey(name Name, data []byte) *RawPublicKey {
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Contributor

Choose a reason for hiding this comment

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

ciphersuite/ciphersuite.go Show resolved Hide resolved
ciphersuite/ciphersuite.go Show resolved Hide resolved
ciphersuite/ciphersuite.go Show resolved Hide resolved
ciphersuite/ed25519.go Show resolved Hide resolved
ciphersuite/ed25519.go Outdated Show resolved Hide resolved
ciphersuite/ed25519.go Show resolved Hide resolved
ciphersuite/registry.go Show resolved Hide resolved
Copy link
Contributor

@nkcr nkcr left a 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
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// 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
Copy link
Contributor

Choose a reason for hiding this comment

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

ok, but why 32?

Copy link
Contributor Author

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.

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'll see tomorrow morning

Copy link
Contributor

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)
Copy link
Contributor

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. :/

Copy link
Contributor

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

Copy link
Contributor Author

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.

Copy link
Contributor

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"
Copy link
Contributor

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

Copy link
Contributor Author

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.

Copy link
Contributor

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 {
Copy link
Contributor

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)

Copy link
Contributor Author

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

Copy link
Member

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")
Copy link
Contributor

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)
Copy link
Contributor

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 {
Copy link
Contributor

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

@Gilthoniel
Copy link
Contributor Author

On hold during the TLS related investigation.

}
}

// RegisterCipherSuite stores the cipher if it does not exist. It returns the
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// 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.
Copy link
Collaborator

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?

@nkcr
Copy link
Contributor

nkcr commented Oct 27, 2020

This PR is celebrating it's first year tomorrow 🎂 😰
Should we consider merging it ? It would require a bit of effort to address some of the comments, anyone's up to do that ?

@ineiti
Copy link
Member

ineiti commented Oct 27, 2020

I propose to close this issue without merging for the following reasons:

  • I think it solves the wrong problem - see my comment in the related issue.
  • there already is a way to have independent keypairs for each service. So if anything, this interfaces should be extended, and not a new added.
  • it was originally thought to help implement Drynx / Spindle from the LDS, but finally they used their own keypair implementation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants