nip04.go raw

   1  package nip04
   2  
   3  import (
   4  	"common/crypto/secp256k1"
   5  	"common/helpers"
   6  	"common/jsbridge/subtle"
   7  )
   8  
   9  // SharedKey derives the NIP-04 shared secret from ECDH (x-coordinate only).
  10  func SharedKey(seckey, pubkey [32]byte) ([32]byte, bool) {
  11  	return secp256k1.ECDH(seckey, pubkey)
  12  }
  13  
  14  // Encrypt encrypts plaintext using NIP-04 (AES-256-CBC).
  15  // sharedKey: 32 bytes from SharedKey.
  16  // Calls fn with the encoded string "base64(ciphertext)?iv=base64(iv)".
  17  func Encrypt(sharedKey [32]byte, plaintext string, fn func(string)) {
  18  	iv := make([]byte, 16)
  19  	subtle.RandomBytes(iv)
  20  	subtle.AESCBCEncrypt(sharedKey[:], iv, []byte(plaintext), func(ct []byte) {
  21  		fn(helpers.Base64Encode(ct) + "?iv=" + helpers.Base64Encode(iv))
  22  	})
  23  }
  24  
  25  // Decrypt decrypts a NIP-04 message "base64(ciphertext)?iv=base64(iv)".
  26  // Calls fn with the plaintext.
  27  func Decrypt(sharedKey [32]byte, message string, fn func(string)) {
  28  	// Split on "?iv=".
  29  	ivIdx := -1
  30  	for i := 0; i < len(message)-3; i++ {
  31  		if message[i] == '?' && message[i+1] == 'i' && message[i+2] == 'v' && message[i+3] == '=' {
  32  			ivIdx = i
  33  			break
  34  		}
  35  	}
  36  	if ivIdx < 0 {
  37  		fn("")
  38  		return
  39  	}
  40  	ct := helpers.Base64Decode(message[:ivIdx])
  41  	iv := helpers.Base64Decode(message[ivIdx+4:])
  42  	if ct == nil || iv == nil {
  43  		fn("")
  44  		return
  45  	}
  46  	subtle.AESCBCDecrypt(sharedKey[:], iv, ct, func(pt []byte) {
  47  		fn(string(pt))
  48  	})
  49  }
  50