Skip to content
This repository has been archived by the owner on Jul 22, 2024. It is now read-only.

Commit

Permalink
Merge pull request #27 from signal-golang/new_contacts
Browse files Browse the repository at this point in the history
New contacts
  • Loading branch information
nanu-c authored Feb 5, 2021
2 parents b375254 + e77a304 commit 6505a4a
Show file tree
Hide file tree
Showing 25 changed files with 1,536 additions and 321 deletions.
3 changes: 2 additions & 1 deletion attachments.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

signalservice "github.com/signal-golang/textsecure/protobuf"
textsecure "github.com/signal-golang/textsecure/protobuf"
"github.com/signal-golang/textsecure/transport"
)

// getAttachment downloads an encrypted attachment blob from the given URL
Expand Down Expand Up @@ -45,7 +46,7 @@ func putAttachment(url string, body []byte) ([]byte, error) {
req.Header.Add("Content-Type", "application/octet-stream")
req.Header.Add("Content-Length", strconv.Itoa(len(body)))

client := newHTTPClient()
client := transport.NewHTTPClient()
resp, err := client.Do(req)
if err != nil {
return nil, err
Expand Down
4 changes: 4 additions & 0 deletions axolotl/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ type ECPublicKey struct {
key [32]byte
}

func (pk ECPublicKey) GetKey() [32]byte {
return pk.key
}

const djbType = 5

func ensureKeyLength(key []byte) {
Expand Down
5 changes: 5 additions & 0 deletions axolotl/ratchet.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ func calculateAgreement(result, theirPub, ourPriv *[32]byte) {
curve25519.ScalarMult(result, ourPriv, theirPub)
}

// CalculateAgreement
func CalculateAgreement(result, theirPub, ourPriv *[32]byte) {
calculateAgreement(result, theirPub, ourPriv)
}

func initializeSenderSession(ss *sessionState, version byte, parameters aliceAxolotlParameters) error {
ss.setSessionVersion(uint32(version))
ss.setLocalIdentityPublic(&parameters.OurIdentityKey.PublicKey)
Expand Down
2 changes: 1 addition & 1 deletion cmd/textsecure/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ func main() {
// If "to" matches a contact name then get its phone number, otherwise assume "to" is a phone number
for _, c := range contacts {
if strings.EqualFold(c.Name, to) {
to = c.Tel
to = c.UUID
break
}
}
Expand Down
200 changes: 200 additions & 0 deletions contactDiscoveryCrypto/contactDiscoveryCrypto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
package contactDiscoveryCrypto

import (
"bytes"
"encoding/binary"
"errors"
"strconv"

"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"

"github.com/signal-golang/textsecure/contactsDiscovery"
)

type QueryEnvelope struct {
Data []byte `json:"data"`
Iv []byte `json:"iv"`
Mac []byte `json:"mac"`
RequestId []byte `json:"requestId"`
}
type DiscoveryRequest struct {
AddressCount int `json:"addressCount"`
Commitment []byte `json:"commitment"`
Data []byte `json:"data"`
Envelopes map[string]*QueryEnvelope `json:"envelopes"`
Iv []byte `json:"iv"`
Mac []byte `json:"mac"`
}

//copied from nanu_c
type RemoteAttestation struct {
RequestId []byte
Keys RemoteAttestationKeys
Cookies []string
}
type RemoteAttestationKeys struct {
ClientKey []byte
ServerKey []byte
}

func CreateDiscoveryRequest(addressBook []string, remoteAttestations map[string]*contactsDiscovery.RemoteAttestation) (*DiscoveryRequest, error) {
queryDataKey, err := getRandomBytes(32)
if err != nil {
return nil, err
}
queryData, err := buildQueryData(addressBook)
if err != nil {
return nil, err
}
encryptedQueryData, err := aesEncrypt(queryDataKey, nil, queryData)
if err != nil {
return nil, err
}
commitment := hashSha256(queryData)
envelopes := make(map[string]*QueryEnvelope)
for key, attestation := range remoteAttestations {
envelopes[key], err = buildQueryEnvelopeFromAttestation(attestation, queryDataKey)
if err != nil {
return nil, err
}
}

return &DiscoveryRequest{
AddressCount: len(addressBook),
Commitment: commitment,
Data: encryptedQueryData.data,
Envelopes: envelopes,
Iv: encryptedQueryData.iv,
Mac: encryptedQueryData.mac}, nil
}

func getRandomBytes(length int) ([]byte, error) {
random := make([]byte, length)
_, err := rand.Read(random)
if err != nil {
return nil, err
}
return random, nil
}

func buildQueryData(addressBook []string) ([]byte, error) {
nonce, err := getRandomBytes(32)
if err != nil {
return nil, err
}
return buildQueryDataWithNonce(addressBook, nonce)
}

func buildQueryDataWithNonce(addressBook []string, nonce []byte) ([]byte, error) {
var addressData []byte
for _, address := range addressBook {
addressId, err := parse(address)
if err != nil {
return nil, err
}
addressData = append(addressData, toByteArray(addressId)...)
}
return append(nonce, addressData...), nil
}

func parse(s string) (int64, error) {
return strconv.ParseInt(s, 10, 0)
}

func toByteArray(i int64) []byte {
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, uint64(i))
return b
}

func toLong(a []byte) int64 {
return int64(binary.BigEndian.Uint64(a))
}

func buildQueryEnvelopeFromAttestation(attestation *contactsDiscovery.RemoteAttestation, queryDataKey []byte) (*QueryEnvelope, error) {
return buildQueryEnvelope(attestation.RequestId, attestation.Keys.ClientKey, queryDataKey)
}

func buildQueryEnvelope(requestId, clientKey, queryDataKey []byte) (*QueryEnvelope, error) {
result, err := aesEncrypt(clientKey, requestId, queryDataKey)
if err != nil {
return nil, err
}
return &QueryEnvelope{
Data: result.data,
Mac: result.mac,
Iv: result.iv,
RequestId: requestId,
}, nil
}

type AESEncryptedResult struct {
iv,
data,
mac,
aad []byte
}

func aesEncrypt(key, aad, data []byte) (*AESEncryptedResult, error) {
nonce, err := getRandomBytes(12) //iv
if err != nil {
return nil, err
}
return aesEncryptNonce(key, aad, data, nonce)
}

const TAG_LENGTH_BYTES = 16

func aesEncryptNonce(key, aad, data, nonce []byte) (*AESEncryptedResult, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
aesgcm, err := cipher.NewGCMWithTagSize(block, TAG_LENGTH_BYTES)
if err != nil {
return nil, err
}
ciphertext := aesgcm.Seal(nil, nonce, data, aad)
macStart := len(ciphertext) - TAG_LENGTH_BYTES
macPart := ciphertext[macStart:]
dataPart := ciphertext[:macStart]
return &AESEncryptedResult{nonce, dataPart, macPart, aad}, nil
}

func hashSha256(queryData []byte) []byte {
hash := sha256.Sum256(queryData)
return hash[:]
}

func GetDiscoveryResponseData(response DiscoveryResponse, remoteAttestations map[string]*contactsDiscovery.RemoteAttestation) ([]byte, error) {
for _, attestation := range remoteAttestations {
if bytes.Equal(response.RequestId, attestation.RequestId) {
return aesDecrypt(attestation.Keys.ServerKey, response.Iv, response.Data, response.Mac, nil)
}
}

return nil, errors.New("No matching RequestId")
}

type DiscoveryResponse struct {
Data []byte `json:"data"`
Iv []byte `json:"iv"`
Mac []byte `json:"mac"`
RequestId []byte `json:"requestId"`
}

func aesDecrypt(key, nonce, data, mac, aad []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
aesgcm, err := cipher.NewGCMWithTagSize(block, TAG_LENGTH_BYTES)
if err != nil {
return nil, err
}
ciphertext := append(data, mac...)
return aesgcm.Open(nil, nonce, ciphertext, aad)
}
100 changes: 100 additions & 0 deletions contactDiscoveryCrypto/contactDiscoveryCrypto_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package contactDiscoveryCrypto

import (
"encoding/base64"
"testing"

"github.com/stretchr/testify/assert"
)

var (
addressBook = []string{"4467060706060"}
queryDataKey = int8toByteArray(48, -93, 23, 89, 42, 38, 19, 106, 96, -33, 77, 36, -93, 68, 83, -83, -115, -60, -70, -76, -17, -69, 117, 100, -118, 31, 13, -96, -47, 17, -78, -66)
queryData = int8toByteArray(-96, 30, 112, 36, 118, -22, 126, -11, 20, 100, -107, -48, -94, 81, 71, -89, -1, 107, -49, 80, 16, 60, 64, 116, 78, -81, -1, 5, -41, -52, -67, -43, 0, 0, 4, 16, 17, -111, 11, 12)
encryptedQueryData = AESEncryptedResult{
iv: int8toByteArray(-62, -125, 70, 88, 85, 58, -103, -6, -83, -87, -87, 0),
data: int8toByteArray(-43, -52, 106, 13, -24, 64, 41, 110, -33, -5, -34, -69, 56, 44, 36, 82, -23, -100, -117, -100, 0, -38, 67, -128, -81, -53, -73, 104, -16, -12, -122, 101, 6, 116, -27, -106, -96, 37, -23, 6),
mac: int8toByteArray(103, 114, -107, -35, -107, 94, 78, 2, 115, -78, 123, 83, 4, -128, 54, -9),
aad: nil,
}
commitment = int8toByteArray(-103, 76, 43, -1, 94, -2, 51, 96, 24, -61, 18, -113, 65, 69, 50, -125, -86, -65, 83, -88, 106, -20, -106, 116, -126, 48, 122, -43, -29, 43, -30, 91)
entry = "bba07fc7-5d44-40fd-b63c-3909949f45ce"

discoveryRequest = DiscoveryRequest{
AddressCount: 1,
Commitment: fromBase64("mUwr/17+M2AYwxKPQUUyg6q/U6hq7JZ0gjB61eMr4ls="),
Iv: fromBase64("woNGWFU6mfqtqakA"),
Data: fromBase64("1cxqDehAKW7f+967OCwkUumci5wA2kOAr8u3aPD0hmUGdOWWoCXpBg=="),
Mac: fromBase64("Z3KV3ZVeTgJzsntTBIA29w=="),
Envelopes: map[string]*QueryEnvelope{
"bba07fc7-5d44-40fd-b63c-3909949f45ce": &QueryEnvelope{
Data: fromBase64("Wg53y3BtjkuxEmvCEByFUfiwReaszt4Wpa1gH0IpYew="),
Iv: fromBase64("VS+Ygv5XXvmZ5I6K"),
Mac: fromBase64("nl/bgPX1OdbVcBerOmhllA=="),
RequestId: fromBase64("RuP9YiMUDc18omowRZk+ynHNAhdQiYMR/ELT5IWsHt0sPEaQ"),
},
},
}
)

func int8toByteArray(in ...int8) []byte {
out := make([]byte, len(in))
for i := 0; i < len(in); i++ {
out[i] = int8toByte(in[i])
}
return out
}

func int8toByte(i int8) byte {
return byte(i)
}

func Test0Int8toByte(t *testing.T) {
result := int8toByte(-128)
var b byte = 0x80
assert.Equal(t, b, result)
}

func TestMinInt8toByte(t *testing.T) {
result := int8toByte(0)
var b byte
assert.Equal(t, b, result)
}

func Test11Int8toByte(t *testing.T) {
result := int8toByte(11)
var b byte = 0x0b
assert.Equal(t, b, result)
}

func Test12Int8toByte(t *testing.T) {
result := int8toByte(12)
var b byte = 0x0c
assert.Equal(t, b, result)
}

func fromBase64(encoded string) []byte {
decoded, _ := base64.StdEncoding.DecodeString(encoded)
return decoded
}

func TestBuildQueryData(t *testing.T) {
result, err := buildQueryDataWithNonce(addressBook, queryData[:32])
assert.Nil(t, err)
assert.Equal(t, queryData, result)
}

func TestAesEncryptNonce(t *testing.T) {
result, err := aesEncryptNonce(queryDataKey, nil, queryData, encryptedQueryData.iv)
assert.Nil(t, err)
assert.Equal(t, &encryptedQueryData, result)
}

func TestCompareCommitment(t *testing.T) {
assert.Equal(t, commitment, discoveryRequest.Commitment)
}

func TestHashSha256(t *testing.T) {
result := hashSha256(queryData)
assert.Equal(t, commitment, result)
}
9 changes: 7 additions & 2 deletions contacts.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ var (
// ReadContacts reads a YAML contacts file
func loadContacts(contactsYaml *yamlContacts) {
for _, c := range contactsYaml.Contacts {
contacts[c.UUID] = c
if c.UUID != "" && c.UUID != "0" && (c.UUID[0] != 0 || c.UUID[len(c.UUID)-1] != 0) {
contacts[c.UUID] = c

} else {
contacts[c.Tel] = c
}
}
}

Expand Down Expand Up @@ -121,7 +126,7 @@ func updateContact(c *signalservice.ContactDetails) error {
InboxPosition: c.GetInboxPosition(),
Archived: c.GetArchived(),
}
log.Debugln(c.GetAvatar(), buf)
log.Debugln("[textsecure] avatar", c.GetAvatar(), buf)
return WriteContactsToPath()
}

Expand Down
Loading

0 comments on commit 6505a4a

Please sign in to comment.