go-libp2p-core
go-libp2p-core copied to clipboard
Provide interfaces related to the abstract cryptographic operation being done
The current PrivKey
is a Key
that can also sign stuff, so it actually is a SigKey
. You could also let MACs implement this interface.
- Signatures (
SigKey
andVerifyKey
) - Key Exchange (
KXPrivKey.Exchange(KXPubKey
) - Symmetric Secrets implementing
SignKey
andVerifyKey
as well asEncryptKey
andDecryptKey
andAuthEncKey
andAuthDecKey
(as ingolang.org/x/crypto/nacl/secretbox
). - Asymmetric Authenticated Encryption, providing defaults by combining Key Exchange + symmetric AE (or just use
golang.org/x/crypto/nacl/box
).
I'll keep on thinking about this and drop some notes over the day.
Obvious:
type SigKey interface {
Key
Sign(data []byte) (sig []byte)
}
type VerifyKey interface {
Key
Verify(data, sig []byte) bool
}
type KXPrivKey interface {
Key
Exchange(KXPubKey) Key
}
type KXPubKey interface {
Key
Exchange(KXPrivKey) Key
}
We can now use the resulting Key
as input for e.g. SecretboxKey
and use it to create secretboxes:
func AsSecretboxKey(k Key) SecretboxKey
type SecretboxKey Key
func (sbk SecretboxKey) Seal(data, nonce []byte) (ciphertext []byte) {...}
func (sbk SecretboxKey) Open(ciphertext, nonce []byte) (data []byte) {...}
Which brings us to the interface for Authenticated Encryption:
type AuthEncSymKey interface {
Key
Seal(data, nonce []byte) []byte
Open(ciphertext, nonce []byte) []byte
}
And the asymmetric version would be
type AuthEncPrivKey interface {
Key
Seal(k AuthEncPubKey, data, nonce []byte) (ciphertext []byte)
Open(k AuthEncPubKey, ciphertext, nonce []byte) (data []byte)
}
Doing AE with RSA is pretty common nowadays too, so I think this is the right interface. We can should provide EncryptionKey
and DecryptionKey
interfaces
type EncryptionKey interface {
Key
Encrypt([]byte) []byte
}
type DecryptionKey interface {
Key
Decrypt([]byte) []byte
}
Then we could provide some default types for NaCl boxes, RSA and Ed25519.
I'm pretty much free right now so I can do it if you like the idea.
Warning: doing encryption and signing using same RSA key leaks information and is potentially unsafe.
My primary concern is not the concrete implementations but the interfaces. I don't plan to make changes to the Rsa*
types, except maybe changing some type such that it fits the new interfaces.
Looking at the code, currently we seem to allow using the same keys for encryption and signing. We might want to fix that too. Filed libp2p/go-libp2p-crypto#9.
Looking at the interfaces I proposed again, this all feels weird. E.g. the key exchange is not performed by the keys, but it's two communicating entities using the Keys to negotiate a shared secret. The key doesn't sign the data, it's the algorithm using the key. I'll think about interfaces that reflect that.
How about
type Key interface {
Type() KeyType // e.g. ed25519-pub, rsa-priv, secret
Purpose() Purpose // eg. Signing, Encryption, Decryption, KeyDerivation, ...
Bytes() []byte
String() string // for good measure
}
// example
func Sign(k Key, data []byte) ([]byte, error) {
var sig []byte
if !k.Purpose() == Signing {
return WrongPurposeError
}
switch k.Type() {
case ed25519-priv:
...
sig = ...
case ...
}
return sig
}
This way it behaves a bit more like a net.Addr
. What do you think about this?