Switched encryption of payload and keys to AES

Added Encrypted field to the protobuf
This commit is contained in:
Samuel Hawksby-Robinson 2021-02-11 17:07:11 +00:00 committed by Andrea Maria Piana
parent 0080de754e
commit b009bac5fb
6 changed files with 213 additions and 51 deletions

View File

@ -9,11 +9,17 @@ import (
"golang.org/x/crypto/sha3"
"github.com/ethereum/go-ethereum/crypto/ecies"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
)
const nonceLength = 12
const(
nonceLength = 12
defaultECHDSharedKeyLength = 16
defaultECHDMACLength = 16
)
var ErrInvalidCiphertextLength = errors.New("invalid cyphertext length")
@ -86,3 +92,11 @@ func HexToPubkey(pk string) (*ecdsa.PublicKey, error) {
}
return crypto.UnmarshalPubkey(bytes)
}
func MakeECDHSharedKey(yourPrivateKey *ecdsa.PrivateKey, theirPubKey *ecdsa.PublicKey) ([]byte, error) {
return ecies.ImportECDSA(yourPrivateKey).GenerateShared(
ecies.ImportECDSAPublic(theirPubKey),
defaultECHDSharedKeyLength,
defaultECHDMACLength,
)
}

View File

@ -5,7 +5,6 @@ import (
"context"
"crypto/ecdsa"
crand "crypto/rand"
"crypto/x509"
"database/sql"
"encoding/hex"
"fmt"
@ -23,8 +22,6 @@ import (
"github.com/davecgh/go-spew/spew"
"github.com/golang/protobuf/proto"
"github.com/ethereum/go-ethereum/crypto/ecies"
"github.com/status-im/status-go/appdatabase"
"github.com/status-im/status-go/appmetrics"
"github.com/status-im/status-go/connection"
@ -780,6 +777,7 @@ func (m *Messenger) attachIdentityImagesToChatIdentity(context chatContext, ci *
return err
}
// TODO remove the use of ProfilePicturesVisibility and create a new settings field,
if s.ProfilePicturesVisibility == accounts.ProfilePicturesVisibilityNone {
m.logger.Info(fmt.Sprintf("settings.ProfilePicturesVisibility is set to '%d', skipping attaching IdentityImages", s.ProfilePicturesVisibility))
return nil
@ -832,46 +830,95 @@ func (m *Messenger) attachIdentityImagesToChatIdentity(context chatContext, ci *
return nil
}
func (m *Messenger) encryptIdentityImagesWithContactPubKeys(ciis map[string]*protobuf.IdentityImage) error {
// Make ephemeral key
pk, err := crypto.GenerateKey()
func (m *Messenger) encryptIdentityImagesWithContactPubKeys(iis map[string]*protobuf.IdentityImage) (err error) {
// Make AES key
AESKey := make([]byte, 32)
_, err = crand.Read(AESKey)
if err != nil {
return err
}
// Marshal the ephemeral private key into bytes
mpk, err := x509.MarshalECPrivateKey(pk)
if err != nil {
return err
}
for _, ii := range ciis {
// Encrypt image payloads with the ephemeral public key
encryptedPayload, err := ecies.Encrypt(crand.Reader, ecies.ImportECDSAPublic(&pk.PublicKey), ii.Payload, nil, nil)
for _, ii := range iis {
// Encrypt image payload with the AES key
encryptedPayload, err := common.Encrypt(ii.Payload, AESKey, crand.Reader)
if err != nil {
return err
}
// Overwrite the unencrypted payload with the newly encrypted payload
ii.Payload = encryptedPayload
for _, c := range m.allContacts {
ii.Encrypted = true
m.allContacts.Range(func(contactID string, c *Contact) (shouldContinue bool) {
if !c.IsAdded() {
continue
return false
}
pubK, err := c.PublicKey()
if err != nil {
return err
return false
}
// Generate a Diffie-Helman (DH) between the sender private key and the recipient's public key
sharedKey, err := common.MakeECDHSharedKey(m.identity, pubK)
if err != nil {
return false
}
// Encrypt the marshalled ephemeral private key with the contact's public key
empk, err := ecies.Encrypt(crand.Reader, ecies.ImportECDSAPublic(pubK), mpk, nil, nil)
// Encrypt the main AES key with AES encryption using the DH key
eAESKey, err := common.Encrypt(AESKey, sharedKey, crand.Reader)
if err != nil {
return false
}
// Append the the encrypted main AES key to the IdentityImage's EncryptionKeys slice.
ii.EncryptionKeys = append(ii.EncryptionKeys, eAESKey)
return true
})
if err != nil {
return err
}
}
return nil
}
func (m *Messenger) decryptIdentityImagesWithIdentityPrivateKey(iis map[string]*protobuf.IdentityImage, senderPubKey *ecdsa.PublicKey) error {
image:
for _, ii := range iis {
for _, empk := range ii.EncryptionKeys {
// Generate a Diffie-Helman (DH) between the recipient's private key and the sender's public key
sharedKey, err := common.MakeECDHSharedKey(m.identity, senderPubKey)
if err != nil {
return err
}
// Append the the encrypted private key to the IdentityImage's EncryptionKeys slice.
ii.EncryptionKeys = append(ii.EncryptionKeys, empk)
// Decrypt the main encryption AES key with AES encryption using the DH key
dAESKey, err := common.Decrypt(empk, sharedKey)
if err != nil {
if err.Error() == "cipher: message authentication failed" {
continue
}
return err
}
if dAESKey == nil{
return errors.New("decrypting the payload encryption key resulted in no error and a nil key")
}
// Decrypt the payload with the newly decrypted main encryption AES key
payload, err := common.Decrypt(ii.Payload, dAESKey)
if err != nil {
return err
}
if payload == nil {
// TODO should this be a logger warn? A payload could theoretically be validly empty
return errors.New("decrypting the payload resulted in no error and a nil payload")
}
// Overwrite the payload with the decrypted data
ii.Payload = payload
ii.Encrypted = false // TODO handle the encryption state in the consuming code
continue image
}
}

View File

@ -79,6 +79,16 @@ func (cm *contactMap) Delete(contactID string) {
cm.sm.Delete(contactID)
}
func (cm *contactMap) Len() int {
count := 0
cm.Range(func(key string, value *Contact) (shouldContinue bool) {
count++
return true
})
return count
}
/*
|--------------------------------------------------------------------------
| systemMessageTranslationsMap

View File

@ -2564,3 +2564,81 @@ func (s *MessengerSuite) TestPublicMessageOnCommunityChat() {
s.Require().Error(err)
s.Require().Equal(ErrMessageForWrongChatType, err)
}
func (s *MessengerSuite) TestEncryptDecryptIdentityImagesWithContactPubKeys() {
smPayload := "hello small image"
lgPayload := "hello large image"
ci := protobuf.ChatIdentity{
Clock: uint64(time.Now().Unix()),
Images: map[string]*protobuf.IdentityImage{
"small": {
Payload: []byte(smPayload),
},
"large": {
Payload: []byte(lgPayload),
},
},
}
// Make contact keys and Contacts, set the Contacts to added
var contactKeys []*ecdsa.PrivateKey
for i:=0; i < 10; i++ {
contactKey, err := crypto.GenerateKey()
s.Require().NoError(err)
contactKeys = append(contactKeys, contactKey)
contact, err := BuildContactFromPublicKey(&contactKey.PublicKey)
s.Require().NoError(err)
contact.SystemTags = append(contact.SystemTags, contactAdded)
s.m.allContacts.Store(contact.ID, contact)
}
// Test encryptIdentityImagesWithContactPubKeys
err := s.m.encryptIdentityImagesWithContactPubKeys(ci.Images)
s.Require().NoError(err)
for _, ii := range ci.Images {
s.Require().Equal(s.m.allContacts.Len, len(ii.EncryptionKeys))
}
s.Require().NotEqual([]byte(smPayload), ci.Images["small"].Payload)
s.Require().NotEqual([]byte(lgPayload), ci.Images["large"].Payload)
// Switch messenger identities
sender := s.m.identity
s.m.identity = contactKeys[2]
// Test decryptIdentityImagesWithIdentityPrivateKey
err = s.m.decryptIdentityImagesWithIdentityPrivateKey(ci.Images, &sender.PublicKey)
s.Require().NoError(err)
s.Require().Equal(smPayload, string(ci.Images["small"].Payload))
s.Require().Equal(lgPayload, string(ci.Images["large"].Payload))
// RESET Messenger identity, Contacts and IdentityImage.EncryptionKeys
s.m.identity = sender
s.m.allContacts = nil
ci.Images["small"].EncryptionKeys = nil
ci.Images["large"].EncryptionKeys = nil
// Test encryptIdentityImagesWithContactPubKeys with no contacts
err = s.m.encryptIdentityImagesWithContactPubKeys(ci.Images)
s.Require().NoError(err)
for _, ii := range ci.Images {
s.Require().Equal(0, len(ii.EncryptionKeys))
}
s.Require().NotEqual([]byte(smPayload), ci.Images["small"].Payload)
s.Require().NotEqual([]byte(lgPayload), ci.Images["large"].Payload)
// Switch messenger identities
s.m.identity = contactKeys[2]
// Test decryptIdentityImagesWithIdentityPrivateKey with no valid identity
err = s.m.decryptIdentityImagesWithIdentityPrivateKey(ci.Images, &sender.PublicKey)
s.Require().NoError(err)
s.Require().NotEqual([]byte(smPayload), ci.Images["small"].Payload)
s.Require().NotEqual([]byte(lgPayload), ci.Images["large"].Payload)
}

View File

@ -149,7 +149,9 @@ type IdentityImage struct {
// image_type signals the image type and method of parsing the payload
ImageType ImageType `protobuf:"varint,3,opt,name=image_type,json=imageType,proto3,enum=protobuf.ImageType" json:"image_type,omitempty"`
// encryption_keys is a list of encrypted keys that can be used to decrypted an encrypted payload
EncryptionKeys [][]byte `protobuf:"bytes,4,rep,name=encryption_keys,json=encryptionKeys,proto3" json:"encryption_keys,omitempty"`
EncryptionKeys [][]byte `protobuf:"bytes,4,rep,name=encryption_keys,json=encryptionKeys,proto3" json:"encryption_keys,omitempty"`
// encrypted signals the encryption state of the payload, default is false.
Encrypted bool `protobuf:"varint,5,opt,name=encrypted,proto3" json:"encrypted,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -208,6 +210,13 @@ func (m *IdentityImage) GetEncryptionKeys() [][]byte {
return nil
}
func (m *IdentityImage) GetEncrypted() bool {
if m != nil {
return m.Encrypted
}
return false
}
func init() {
proto.RegisterEnum("protobuf.IdentityImage_SourceType", IdentityImage_SourceType_name, IdentityImage_SourceType_value)
proto.RegisterType((*ChatIdentity)(nil), "protobuf.ChatIdentity")
@ -220,31 +229,32 @@ func init() {
}
var fileDescriptor_7a652489000a5879 = []byte{
// 414 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x90, 0xdf, 0x6e, 0xd3, 0x30,
0x14, 0xc6, 0x49, 0xd2, 0x76, 0xeb, 0x71, 0xd7, 0x55, 0x2e, 0xd2, 0xc2, 0xae, 0x42, 0x6f, 0xe8,
0x0d, 0x41, 0x2a, 0x37, 0x68, 0x5c, 0x85, 0x52, 0xa4, 0x69, 0x28, 0x9d, 0xdc, 0x8e, 0x69, 0xdc,
0x58, 0x5e, 0x6a, 0x58, 0xd4, 0xd4, 0x8e, 0x62, 0x17, 0xc9, 0xcf, 0xc1, 0x5b, 0xf0, 0x94, 0x68,
0x76, 0xb3, 0xa4, 0x57, 0x3e, 0x7f, 0xbe, 0xf3, 0x3b, 0x9f, 0x0f, 0x8c, 0xb3, 0x27, 0xa6, 0x69,
0xbe, 0xe1, 0x42, 0xe7, 0xda, 0xc4, 0x65, 0x25, 0xb5, 0xc4, 0xa7, 0xf6, 0x79, 0xdc, 0xff, 0xba,
0x44, 0x5c, 0xec, 0x77, 0xca, 0x95, 0x27, 0xff, 0x7c, 0x18, 0xcc, 0x9f, 0x98, 0xbe, 0x3e, 0xa8,
0xf1, 0x6b, 0xe8, 0x66, 0x85, 0xcc, 0xb6, 0xa1, 0x17, 0x79, 0xd3, 0x0e, 0x71, 0x09, 0x7e, 0x03,
0xa7, 0x5c, 0x28, 0x2a, 0xd8, 0x8e, 0x87, 0x7e, 0xe4, 0x4d, 0xfb, 0xe4, 0x84, 0x0b, 0x95, 0xb2,
0x1d, 0xc7, 0x57, 0xd0, 0xcb, 0x77, 0xec, 0x37, 0x57, 0x61, 0x10, 0x05, 0x53, 0x34, 0x9b, 0xc4,
0xf5, 0xa6, 0xb8, 0x0d, 0x8e, 0xaf, 0xad, 0x68, 0x21, 0x74, 0x65, 0xc8, 0x61, 0x02, 0xbf, 0x85,
0xc1, 0x26, 0x57, 0x65, 0xc1, 0x8c, 0x43, 0x77, 0x2c, 0x1a, 0x1d, 0x6a, 0x16, 0x1f, 0x01, 0xda,
0x70, 0x95, 0x55, 0x79, 0xa9, 0x73, 0x29, 0xc2, 0xee, 0x41, 0xd1, 0x94, 0xac, 0x63, 0x59, 0xc8,
0x2a, 0xec, 0xd9, 0x9e, 0x4b, 0x2e, 0x09, 0xa0, 0xd6, 0x46, 0x3c, 0x82, 0x60, 0xcb, 0x8d, 0xfd,
0x54, 0x9f, 0x3c, 0x87, 0xf8, 0x3d, 0x74, 0xff, 0xb0, 0x62, 0xef, 0xfe, 0x83, 0x66, 0x17, 0x8d,
0xed, 0xda, 0xb2, 0x9d, 0x27, 0x4e, 0x75, 0xe5, 0x7f, 0xf2, 0x26, 0x7f, 0x7d, 0x38, 0x3b, 0x6a,
0xe2, 0x10, 0x4e, 0x4a, 0x66, 0x0a, 0xc9, 0x36, 0x16, 0x3d, 0x20, 0x75, 0x8a, 0xe7, 0x80, 0x94,
0xdc, 0x57, 0x19, 0xa7, 0xda, 0x94, 0x6e, 0xc9, 0xb0, 0x7d, 0x9b, 0x23, 0x4e, 0xbc, 0xb2, 0xd2,
0xb5, 0x29, 0x39, 0x01, 0xf5, 0x12, 0xe3, 0x19, 0x80, 0xbd, 0x94, 0x63, 0x04, 0x96, 0x31, 0x6e,
0x31, 0x9e, 0x7b, 0x76, 0xa8, 0x9f, 0xd7, 0x21, 0x7e, 0x07, 0xe7, 0x5c, 0x64, 0x95, 0xb1, 0xc7,
0xa1, 0x5b, 0x6e, 0x54, 0xd8, 0x89, 0x82, 0xe9, 0x80, 0x0c, 0x9b, 0xf2, 0x0d, 0x37, 0x6a, 0xf2,
0x0d, 0xa0, 0x59, 0x8b, 0x2f, 0x60, 0x7c, 0x97, 0xde, 0xa4, 0xcb, 0xfb, 0x94, 0xae, 0x96, 0x77,
0x64, 0xbe, 0xa0, 0xeb, 0x87, 0xdb, 0xc5, 0xe8, 0x15, 0x3e, 0x07, 0x44, 0x92, 0x7b, 0x7a, 0x9b,
0x3c, 0x7c, 0x5f, 0x26, 0x5f, 0x47, 0x1e, 0x1e, 0x02, 0x2c, 0xd2, 0x15, 0x4d, 0x7e, 0x24, 0xeb,
0x84, 0x8c, 0xfc, 0x2f, 0x67, 0x3f, 0x51, 0xfc, 0xe1, 0x73, 0x6d, 0xea, 0xb1, 0x67, 0xa3, 0x8f,
0xff, 0x03, 0x00, 0x00, 0xff, 0xff, 0x8e, 0x12, 0x2e, 0x02, 0x86, 0x02, 0x00, 0x00,
// 425 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x90, 0xc1, 0x6e, 0xd3, 0x40,
0x10, 0x86, 0xb1, 0x9d, 0xa4, 0xc9, 0x6c, 0x9a, 0x46, 0x1b, 0xa4, 0x9a, 0x8a, 0x83, 0xc9, 0x85,
0x5c, 0x30, 0x52, 0xb8, 0xa0, 0x72, 0x32, 0x21, 0x48, 0x55, 0x91, 0x53, 0x6d, 0x52, 0xaa, 0x72,
0xb1, 0xb6, 0xf6, 0x42, 0xad, 0x38, 0xb6, 0xe5, 0xdd, 0x20, 0xed, 0x2b, 0x71, 0xe3, 0x0d, 0x51,
0x67, 0x6d, 0xec, 0x9e, 0x76, 0xe6, 0x9f, 0x99, 0x6f, 0xfe, 0x1d, 0x98, 0xc5, 0x8f, 0x5c, 0x45,
0x69, 0x22, 0x72, 0x95, 0x2a, 0xed, 0x97, 0x55, 0xa1, 0x0a, 0x3a, 0xc4, 0xe7, 0xe1, 0xf8, 0xf3,
0x82, 0x88, 0xfc, 0x78, 0x90, 0x46, 0x9e, 0xff, 0xb1, 0x61, 0xbc, 0x7a, 0xe4, 0xea, 0xaa, 0xee,
0xa6, 0x2f, 0xa1, 0x1f, 0x67, 0x45, 0xbc, 0x77, 0x2d, 0xcf, 0x5a, 0xf4, 0x98, 0x49, 0xe8, 0x2b,
0x18, 0x8a, 0x5c, 0x46, 0x39, 0x3f, 0x08, 0xd7, 0xf6, 0xac, 0xc5, 0x88, 0x9d, 0x88, 0x5c, 0x86,
0xfc, 0x20, 0xe8, 0x25, 0x0c, 0xd2, 0x03, 0xff, 0x25, 0xa4, 0xeb, 0x78, 0xce, 0x82, 0x2c, 0xe7,
0x7e, 0xb3, 0xc9, 0xef, 0x82, 0xfd, 0x2b, 0x6c, 0x5a, 0xe7, 0xaa, 0xd2, 0xac, 0x9e, 0xa0, 0x6f,
0x60, 0x9c, 0xa4, 0xb2, 0xcc, 0xb8, 0x36, 0xe8, 0x1e, 0xa2, 0x49, 0xad, 0x21, 0xde, 0x03, 0x92,
0x08, 0x19, 0x57, 0x69, 0xa9, 0xd2, 0x22, 0x77, 0xfb, 0x75, 0x47, 0x2b, 0xa1, 0xe3, 0x22, 0x2b,
0x2a, 0x77, 0x80, 0x35, 0x93, 0x5c, 0x30, 0x20, 0x9d, 0x8d, 0x74, 0x0a, 0xce, 0x5e, 0x68, 0xfc,
0xd4, 0x88, 0x3d, 0x85, 0xf4, 0x1d, 0xf4, 0x7f, 0xf3, 0xec, 0x68, 0xfe, 0x43, 0x96, 0xe7, 0xad,
0xed, 0xc6, 0x32, 0xce, 0x33, 0xd3, 0x75, 0x69, 0x7f, 0xb4, 0xe6, 0x7f, 0x6d, 0x38, 0x7d, 0x56,
0xa4, 0x2e, 0x9c, 0x94, 0x5c, 0x67, 0x05, 0x4f, 0x10, 0x3d, 0x66, 0x4d, 0x4a, 0x57, 0x40, 0x64,
0x71, 0xac, 0x62, 0x11, 0x29, 0x5d, 0x9a, 0x25, 0x93, 0xee, 0x6d, 0x9e, 0x71, 0xfc, 0x2d, 0xb6,
0xee, 0x74, 0x29, 0x18, 0xc8, 0xff, 0x31, 0x5d, 0x02, 0xe0, 0xa5, 0x0c, 0xc3, 0x41, 0xc6, 0xac,
0xc3, 0x78, 0xaa, 0xe1, 0xd0, 0x28, 0x6d, 0x42, 0xfa, 0x16, 0xce, 0x44, 0x1e, 0x57, 0x1a, 0x8f,
0x13, 0xed, 0x85, 0x96, 0x6e, 0xcf, 0x73, 0x16, 0x63, 0x36, 0x69, 0xe5, 0x6b, 0xa1, 0x25, 0x7d,
0x0d, 0xa3, 0x5a, 0x11, 0x09, 0xde, 0x75, 0xc8, 0x5a, 0x61, 0xfe, 0x15, 0xa0, 0x35, 0x45, 0xcf,
0x61, 0x76, 0x1b, 0x5e, 0x87, 0x9b, 0xbb, 0x30, 0xda, 0x6e, 0x6e, 0xd9, 0x6a, 0x1d, 0xed, 0xee,
0x6f, 0xd6, 0xd3, 0x17, 0xf4, 0x0c, 0x08, 0x0b, 0xee, 0xa2, 0x9b, 0xe0, 0xfe, 0xdb, 0x26, 0xf8,
0x32, 0xb5, 0xe8, 0x04, 0x60, 0x1d, 0x6e, 0xa3, 0xe0, 0x7b, 0xb0, 0x0b, 0xd8, 0xd4, 0xfe, 0x7c,
0xfa, 0x83, 0xf8, 0xef, 0x3f, 0x35, 0x96, 0x1f, 0x06, 0x18, 0x7d, 0xf8, 0x17, 0x00, 0x00, 0xff,
0xff, 0xc9, 0xd1, 0xd6, 0xde, 0xa4, 0x02, 0x00, 0x00,
}

View File

@ -41,6 +41,9 @@ message IdentityImage {
// encryption_keys is a list of encrypted keys that can be used to decrypted an encrypted payload
repeated bytes encryption_keys = 4;
// encrypted signals the encryption state of the payload, default is false.
bool encrypted = 5;
// SourceType are the predefined types of image source allowed
enum SourceType {
UNKNOWN_SOURCE_TYPE = 0;