fermentord/vendor/github.com/nats-io/nkeys/xkeys.go

185 lines
4.9 KiB
Go

// Copyright 2022-2023 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package nkeys
import (
"bytes"
"crypto/rand"
"encoding/binary"
"io"
"golang.org/x/crypto/curve25519"
"golang.org/x/crypto/nacl/box"
)
// This package will support safe use of X25519 keys for asymmetric encryption.
// We will be compatible with nacl.Box, but generate random nonces automatically.
// We may add more advanced options in the future for group recipients and better
// end to end algorithms.
const (
curveKeyLen = 32
curveDecodeLen = 35
curveNonceLen = 24
)
type ckp struct {
seed [curveKeyLen]byte // Private raw key.
}
// CreateCurveKeys will create a Curve typed KeyPair.
func CreateCurveKeys() (KeyPair, error) {
return CreateCurveKeysWithRand(rand.Reader)
}
// CreateCurveKeysWithRand will create a Curve typed KeyPair
// with specified rand source.
func CreateCurveKeysWithRand(rr io.Reader) (KeyPair, error) {
var kp ckp
_, err := io.ReadFull(rr, kp.seed[:])
if err != nil {
return nil, err
}
return &kp, nil
}
// Will create a curve key pair from seed.
func FromCurveSeed(seed []byte) (KeyPair, error) {
pb, raw, err := DecodeSeed(seed)
if err != nil {
return nil, err
}
if pb != PrefixByteCurve || len(raw) != curveKeyLen {
return nil, ErrInvalidCurveSeed
}
var kp ckp
copy(kp.seed[:], raw)
return &kp, nil
}
// Seed will return the encoded seed.
func (pair *ckp) Seed() ([]byte, error) {
return EncodeSeed(PrefixByteCurve, pair.seed[:])
}
// PublicKey will return the encoded public key.
func (pair *ckp) PublicKey() (string, error) {
var pub [curveKeyLen]byte
curve25519.ScalarBaseMult(&pub, &pair.seed)
key, err := Encode(PrefixByteCurve, pub[:])
return string(key), err
}
// PrivateKey will return the encoded private key.
func (pair *ckp) PrivateKey() ([]byte, error) {
return Encode(PrefixBytePrivate, pair.seed[:])
}
func decodePubCurveKey(src string, dest []byte) error {
var raw [curveDecodeLen]byte // should always be 35
n, err := b32Enc.Decode(raw[:], []byte(src))
if err != nil {
return err
}
if n != curveDecodeLen {
return ErrInvalidCurveKey
}
// Make sure it is what we expected.
if prefix := PrefixByte(raw[0]); prefix != PrefixByteCurve {
return ErrInvalidPublicKey
}
var crc uint16
end := n - 2
sum := raw[end:n]
checksum := bytes.NewReader(sum)
if err := binary.Read(checksum, binary.LittleEndian, &crc); err != nil {
return err
}
// ensure checksum is valid
if err := validate(raw[:end], crc); err != nil {
return err
}
// Copy over, ignore prefix byte.
copy(dest, raw[1:end])
return nil
}
// Only version for now, but could add in X3DH in the future, etc.
const XKeyVersionV1 = "xkv1"
const vlen = len(XKeyVersionV1)
// Seal is compatible with nacl.Box.Seal() and can be used in similar situations for small messages.
// We generate the nonce from crypto rand by default.
func (pair *ckp) Seal(input []byte, recipient string) ([]byte, error) {
return pair.SealWithRand(input, recipient, rand.Reader)
}
func (pair *ckp) SealWithRand(input []byte, recipient string, rr io.Reader) ([]byte, error) {
var (
rpub [curveKeyLen]byte
nonce [curveNonceLen]byte
out [vlen + curveNonceLen]byte
err error
)
if err = decodePubCurveKey(recipient, rpub[:]); err != nil {
return nil, ErrInvalidRecipient
}
if _, err := io.ReadFull(rr, nonce[:]); err != nil {
return nil, err
}
copy(out[:vlen], []byte(XKeyVersionV1))
copy(out[vlen:], nonce[:])
return box.Seal(out[:], input, &nonce, &rpub, &pair.seed), nil
}
func (pair *ckp) Open(input []byte, sender string) ([]byte, error) {
if len(input) <= vlen+curveNonceLen {
return nil, ErrInvalidEncrypted
}
var (
spub [curveKeyLen]byte
nonce [curveNonceLen]byte
err error
)
if !bytes.Equal(input[:vlen], []byte(XKeyVersionV1)) {
return nil, ErrInvalidEncVersion
}
copy(nonce[:], input[vlen:vlen+curveNonceLen])
if err = decodePubCurveKey(sender, spub[:]); err != nil {
return nil, ErrInvalidSender
}
decrypted, ok := box.Open(nil, input[vlen+curveNonceLen:], &nonce, &spub, &pair.seed)
if !ok {
return nil, ErrCouldNotDecrypt
}
return decrypted, nil
}
// Wipe will randomize the contents of the secret key
func (pair *ckp) Wipe() {
io.ReadFull(rand.Reader, pair.seed[:])
}
func (pair *ckp) Sign(_ []byte) ([]byte, error) {
return nil, ErrInvalidCurveKeyOperation
}
func (pair *ckp) Verify(_ []byte, _ []byte) error {
return ErrInvalidCurveKeyOperation
}