~bfiedler/go-threema

ref: 97f9d9c50d77f74b7145149768aba2d426f5f9c7 go-threema/client/crypto.go -rw-r--r-- 2.5 KiB
97f9d9c5 — Ben Fiedler Return raw bytes when hashing phone numbers 1 year, 8 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package client

import (
	"crypto/rand"
	"math/big"
	"strings"

	"golang.org/x/crypto/curve25519"
	"golang.org/x/crypto/nacl/box"
)

type MessageType = byte

const (
	TextMessage     MessageType = 0x01 // Content: UTF-8 encoded string
	ImageMessage                = 0x02 // Unsupported, TODO
	FileMessage                 = 0x17 // Unsupported, TODO
	DeliveryReceipt             = 0x80 // Unsupported, TODO
)

// Computes the public key belonging to privateKey.
//
// Implementation taken from /x/crypto/nacl/box.GenerateKey
func computePublicKey(privateKey PrivateKey) PublicKey {
	var publicKey PublicKey
	curve25519.ScalarBaseMult(&publicKey, &privateKey)
	return publicKey
}

// Hashes the given phone number using the defined HMAC function.
func (c *client) hashPhone(phoneNumber string) ([]byte, error) {
	c.phoneMu.Lock()
	defer c.phoneMu.Unlock()

	c.phoneHMAC.Reset()
	_, err := c.phoneHMAC.Write([]byte(phoneNumber))
	if err != nil {
		return nil, err
	}
	return c.phoneHMAC.Sum(nil), nil
}

// Hashes the given e-mail address using the defined HMAC function.
func (c *client) hashEmail(address string) ([]byte, error) {
	address = strings.ToLower(strings.TrimSpace(address))

	c.emailMu.Lock()
	defer c.emailMu.Unlock()

	c.emailHMAC.Reset()
	_, err := c.emailHMAC.Write([]byte(address))
	if err != nil {
		return nil, err
	}
	return c.emailHMAC.Sum(nil), nil
}

// Constructs, encrypts and encodes `msg` as text message. Returns the
// hex-encoded box and nonce
func (c *E2EClient) encryptTextMessage(pubKey PublicKey, msg string) ([]byte, Nonce, error) {
	var nonce Nonce

	rawMsg, err := newTextMessage(msg)
	if err != nil {
		return nil, nonce, err
	}

	_, err = rand.Read(nonce[:])
	if err != nil {
		return nil, nonce, err
	}

	out := box.Seal(nil, rawMsg, &nonce, &pubKey, &c.privateKey)
	return out, nonce, nil
}

// Constructs a new text message with padding already applied
func newTextMessage(msg string) ([]byte, error) {
	out := make([]byte, 1+len(msg))
	copy(out[1:], msg)
	out[0] = TextMessage

	return padMessage(out)
}

// Pads the given message with n random bytes, where 1 <= n <= 255.
func padMessage(msg []byte) ([]byte, error) {
	// rand.Int excludes its upper limit, so it returns a number in the
	// range [0..254]
	n, err := rand.Int(rand.Reader, big.NewInt(255))
	if err != nil {
		return nil, err
	}
	padding := int(n.Int64() + 1)

	out := make([]byte, len(msg)+padding)
	copy(out, msg)

	for i := 0; i < padding; i++ {
		out[len(msg)+i] = byte(padding)
	}

	return out, nil
}