2023-08-10 13:30:38 +00:00
|
|
|
package library
|
2022-04-03 00:22:42 +00:00
|
|
|
|
|
|
|
import (
|
2023-08-09 15:32:00 +00:00
|
|
|
"crypto/ecdsa"
|
|
|
|
"crypto/elliptic"
|
2022-04-03 00:22:42 +00:00
|
|
|
"encoding/json"
|
2023-08-09 15:32:00 +00:00
|
|
|
"errors"
|
2022-04-03 00:22:42 +00:00
|
|
|
|
2023-08-09 15:32:00 +00:00
|
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
2022-04-03 00:22:42 +00:00
|
|
|
"github.com/ethereum/go-ethereum/crypto"
|
2023-08-09 15:32:00 +00:00
|
|
|
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
2022-12-16 00:47:14 +00:00
|
|
|
"github.com/waku-org/go-waku/waku/v2/payload"
|
2022-11-09 19:53:01 +00:00
|
|
|
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
|
|
|
"github.com/waku-org/go-waku/waku/v2/utils"
|
2023-11-07 19:48:43 +00:00
|
|
|
"google.golang.org/protobuf/proto"
|
2022-04-03 00:22:42 +00:00
|
|
|
)
|
|
|
|
|
2023-02-06 22:16:20 +00:00
|
|
|
func wakuMessage(messageJSON string) (*pb.WakuMessage, error) {
|
|
|
|
var msg *pb.WakuMessage
|
2022-04-03 00:22:42 +00:00
|
|
|
err := json.Unmarshal([]byte(messageJSON), &msg)
|
|
|
|
return msg, err
|
|
|
|
}
|
|
|
|
|
2023-08-09 15:32:00 +00:00
|
|
|
func EncodeSymmetric(messageJSON string, symmetricKey string, optionalSigningKey string) (string, error) {
|
2022-04-03 00:22:42 +00:00
|
|
|
msg, err := wakuMessage(messageJSON)
|
|
|
|
if err != nil {
|
2023-08-09 15:32:00 +00:00
|
|
|
return "", err
|
2022-04-03 00:22:42 +00:00
|
|
|
}
|
|
|
|
|
2023-11-07 19:48:43 +00:00
|
|
|
msgPayload := payload.Payload{
|
2022-04-03 00:22:42 +00:00
|
|
|
Data: msg.Payload,
|
2022-12-16 00:47:14 +00:00
|
|
|
Key: &payload.KeyInfo{
|
|
|
|
Kind: payload.Symmetric,
|
2022-04-03 00:22:42 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2022-10-14 14:19:17 +00:00
|
|
|
keyBytes, err := utils.DecodeHexString(symmetricKey)
|
2022-04-03 00:22:42 +00:00
|
|
|
if err != nil {
|
2023-08-09 15:32:00 +00:00
|
|
|
return "", err
|
2022-04-03 00:22:42 +00:00
|
|
|
}
|
|
|
|
|
2023-11-07 19:48:43 +00:00
|
|
|
msgPayload.Key.SymKey = keyBytes
|
2022-04-03 00:22:42 +00:00
|
|
|
|
|
|
|
if optionalSigningKey != "" {
|
2022-09-14 19:19:04 +00:00
|
|
|
signingKeyBytes, err := utils.DecodeHexString(optionalSigningKey)
|
2022-04-03 00:22:42 +00:00
|
|
|
if err != nil {
|
2023-08-09 15:32:00 +00:00
|
|
|
return "", err
|
2022-04-03 00:22:42 +00:00
|
|
|
}
|
|
|
|
|
2023-11-07 19:48:43 +00:00
|
|
|
msgPayload.Key.PrivKey, err = crypto.ToECDSA(signingKeyBytes)
|
2022-04-03 00:22:42 +00:00
|
|
|
if err != nil {
|
2023-08-09 15:32:00 +00:00
|
|
|
return "", err
|
2022-04-03 00:22:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-07 19:48:43 +00:00
|
|
|
msg.Version = proto.Uint32(payload.V1Encryption)
|
|
|
|
msg.Payload, err = msgPayload.Encode(1)
|
2023-08-09 15:32:00 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2022-04-03 00:22:42 +00:00
|
|
|
|
2023-08-09 15:32:00 +00:00
|
|
|
encodedMsg, err := json.Marshal(msg)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(encodedMsg), err
|
2022-04-03 00:22:42 +00:00
|
|
|
}
|
|
|
|
|
2023-08-09 15:32:00 +00:00
|
|
|
func EncodeAsymmetric(messageJSON string, publicKey string, optionalSigningKey string) (string, error) {
|
2022-04-03 00:22:42 +00:00
|
|
|
msg, err := wakuMessage(messageJSON)
|
|
|
|
if err != nil {
|
2023-08-09 15:32:00 +00:00
|
|
|
return "", err
|
2022-04-03 00:22:42 +00:00
|
|
|
}
|
|
|
|
|
2023-11-07 19:48:43 +00:00
|
|
|
msgPayload := payload.Payload{
|
2022-04-03 00:22:42 +00:00
|
|
|
Data: msg.Payload,
|
2022-12-16 00:47:14 +00:00
|
|
|
Key: &payload.KeyInfo{
|
|
|
|
Kind: payload.Asymmetric,
|
2022-04-03 00:22:42 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2022-09-14 19:19:04 +00:00
|
|
|
keyBytes, err := utils.DecodeHexString(publicKey)
|
2022-04-03 00:22:42 +00:00
|
|
|
if err != nil {
|
2023-08-09 15:32:00 +00:00
|
|
|
return "", err
|
2022-04-03 00:22:42 +00:00
|
|
|
}
|
|
|
|
|
2023-11-07 19:48:43 +00:00
|
|
|
msgPayload.Key.PubKey, err = unmarshalPubkey(keyBytes)
|
2022-04-03 00:22:42 +00:00
|
|
|
if err != nil {
|
2023-08-09 15:32:00 +00:00
|
|
|
return "", err
|
2022-04-03 00:22:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if optionalSigningKey != "" {
|
2022-09-14 19:19:04 +00:00
|
|
|
signingKeyBytes, err := utils.DecodeHexString(optionalSigningKey)
|
2022-04-03 00:22:42 +00:00
|
|
|
if err != nil {
|
2023-08-09 15:32:00 +00:00
|
|
|
return "", err
|
2022-04-03 00:22:42 +00:00
|
|
|
}
|
|
|
|
|
2023-11-07 19:48:43 +00:00
|
|
|
msgPayload.Key.PrivKey, err = crypto.ToECDSA(signingKeyBytes)
|
2022-04-03 00:22:42 +00:00
|
|
|
if err != nil {
|
2023-08-09 15:32:00 +00:00
|
|
|
return "", err
|
2022-04-03 00:22:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-07 19:48:43 +00:00
|
|
|
msg.Version = proto.Uint32(payload.V1Encryption)
|
|
|
|
msg.Payload, err = msgPayload.Encode(payload.V1Encryption)
|
2023-08-09 15:32:00 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2022-04-03 00:22:42 +00:00
|
|
|
|
2023-08-09 15:32:00 +00:00
|
|
|
encodedMsg, err := json.Marshal(msg)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(encodedMsg), err
|
|
|
|
}
|
|
|
|
|
|
|
|
func extractPubKeyAndSignature(payload *payload.DecodedPayload) (pubkey string, signature string) {
|
|
|
|
pkBytes := crypto.FromECDSAPub(payload.PubKey)
|
|
|
|
if len(pkBytes) != 0 {
|
|
|
|
pubkey = hexutil.Encode(pkBytes)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(payload.Signature) != 0 {
|
|
|
|
signature = hexutil.Encode(payload.Signature)
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-10-28 23:37:53 +00:00
|
|
|
type v0Response struct {
|
|
|
|
Data []byte `json:"data"`
|
|
|
|
Padding []byte `json:"padding"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type v1Response struct {
|
|
|
|
PubKey string `json:"pubkey,omitempty"`
|
|
|
|
Signature string `json:"signature,omitempty"`
|
|
|
|
Data []byte `json:"data"`
|
|
|
|
Padding []byte `json:"padding"`
|
|
|
|
}
|
|
|
|
|
2023-08-09 15:32:00 +00:00
|
|
|
// DecodeSymmetric decodes a waku message using a 32 bytes symmetric key. The key must be a hex encoded string with "0x" prefix
|
|
|
|
func DecodeSymmetric(messageJSON string, symmetricKey string) (string, error) {
|
|
|
|
var msg pb.WakuMessage
|
|
|
|
err := json.Unmarshal([]byte(messageJSON), &msg)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2023-11-07 19:48:43 +00:00
|
|
|
if msg.GetVersion() == payload.Unencrypted {
|
2023-10-28 23:37:53 +00:00
|
|
|
return marshalJSON(v0Response{
|
|
|
|
Data: msg.Payload,
|
|
|
|
})
|
2023-11-07 19:48:43 +00:00
|
|
|
} else if msg.GetVersion() != payload.V1Encryption {
|
2023-08-09 15:32:00 +00:00
|
|
|
return "", errors.New("unsupported wakumessage version")
|
|
|
|
}
|
|
|
|
|
|
|
|
keyInfo := &payload.KeyInfo{
|
|
|
|
Kind: payload.Symmetric,
|
|
|
|
}
|
|
|
|
|
|
|
|
keyInfo.SymKey, err = utils.DecodeHexString(symmetricKey)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
payload, err := payload.DecodePayload(&msg, keyInfo)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
pubkey, signature := extractPubKeyAndSignature(payload)
|
|
|
|
|
2023-10-28 23:37:53 +00:00
|
|
|
response := v1Response{
|
2023-08-09 15:32:00 +00:00
|
|
|
PubKey: pubkey,
|
|
|
|
Signature: signature,
|
|
|
|
Data: payload.Data,
|
|
|
|
Padding: payload.Padding,
|
|
|
|
}
|
|
|
|
|
|
|
|
return marshalJSON(response)
|
|
|
|
}
|
|
|
|
|
|
|
|
// DecodeAsymmetric decodes a waku message using a secp256k1 private key. The key must be a hex encoded string with "0x" prefix
|
|
|
|
func DecodeAsymmetric(messageJSON string, privateKey string) (string, error) {
|
|
|
|
var msg pb.WakuMessage
|
|
|
|
err := json.Unmarshal([]byte(messageJSON), &msg)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2023-11-07 19:48:43 +00:00
|
|
|
if msg.GetVersion() == payload.Unencrypted {
|
2023-10-28 23:37:53 +00:00
|
|
|
return marshalJSON(v0Response{
|
|
|
|
Data: msg.Payload,
|
|
|
|
})
|
2023-11-07 19:48:43 +00:00
|
|
|
} else if msg.GetVersion() != payload.V1Encryption {
|
2023-08-09 15:32:00 +00:00
|
|
|
return "", errors.New("unsupported wakumessage version")
|
|
|
|
}
|
|
|
|
|
|
|
|
keyInfo := &payload.KeyInfo{
|
|
|
|
Kind: payload.Asymmetric,
|
|
|
|
}
|
|
|
|
|
|
|
|
keyBytes, err := utils.DecodeHexString(privateKey)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
keyInfo.PrivKey, err = crypto.ToECDSA(keyBytes)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
payload, err := payload.DecodePayload(&msg, keyInfo)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
pubkey, signature := extractPubKeyAndSignature(payload)
|
|
|
|
|
2023-10-28 23:37:53 +00:00
|
|
|
response := v1Response{
|
2023-08-09 15:32:00 +00:00
|
|
|
PubKey: pubkey,
|
|
|
|
Signature: signature,
|
|
|
|
Data: payload.Data,
|
|
|
|
Padding: payload.Padding,
|
|
|
|
}
|
|
|
|
|
|
|
|
return marshalJSON(response)
|
|
|
|
}
|
|
|
|
|
|
|
|
func unmarshalPubkey(pub []byte) (ecdsa.PublicKey, error) {
|
|
|
|
x, y := elliptic.Unmarshal(secp256k1.S256(), pub)
|
|
|
|
if x == nil {
|
|
|
|
return ecdsa.PublicKey{}, errors.New("invalid public key")
|
|
|
|
}
|
|
|
|
return ecdsa.PublicKey{Curve: secp256k1.S256(), X: x, Y: y}, nil
|
2022-04-03 00:22:42 +00:00
|
|
|
}
|