github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/pkg/keyring/nacl_key.go (about)

     1  package keyring
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/rand"
     6  	"encoding/pem"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  
    11  	"golang.org/x/crypto/nacl/box"
    12  )
    13  
    14  const (
    15  	naclKeyBlockType = "NACL KEY"
    16  
    17  	naclKeyLen = 32
    18  )
    19  
    20  var errNACLBadKey = errors.New("nacl: bad nacl key")
    21  
    22  // NACLKey contains a NACL crypto box keypair.
    23  type NACLKey struct {
    24  	publicKey  *[32]byte
    25  	privateKey *[32]byte
    26  }
    27  
    28  // PublicKey returns the public part of the keypair.
    29  func (n *NACLKey) PublicKey() *[32]byte {
    30  	return n.publicKey
    31  }
    32  
    33  // PrivateKey returns the private part of the keypair.
    34  func (n *NACLKey) PrivateKey() *[32]byte {
    35  	return n.privateKey
    36  }
    37  
    38  // GenerateKeyPair returns a couple keypairs that can be used for asymmetric
    39  // encryption/decryption using nacl crypto box API.
    40  func GenerateKeyPair(r io.Reader) (encryptorKey *NACLKey, decryptorKey *NACLKey, err error) {
    41  	senderPublicKey, senderPrivateKey, err := box.GenerateKey(r)
    42  	if err != nil {
    43  		return
    44  	}
    45  	receiverPublicKey, receiverPrivateKey, err := box.GenerateKey(r)
    46  	if err != nil {
    47  		return
    48  	}
    49  	encryptorKey = &NACLKey{
    50  		publicKey:  receiverPublicKey,
    51  		privateKey: senderPrivateKey,
    52  	}
    53  	decryptorKey = &NACLKey{
    54  		publicKey:  senderPublicKey,
    55  		privateKey: receiverPrivateKey,
    56  	}
    57  	return
    58  }
    59  
    60  // GenerateEncodedNACLKeyPair returns to byte slice containing the encoded
    61  // values of the couple of keypairs freshly generated.
    62  func GenerateEncodedNACLKeyPair() (marshaledEncryptorKey []byte, marshaledDecryptorKey []byte, err error) {
    63  	encryptorKey, decryptorKey, err := GenerateKeyPair(rand.Reader)
    64  	if err != nil {
    65  		return
    66  	}
    67  	marshaledEncryptorKey = MarshalNACLKey(encryptorKey)
    68  	marshaledDecryptorKey = MarshalNACLKey(decryptorKey)
    69  	return
    70  }
    71  
    72  // UnmarshalNACLKey takes and encoded value of a keypair and unmarshal its
    73  // value, returning the associated key.
    74  func UnmarshalNACLKey(marshaledKey []byte) (key *NACLKey, err error) {
    75  	keys, err := unmarshalPEMBlock(marshaledKey, naclKeyBlockType)
    76  	if err != nil {
    77  		return
    78  	}
    79  	if len(keys) != 2*naclKeyLen {
    80  		err = errNACLBadKey
    81  		return
    82  	}
    83  	publicKey := new([naclKeyLen]byte)
    84  	privateKey := new([naclKeyLen]byte)
    85  	copy(publicKey[:], keys)
    86  	copy(privateKey[:], keys[naclKeyLen:])
    87  	key = &NACLKey{
    88  		publicKey:  publicKey,
    89  		privateKey: privateKey,
    90  	}
    91  	return
    92  }
    93  
    94  // MarshalNACLKey takes a key and returns its encoded version.
    95  func MarshalNACLKey(key *NACLKey) []byte {
    96  	keyBytes := make([]byte, 2*naclKeyLen)
    97  	copy(keyBytes, key.publicKey[:])
    98  	copy(keyBytes[naclKeyLen:], key.privateKey[:])
    99  	return pem.EncodeToMemory(&pem.Block{
   100  		Type:  naclKeyBlockType,
   101  		Bytes: keyBytes,
   102  	})
   103  }
   104  
   105  func unmarshalPEMBlock(keyBytes []byte, blockType string) ([]byte, error) {
   106  	if !bytes.HasPrefix(keyBytes, []byte("-----BEGIN")) {
   107  		return nil, fmt.Errorf("nacl: bad PEM block header")
   108  	}
   109  	block, _ := pem.Decode(keyBytes)
   110  	if block == nil {
   111  		return nil, fmt.Errorf("nacl: failed to parse PEM block containing the public key")
   112  	}
   113  	if block.Type != blockType {
   114  		return nil, fmt.Errorf(`nacl: bad PEM block type, got %q expecting %q`,
   115  			block.Type, blockType)
   116  	}
   117  	return block.Bytes, nil
   118  }