status-go/services/shhext/chat/x3dh_test.go

233 lines
7.9 KiB
Go

package chat
import (
"testing"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/ecies"
"github.com/stretchr/testify/require"
)
const (
alicePrivateKey = "00000000000000000000000000000000"
aliceEphemeralKey = "11111111111111111111111111111111"
bobPrivateKey = "22222222222222222222222222222222"
bobSignedPreKey = "33333333333333333333333333333333"
base64Bundle = "CiECkJmdu/QwNL/7HdU+rB60wzpOocT0i6WFz944MIQPBVUSKAoBMhIjCiECPHKt20/fCa+U8MlNf+kqOGp+cM+KHYWRY4a7JTXHsbEiQT9Wse3UkJgo6/1HzxQfHcZNBaMH0j+0eylfBf1ropsLZ7yZM98k/qDQ3ZW5uHXQ4zhY8E1Q7HDytqm62k5JIPYA"
)
var sharedKey = []byte{0xa4, 0xe9, 0x23, 0xd0, 0xaf, 0x8f, 0xe7, 0x8a, 0x5, 0x63, 0x63, 0xbe, 0x20, 0xe7, 0x1c, 0xa, 0x58, 0xe5, 0x69, 0xea, 0x8f, 0xc1, 0xf7, 0x92, 0x89, 0xec, 0xa1, 0xd, 0x9f, 0x68, 0x13, 0x3a}
func bobBundle() (*Bundle, error) {
privateKey, err := crypto.ToECDSA([]byte(bobPrivateKey))
if err != nil {
return nil, err
}
signedPreKey, err := crypto.ToECDSA([]byte(bobSignedPreKey))
if err != nil {
return nil, err
}
compressedPreKey := crypto.CompressPubkey(&signedPreKey.PublicKey)
signature, err := crypto.Sign(crypto.Keccak256(compressedPreKey), privateKey)
if err != nil {
return nil, err
}
signedPreKeys := make(map[string]*SignedPreKey)
signedPreKeys[bobInstallationID] = &SignedPreKey{SignedPreKey: compressedPreKey}
bundle := Bundle{
Identity: crypto.CompressPubkey(&privateKey.PublicKey),
SignedPreKeys: signedPreKeys,
Signature: signature,
}
return &bundle, nil
}
func TestNewBundleContainer(t *testing.T) {
privateKey, err := crypto.ToECDSA([]byte(alicePrivateKey))
require.NoError(t, err, "Private key should be generated without errors")
bundleContainer, err := NewBundleContainer(privateKey, bobInstallationID)
require.NoError(t, err, "Bundle container should be created successfully")
err = SignBundle(privateKey, bundleContainer)
require.NoError(t, err, "Bundle container should be signed successfully")
require.NoError(t, err, "Bundle container should be created successfully")
bundle := bundleContainer.Bundle
require.NotNil(t, bundle, "Bundle should be generated without errors")
signatureMaterial := append([]byte(bobInstallationID), bundle.GetSignedPreKeys()[bobInstallationID].GetSignedPreKey()...)
recoveredPublicKey, err := crypto.SigToPub(
crypto.Keccak256(signatureMaterial),
bundle.Signature,
)
require.NoError(t, err, "Public key should be recovered from the bundle successfully")
require.Equal(
t,
&privateKey.PublicKey,
recoveredPublicKey,
"The correct public key should be recovered",
)
}
func TestSignBundle(t *testing.T) {
privateKey, err := crypto.ToECDSA([]byte(alicePrivateKey))
require.NoError(t, err, "Private key should be generated without errors")
bundleContainer1, err := NewBundleContainer(privateKey, "1")
require.NoError(t, err, "Bundle container should be created successfully")
bundle1 := bundleContainer1.Bundle
require.NotNil(t, bundle1, "Bundle should be generated without errors")
// We add a signed pre key
signedPreKeys := bundle1.GetSignedPreKeys()
signedPreKeys["2"] = &SignedPreKey{SignedPreKey: []byte("key")}
err = SignBundle(privateKey, bundleContainer1)
require.NoError(t, err)
signatureMaterial := append([]byte("1"), bundle1.GetSignedPreKeys()["1"].GetSignedPreKey()...)
signatureMaterial = append(signatureMaterial, []byte("2")...)
signatureMaterial = append(signatureMaterial, []byte("key")...)
recoveredPublicKey, err := crypto.SigToPub(
crypto.Keccak256(signatureMaterial),
bundleContainer1.GetBundle().Signature,
)
require.NoError(t, err, "Public key should be recovered from the bundle successfully")
require.Equal(
t,
&privateKey.PublicKey,
recoveredPublicKey,
"The correct public key should be recovered",
)
}
func TestToBase64(t *testing.T) {
bundle, err := bobBundle()
require.NoError(t, err, "Test bundle should be generated without errors")
actualBase64Bundle, err := bundle.ToBase64()
require.NoError(t, err, "No error should be reported")
require.Equal(
t,
base64Bundle,
actualBase64Bundle,
"The correct bundle should be generated",
)
}
func TestFromBase64(t *testing.T) {
expectedBundle, err := bobBundle()
require.NoError(t, err, "Test bundle should be generated without errors")
actualBundle, err := FromBase64(base64Bundle)
require.NoError(t, err, "Bundle should be unmarshaled without errors")
require.Equal(
t,
expectedBundle,
actualBundle,
"The correct bundle should be generated",
)
}
func TestExtractIdentity(t *testing.T) {
privateKey, err := crypto.ToECDSA([]byte(alicePrivateKey))
require.NoError(t, err, "Private key should be generated without errors")
bundleContainer, err := NewBundleContainer(privateKey, "1")
require.NoError(t, err, "Bundle container should be created successfully")
err = SignBundle(privateKey, bundleContainer)
require.NoError(t, err, "Bundle container should be signed successfully")
bundle := bundleContainer.Bundle
require.NotNil(t, bundle, "Bundle should be generated without errors")
recoveredPublicKey, err := ExtractIdentity(bundle)
require.NoError(t, err, "Public key should be recovered from the bundle successfully")
require.Equal(
t,
"0x042ed557f5ad336b31a49857e4e9664954ac33385aa20a93e2d64bfe7f08f51277bcb27c1259f802a52ed3ea7ac939043f0cc864e27400294bf121f23877995852",
recoveredPublicKey,
"The correct public key should be recovered",
)
}
// Alice wants to send a message to Bob
func TestX3dhActive(t *testing.T) {
bobIdentityKey, err := crypto.ToECDSA([]byte(bobPrivateKey))
require.NoError(t, err, "Bundle identity key should be generated without errors")
bobSignedPreKey, err := crypto.ToECDSA([]byte(bobSignedPreKey))
require.NoError(t, err, "Bundle signed pre key should be generated without errors")
aliceIdentityKey, err := crypto.ToECDSA([]byte(alicePrivateKey))
require.NoError(t, err, "Private key should be generated without errors")
aliceEphemeralKey, err := crypto.ToECDSA([]byte(aliceEphemeralKey))
require.NoError(t, err, "Ephemeral key should be generated without errors")
x3dh, err := x3dhActive(
ecies.ImportECDSA(aliceIdentityKey),
ecies.ImportECDSAPublic(&bobSignedPreKey.PublicKey),
ecies.ImportECDSA(aliceEphemeralKey),
ecies.ImportECDSAPublic(&bobIdentityKey.PublicKey),
)
require.NoError(t, err, "Shared key should be generated without errors")
require.Equal(t, sharedKey, x3dh, "Should generate the correct key")
}
// Bob receives a message from Alice
func TestPerformPassiveX3DH(t *testing.T) {
alicePrivateKey, err := crypto.ToECDSA([]byte(alicePrivateKey))
require.NoError(t, err, "Private key should be generated without errors")
bobSignedPreKey, err := crypto.ToECDSA([]byte(bobSignedPreKey))
require.NoError(t, err, "Private key should be generated without errors")
aliceEphemeralKey, err := crypto.ToECDSA([]byte(aliceEphemeralKey))
require.NoError(t, err, "Ephemeral key should be generated without errors")
bobPrivateKey, err := crypto.ToECDSA([]byte(bobPrivateKey))
require.NoError(t, err, "Private key should be generated without errors")
x3dh, err := PerformPassiveX3DH(
&alicePrivateKey.PublicKey,
bobSignedPreKey,
&aliceEphemeralKey.PublicKey,
bobPrivateKey,
)
require.NoError(t, err, "Shared key should be generated without errors")
require.Equal(t, sharedKey, x3dh, "Should generate the correct key")
}
func TestPerformActiveX3DH(t *testing.T) {
bundle, err := bobBundle()
require.NoError(t, err, "Test bundle should be generated without errors")
privateKey, err := crypto.ToECDSA([]byte(bobPrivateKey))
require.NoError(t, err, "Private key should be imported without errors")
signedPreKey := bundle.GetSignedPreKeys()[bobInstallationID].GetSignedPreKey()
actualSharedSecret, actualEphemeralKey, err := PerformActiveX3DH(bundle.GetIdentity(), signedPreKey, privateKey)
require.NoError(t, err, "No error should be reported")
require.NotNil(t, actualEphemeralKey, "An ephemeral key-pair should be generated")
require.NotNil(t, actualSharedSecret, "A shared key should be generated")
}