123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- // 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
- }
|