Narrow pkg/cbk to keypair derivation
Remove KeyFromHex, Seal, and Open with their errors and constants. The library only derives crypto_box keypairs from BIP39 mnemonics; callers use golang.org/x/crypto/nacl/box directly for everything else.
This commit is contained in:
+4
-102
@@ -1,8 +1,6 @@
|
||||
// Package cbk derives NaCl crypto_box keypairs (Curve25519, XSalsa20,
|
||||
// Poly1305) deterministically from BIP39 mnemonic phrases, and seals and
|
||||
// opens messages with them.
|
||||
//
|
||||
// # Key derivation
|
||||
// Package cbk derives NaCl crypto_box keypairs (Curve25519) deterministically
|
||||
// from BIP39 mnemonic phrases. That is all it does; the resulting keys are
|
||||
// plain 32-byte crypto_box keys for use with golang.org/x/crypto/nacl/box.
|
||||
//
|
||||
// The same mnemonic and passphrase always produce the same keypair:
|
||||
//
|
||||
@@ -16,21 +14,13 @@
|
||||
// HKDF-SHA256, using no salt and DerivationInfo as the info string.
|
||||
// 4. The public key is the Curve25519 scalar multiplication of the
|
||||
// private key with the base point, via golang.org/x/crypto/nacl/box.
|
||||
//
|
||||
// # Sealing
|
||||
//
|
||||
// Seal and Open wrap box.Seal and box.Open. A sealed message is the
|
||||
// 24-byte random nonce followed by the box ciphertext, so it can be stored
|
||||
// or transmitted as a single opaque byte string.
|
||||
package cbk
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hkdf"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/tyler-smith/go-bip39"
|
||||
@@ -43,56 +33,14 @@ const (
|
||||
// KeySize is the length in bytes of a crypto_box public or private key.
|
||||
KeySize = 32
|
||||
|
||||
// NonceSize is the length in bytes of the nonce that prefixes every
|
||||
// sealed message.
|
||||
NonceSize = 24
|
||||
|
||||
// DerivationInfo is the HKDF info string that binds derived keys to
|
||||
// this package. Changing it changes every derived key.
|
||||
DerivationInfo = "sneak.berlin/go/x/pkg/cbk crypto_box v1"
|
||||
|
||||
hexKeyLength = KeySize * 2
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidKeyHex is returned by KeyFromHex for input that is not
|
||||
// exactly 64 hexadecimal characters.
|
||||
ErrInvalidKeyHex = errors.New("cbk: key must be 64 hexadecimal characters")
|
||||
|
||||
// ErrSealedTooShort is returned by Open when the input is shorter than
|
||||
// the nonce that must prefix it.
|
||||
ErrSealedTooShort = errors.New("cbk: sealed message is shorter than a nonce")
|
||||
|
||||
// ErrOpenFailed is returned by Open when authentication fails: the
|
||||
// message was sealed for a different keypair, by a different sender,
|
||||
// or has been modified.
|
||||
ErrOpenFailed = errors.New("cbk: open failed: wrong keys or modified message")
|
||||
)
|
||||
|
||||
// CryptoBoxKey is a 32-byte crypto_box key, either public or private.
|
||||
type CryptoBoxKey [KeySize]byte
|
||||
|
||||
// KeyFromHex parses a key from its 64-character hexadecimal encoding, as
|
||||
// produced by Hex.
|
||||
func KeyFromHex(encoded string) (CryptoBoxKey, error) {
|
||||
var key CryptoBoxKey
|
||||
|
||||
if len(encoded) != hexKeyLength {
|
||||
return key, fmt.Errorf(
|
||||
"%w: got %d characters", ErrInvalidKeyHex, len(encoded),
|
||||
)
|
||||
}
|
||||
|
||||
decoded, err := hex.DecodeString(encoded)
|
||||
if err != nil {
|
||||
return key, fmt.Errorf("%w: %w", ErrInvalidKeyHex, err)
|
||||
}
|
||||
|
||||
copy(key[:], decoded)
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// Bytes returns a copy of the key as a byte slice.
|
||||
func (k CryptoBoxKey) Bytes() []byte {
|
||||
return k[:]
|
||||
@@ -151,7 +99,7 @@ func New(p Params) (CryptoBoxKeypair, error) {
|
||||
// FromMnemonic derives the keypair for mnemonic with an empty passphrase.
|
||||
// It is shorthand for New(Params{Mnemonic: mnemonic}).
|
||||
func FromMnemonic(mnemonic string) (CryptoBoxKeypair, error) {
|
||||
return New(Params{Mnemonic: mnemonic})
|
||||
return New(Params{Mnemonic: mnemonic, Passphrase: ""})
|
||||
}
|
||||
|
||||
// PublicKeyBytes returns a copy of the public key as a byte slice.
|
||||
@@ -175,49 +123,3 @@ func (kp CryptoBoxKeypair) PublicKeyHex() string {
|
||||
func (kp CryptoBoxKeypair) PrivateKeyHex() string {
|
||||
return kp.PrivateKey.Hex()
|
||||
}
|
||||
|
||||
// Seal encrypts and authenticates message from this keypair to recipient.
|
||||
// The result is a fresh random nonce followed by the ciphertext, so it is
|
||||
// NonceSize + len(message) + box.Overhead bytes long. Only the holder of
|
||||
// recipient's private key, knowing this keypair's public key, can Open it.
|
||||
func (kp CryptoBoxKeypair) Seal(
|
||||
message []byte, recipient CryptoBoxKey,
|
||||
) ([]byte, error) {
|
||||
var nonce [NonceSize]byte
|
||||
|
||||
_, err := rand.Read(nonce[:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cbk: generating nonce: %w", err)
|
||||
}
|
||||
|
||||
peersPublicKey := [KeySize]byte(recipient)
|
||||
privateKey := [KeySize]byte(kp.PrivateKey)
|
||||
|
||||
return box.Seal(nonce[:], message, &nonce, &peersPublicKey, &privateKey), nil
|
||||
}
|
||||
|
||||
// Open authenticates and decrypts a message that sender produced with Seal
|
||||
// for this keypair. It returns ErrSealedTooShort if sealed cannot contain a
|
||||
// nonce and ErrOpenFailed if authentication fails.
|
||||
func (kp CryptoBoxKeypair) Open(
|
||||
sealed []byte, sender CryptoBoxKey,
|
||||
) ([]byte, error) {
|
||||
if len(sealed) < NonceSize {
|
||||
return nil, ErrSealedTooShort
|
||||
}
|
||||
|
||||
var nonce [NonceSize]byte
|
||||
|
||||
copy(nonce[:], sealed[:NonceSize])
|
||||
|
||||
peersPublicKey := [KeySize]byte(sender)
|
||||
privateKey := [KeySize]byte(kp.PrivateKey)
|
||||
ciphertext := sealed[NonceSize:]
|
||||
|
||||
message, ok := box.Open(nil, ciphertext, &nonce, &peersPublicKey, &privateKey)
|
||||
if !ok {
|
||||
return nil, ErrOpenFailed
|
||||
}
|
||||
|
||||
return message, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user