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 }