github.com/status-im/status-go@v1.1.0/protocol/encryption/x3dh_test.go (about)

     1  package encryption
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/require"
     8  
     9  	"github.com/status-im/status-go/eth-node/crypto"
    10  	"github.com/status-im/status-go/eth-node/crypto/ecies"
    11  )
    12  
    13  const (
    14  	alicePrivateKey   = "00000000000000000000000000000000"
    15  	aliceEphemeralKey = "11111111111111111111111111111111"
    16  	bobPrivateKey     = "22222222222222222222222222222222"
    17  	bobSignedPreKey   = "33333333333333333333333333333333"
    18  )
    19  
    20  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}
    21  
    22  func bobBundle() (*Bundle, error) {
    23  	privateKey, err := crypto.ToECDSA([]byte(bobPrivateKey))
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  
    28  	signedPreKey, err := crypto.ToECDSA([]byte(bobSignedPreKey))
    29  	if err != nil {
    30  		return nil, err
    31  	}
    32  
    33  	compressedPreKey := crypto.CompressPubkey(&signedPreKey.PublicKey)
    34  
    35  	signature, err := crypto.Sign(crypto.Keccak256(compressedPreKey), privateKey)
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  
    40  	signedPreKeys := make(map[string]*SignedPreKey)
    41  	signedPreKeys[bobInstallationID] = &SignedPreKey{SignedPreKey: compressedPreKey}
    42  
    43  	bundle := Bundle{
    44  		Identity:      crypto.CompressPubkey(&privateKey.PublicKey),
    45  		SignedPreKeys: signedPreKeys,
    46  		Signature:     signature,
    47  	}
    48  
    49  	return &bundle, nil
    50  }
    51  
    52  func TestNewBundleContainer(t *testing.T) {
    53  	privateKey, err := crypto.ToECDSA([]byte(alicePrivateKey))
    54  	require.NoError(t, err, "Private key should be generated without errors")
    55  
    56  	bundleContainer, err := NewBundleContainer(privateKey, bobInstallationID)
    57  	require.NoError(t, err, "Bundle container should be created successfully")
    58  
    59  	err = SignBundle(privateKey, bundleContainer)
    60  	require.NoError(t, err, "Bundle container should be signed successfully")
    61  
    62  	require.NoError(t, err, "Bundle container should be created successfully")
    63  
    64  	bundle := bundleContainer.Bundle
    65  	require.NotNil(t, bundle, "Bundle should be generated without errors")
    66  
    67  	signatureMaterial := append([]byte(bobInstallationID), bundle.GetSignedPreKeys()[bobInstallationID].GetSignedPreKey()...)
    68  	signatureMaterial = append(signatureMaterial, []byte("0")...)
    69  	signatureMaterial = append(signatureMaterial, []byte(fmt.Sprint(bundle.GetTimestamp()))...)
    70  	recoveredPublicKey, err := crypto.SigToPub(
    71  		crypto.Keccak256(signatureMaterial),
    72  		bundle.Signature,
    73  	)
    74  
    75  	require.NoError(t, err, "Public key should be recovered from the bundle successfully")
    76  
    77  	require.Equal(
    78  		t,
    79  		privateKey.PublicKey,
    80  		*recoveredPublicKey,
    81  		"The correct public key should be recovered",
    82  	)
    83  }
    84  
    85  func TestSignBundle(t *testing.T) {
    86  	privateKey, err := crypto.ToECDSA([]byte(alicePrivateKey))
    87  	require.NoError(t, err, "Private key should be generated without errors")
    88  
    89  	bundleContainer1, err := NewBundleContainer(privateKey, "1")
    90  	require.NoError(t, err, "Bundle container should be created successfully")
    91  
    92  	bundle1 := bundleContainer1.Bundle
    93  	require.NotNil(t, bundle1, "Bundle should be generated without errors")
    94  
    95  	// We add a signed pre key
    96  	signedPreKeys := bundle1.GetSignedPreKeys()
    97  	signedPreKeys["2"] = &SignedPreKey{SignedPreKey: []byte("key")}
    98  
    99  	err = SignBundle(privateKey, bundleContainer1)
   100  	require.NoError(t, err)
   101  
   102  	signatureMaterial := append([]byte("1"), bundle1.GetSignedPreKeys()["1"].GetSignedPreKey()...)
   103  	signatureMaterial = append(signatureMaterial, []byte("0")...)
   104  	signatureMaterial = append(signatureMaterial, []byte("2")...)
   105  	signatureMaterial = append(signatureMaterial, []byte("key")...)
   106  	signatureMaterial = append(signatureMaterial, []byte("0")...)
   107  	signatureMaterial = append(signatureMaterial, []byte(fmt.Sprint(bundle1.GetTimestamp()))...)
   108  
   109  	recoveredPublicKey, err := crypto.SigToPub(
   110  		crypto.Keccak256(signatureMaterial),
   111  		bundleContainer1.GetBundle().Signature,
   112  	)
   113  
   114  	require.NoError(t, err, "Public key should be recovered from the bundle successfully")
   115  
   116  	require.Equal(
   117  		t,
   118  		privateKey.PublicKey,
   119  		*recoveredPublicKey,
   120  		"The correct public key should be recovered",
   121  	)
   122  }
   123  
   124  func TestExtractIdentity(t *testing.T) {
   125  	privateKey, err := crypto.ToECDSA([]byte(alicePrivateKey))
   126  	require.NoError(t, err, "Private key should be generated without errors")
   127  
   128  	bundleContainer, err := NewBundleContainer(privateKey, "1")
   129  	require.NoError(t, err, "Bundle container should be created successfully")
   130  
   131  	err = SignBundle(privateKey, bundleContainer)
   132  	require.NoError(t, err, "Bundle container should be signed successfully")
   133  
   134  	bundle := bundleContainer.Bundle
   135  	require.NotNil(t, bundle, "Bundle should be generated without errors")
   136  
   137  	recoveredPublicKey, err := ExtractIdentity(bundle)
   138  
   139  	require.NoError(t, err, "Public key should be recovered from the bundle successfully")
   140  
   141  	require.Equal(
   142  		t,
   143  		privateKey.PublicKey,
   144  		*recoveredPublicKey,
   145  		"The correct public key should be recovered",
   146  	)
   147  }
   148  
   149  // Alice wants to send a message to Bob
   150  func TestX3dhActive(t *testing.T) {
   151  	bobIdentityKey, err := crypto.ToECDSA([]byte(bobPrivateKey))
   152  	require.NoError(t, err, "Bundle identity key should be generated without errors")
   153  
   154  	bobSignedPreKey, err := crypto.ToECDSA([]byte(bobSignedPreKey))
   155  	require.NoError(t, err, "Bundle signed pre key should be generated without errors")
   156  
   157  	aliceIdentityKey, err := crypto.ToECDSA([]byte(alicePrivateKey))
   158  	require.NoError(t, err, "Private key should be generated without errors")
   159  
   160  	aliceEphemeralKey, err := crypto.ToECDSA([]byte(aliceEphemeralKey))
   161  	require.NoError(t, err, "Ephemeral key should be generated without errors")
   162  
   163  	x3dh, err := x3dhActive(
   164  		ecies.ImportECDSA(aliceIdentityKey),
   165  		ecies.ImportECDSAPublic(&bobSignedPreKey.PublicKey),
   166  		ecies.ImportECDSA(aliceEphemeralKey),
   167  		ecies.ImportECDSAPublic(&bobIdentityKey.PublicKey),
   168  	)
   169  	require.NoError(t, err, "Shared key should be generated without errors")
   170  	require.Equal(t, sharedKey, x3dh, "Should generate the correct key")
   171  }
   172  
   173  // Bob receives a message from Alice
   174  func TestPerformPassiveX3DH(t *testing.T) {
   175  	alicePrivateKey, err := crypto.ToECDSA([]byte(alicePrivateKey))
   176  	require.NoError(t, err, "Private key should be generated without errors")
   177  
   178  	bobSignedPreKey, err := crypto.ToECDSA([]byte(bobSignedPreKey))
   179  	require.NoError(t, err, "Private key should be generated without errors")
   180  
   181  	aliceEphemeralKey, err := crypto.ToECDSA([]byte(aliceEphemeralKey))
   182  	require.NoError(t, err, "Ephemeral key should be generated without errors")
   183  
   184  	bobPrivateKey, err := crypto.ToECDSA([]byte(bobPrivateKey))
   185  	require.NoError(t, err, "Private key should be generated without errors")
   186  
   187  	x3dh, err := PerformPassiveX3DH(
   188  		&alicePrivateKey.PublicKey,
   189  		bobSignedPreKey,
   190  		&aliceEphemeralKey.PublicKey,
   191  		bobPrivateKey,
   192  	)
   193  	require.NoError(t, err, "Shared key should be generated without errors")
   194  	require.Equal(t, sharedKey, x3dh, "Should generate the correct key")
   195  }
   196  
   197  func TestPerformActiveX3DH(t *testing.T) {
   198  	bundle, err := bobBundle()
   199  	require.NoError(t, err, "Test bundle should be generated without errors")
   200  
   201  	privateKey, err := crypto.ToECDSA([]byte(bobPrivateKey))
   202  	require.NoError(t, err, "Private key should be imported without errors")
   203  
   204  	signedPreKey := bundle.GetSignedPreKeys()[bobInstallationID].GetSignedPreKey()
   205  
   206  	actualSharedSecret, actualEphemeralKey, err := PerformActiveX3DH(bundle.GetIdentity(), signedPreKey, privateKey)
   207  	require.NoError(t, err, "No error should be reported")
   208  	require.NotNil(t, actualEphemeralKey, "An ephemeral key-pair should be generated")
   209  	require.NotNil(t, actualSharedSecret, "A shared key should be generated")
   210  }