xkeys.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. // Copyright 2022-2023 The NATS Authors
  2. // Licensed under the Apache License, Version 2.0 (the "License");
  3. // you may not use this file except in compliance with the License.
  4. // You may obtain a copy of the License at
  5. //
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. package nkeys
  14. import (
  15. "bytes"
  16. "crypto/rand"
  17. "encoding/binary"
  18. "io"
  19. "golang.org/x/crypto/curve25519"
  20. "golang.org/x/crypto/nacl/box"
  21. )
  22. // This package will support safe use of X25519 keys for asymmetric encryption.
  23. // We will be compatible with nacl.Box, but generate random nonces automatically.
  24. // We may add more advanced options in the future for group recipients and better
  25. // end to end algorithms.
  26. const (
  27. curveKeyLen = 32
  28. curveDecodeLen = 35
  29. curveNonceLen = 24
  30. )
  31. type ckp struct {
  32. seed [curveKeyLen]byte // Private raw key.
  33. }
  34. // CreateCurveKeys will create a Curve typed KeyPair.
  35. func CreateCurveKeys() (KeyPair, error) {
  36. return CreateCurveKeysWithRand(rand.Reader)
  37. }
  38. // CreateCurveKeysWithRand will create a Curve typed KeyPair
  39. // with specified rand source.
  40. func CreateCurveKeysWithRand(rr io.Reader) (KeyPair, error) {
  41. var kp ckp
  42. _, err := io.ReadFull(rr, kp.seed[:])
  43. if err != nil {
  44. return nil, err
  45. }
  46. return &kp, nil
  47. }
  48. // Will create a curve key pair from seed.
  49. func FromCurveSeed(seed []byte) (KeyPair, error) {
  50. pb, raw, err := DecodeSeed(seed)
  51. if err != nil {
  52. return nil, err
  53. }
  54. if pb != PrefixByteCurve || len(raw) != curveKeyLen {
  55. return nil, ErrInvalidCurveSeed
  56. }
  57. var kp ckp
  58. copy(kp.seed[:], raw)
  59. return &kp, nil
  60. }
  61. // Seed will return the encoded seed.
  62. func (pair *ckp) Seed() ([]byte, error) {
  63. return EncodeSeed(PrefixByteCurve, pair.seed[:])
  64. }
  65. // PublicKey will return the encoded public key.
  66. func (pair *ckp) PublicKey() (string, error) {
  67. var pub [curveKeyLen]byte
  68. curve25519.ScalarBaseMult(&pub, &pair.seed)
  69. key, err := Encode(PrefixByteCurve, pub[:])
  70. return string(key), err
  71. }
  72. // PrivateKey will return the encoded private key.
  73. func (pair *ckp) PrivateKey() ([]byte, error) {
  74. return Encode(PrefixBytePrivate, pair.seed[:])
  75. }
  76. func decodePubCurveKey(src string, dest []byte) error {
  77. var raw [curveDecodeLen]byte // should always be 35
  78. n, err := b32Enc.Decode(raw[:], []byte(src))
  79. if err != nil {
  80. return err
  81. }
  82. if n != curveDecodeLen {
  83. return ErrInvalidCurveKey
  84. }
  85. // Make sure it is what we expected.
  86. if prefix := PrefixByte(raw[0]); prefix != PrefixByteCurve {
  87. return ErrInvalidPublicKey
  88. }
  89. var crc uint16
  90. end := n - 2
  91. sum := raw[end:n]
  92. checksum := bytes.NewReader(sum)
  93. if err := binary.Read(checksum, binary.LittleEndian, &crc); err != nil {
  94. return err
  95. }
  96. // ensure checksum is valid
  97. if err := validate(raw[:end], crc); err != nil {
  98. return err
  99. }
  100. // Copy over, ignore prefix byte.
  101. copy(dest, raw[1:end])
  102. return nil
  103. }
  104. // Only version for now, but could add in X3DH in the future, etc.
  105. const XKeyVersionV1 = "xkv1"
  106. const vlen = len(XKeyVersionV1)
  107. // Seal is compatible with nacl.Box.Seal() and can be used in similar situations for small messages.
  108. // We generate the nonce from crypto rand by default.
  109. func (pair *ckp) Seal(input []byte, recipient string) ([]byte, error) {
  110. return pair.SealWithRand(input, recipient, rand.Reader)
  111. }
  112. func (pair *ckp) SealWithRand(input []byte, recipient string, rr io.Reader) ([]byte, error) {
  113. var (
  114. rpub [curveKeyLen]byte
  115. nonce [curveNonceLen]byte
  116. out [vlen + curveNonceLen]byte
  117. err error
  118. )
  119. if err = decodePubCurveKey(recipient, rpub[:]); err != nil {
  120. return nil, ErrInvalidRecipient
  121. }
  122. if _, err := io.ReadFull(rr, nonce[:]); err != nil {
  123. return nil, err
  124. }
  125. copy(out[:vlen], []byte(XKeyVersionV1))
  126. copy(out[vlen:], nonce[:])
  127. return box.Seal(out[:], input, &nonce, &rpub, &pair.seed), nil
  128. }
  129. func (pair *ckp) Open(input []byte, sender string) ([]byte, error) {
  130. if len(input) <= vlen+curveNonceLen {
  131. return nil, ErrInvalidEncrypted
  132. }
  133. var (
  134. spub [curveKeyLen]byte
  135. nonce [curveNonceLen]byte
  136. err error
  137. )
  138. if !bytes.Equal(input[:vlen], []byte(XKeyVersionV1)) {
  139. return nil, ErrInvalidEncVersion
  140. }
  141. copy(nonce[:], input[vlen:vlen+curveNonceLen])
  142. if err = decodePubCurveKey(sender, spub[:]); err != nil {
  143. return nil, ErrInvalidSender
  144. }
  145. decrypted, ok := box.Open(nil, input[vlen+curveNonceLen:], &nonce, &spub, &pair.seed)
  146. if !ok {
  147. return nil, ErrCouldNotDecrypt
  148. }
  149. return decrypted, nil
  150. }
  151. // Wipe will randomize the contents of the secret key
  152. func (pair *ckp) Wipe() {
  153. io.ReadFull(rand.Reader, pair.seed[:])
  154. }
  155. func (pair *ckp) Sign(_ []byte) ([]byte, error) {
  156. return nil, ErrInvalidCurveKeyOperation
  157. }
  158. func (pair *ckp) Verify(_ []byte, _ []byte) error {
  159. return ErrInvalidCurveKeyOperation
  160. }