Completed
Pull Request — master (#42)
by rugk
03:18
created

CryptToolSodium   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 208
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 3

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 21
c 2
b 0
f 0
lcom 0
cbo 3
dl 0
loc 208
rs 10

15 Methods

Rating   Name   Duplication   Size   Complexity  
A makeBox() 0 7 1
A makeSecretBox() 0 4 1
A openBox() 0 6 1
A openSecretBox() 0 4 1
A generateKeyPair() 0 6 1
A createRandom() 0 4 1
A derivePublicKey() 0 4 1
A bin2hex() 0 5 1
A hex2bin() 0 5 1
A stringCompare() 0 10 3
A removeVar() 0 13 3
A isSupported() 0 4 2
A validate() 0 6 2
A getName() 0 3 1
A getDescription() 0 4 1
1
<?php
2
/**
3
 * @author Threema GmbH
4
 * @copyright Copyright (c) 2015-2016 Threema GmbH
5
 */
6
7
8
namespace Threema\MsgApi\Tools;
9
10
use Threema\Core\Exception;
11
use Threema\Core\KeyPair;
12
13
/**
14
 * Contains static methods to do various Threema cryptography related tasks.
15
 * Support libsodium >= 0.2.0 (Namespaces)
16
 *
17
 * @package Threema\Core
18
 */
19
class CryptToolSodium extends CryptTool {
20
	/**
21
	 * @param string $data
22
	 * @param string $nonce
23
	 * @param string $senderPrivateKey
24
	 * @param string $recipientPublicKey
25
	 * @return string encrypted box
26
	 */
27
	protected function makeBox($data, $nonce, $senderPrivateKey, $recipientPublicKey) {
28
		/** @noinspection PhpUndefinedNamespaceInspection @noinspection PhpUndefinedFunctionInspection */
29
		$kp = \Sodium\crypto_box_keypair_from_secretkey_and_publickey($senderPrivateKey, $recipientPublicKey);
30
31
		/** @noinspection PhpUndefinedNamespaceInspection @noinspection PhpUndefinedFunctionInspection */
32
		return \Sodium\crypto_box($data, $nonce, $kp);
33
	}
34
35
	/**
36
	 * make a secret box
37
	 *
38
	 * @param $data
39
	 * @param $nonce
40
	 * @param $key
41
	 * @return mixed
42
	 */
43
	protected function makeSecretBox($data, $nonce, $key) {
44
		/** @noinspection PhpUndefinedNamespaceInspection @noinspection PhpUndefinedFunctionInspection */
45
		return \Sodium\crypto_secretbox($data, $nonce, $key);
46
	}
47
48
49
	/**
50
	 * @param string $box
51
	 * @param string $recipientPrivateKey
52
	 * @param string $senderPublicKey
53
	 * @param string $nonce
54
	 * @return null|string
55
	 */
56
	protected function openBox($box, $recipientPrivateKey, $senderPublicKey, $nonce) {
57
		/** @noinspection PhpUndefinedNamespaceInspection @noinspection PhpUndefinedFunctionInspection */
58
		$kp = \Sodium\crypto_box_keypair_from_secretkey_and_publickey($recipientPrivateKey, $senderPublicKey);
59
		/** @noinspection PhpUndefinedNamespaceInspection @noinspection PhpUndefinedFunctionInspection */
60
		return \Sodium\crypto_box_open($box, $nonce, $kp);
61
	}
62
63
	/**
64
	 * decrypt a secret box
65
	 *
66
	 * @param string $box as binary
67
	 * @param string $nonce as binary
68
	 * @param string $key as binary
69
	 * @return string as binary
70
	 */
71
	protected function openSecretBox($box, $nonce, $key) {
72
		/** @noinspection PhpUndefinedNamespaceInspection @noinspection PhpUndefinedFunctionInspection */
73
		return \Sodium\crypto_secretbox_open($box, $nonce, $key);
74
	}
75
76
	/**
77
	 * Generate a new key pair.
78
	 *
79
	 * @return KeyPair the new key pair
80
	 */
81
	final public function generateKeyPair() {
82
		/** @noinspection PhpUndefinedNamespaceInspection @noinspection PhpUndefinedFunctionInspection */
83
		$kp = \Sodium\crypto_box_keypair();
84
		/** @noinspection PhpUndefinedNamespaceInspection @noinspection PhpUndefinedFunctionInspection */
85
		return new KeyPair(\Sodium\crypto_box_secretkey($kp), \Sodium\crypto_box_publickey($kp));
86
	}
87
88
	/**
89
	 * @param int $size
90
	 * @return string
91
	 */
92
	protected function createRandom($size) {
93
		/** @noinspection PhpUndefinedNamespaceInspection @noinspection PhpUndefinedFunctionInspection */
94
		return \Sodium\randombytes_buf($size);
95
	}
96
97
	/**
98
	 * Derive the public key
99
	 *
100
	 * @param string $privateKey in binary
101
	 * @return string public key as binary
102
	 */
103
	final public function derivePublicKey($privateKey) {
104
		/** @noinspection PhpUndefinedNamespaceInspection @noinspection PhpUndefinedFunctionInspection */
105
		return \Sodium\crypto_box_publickey_from_secretkey($privateKey);
106
	}
107
108
	/**
109
	 * Converts a binary string to an hexdecimal string.
110
	 *
111
	 * This is the same as PHP's bin2hex() implementation, but it is resistant to
112
	 * timing attacks.
113
	 *
114
	 * @link https://paragonie.com/book/pecl-libsodium/read/03-utilities-helpers.md#bin2hex
115
	 * @param  string $binaryString The binary string to convert
116
	 * @return string
117
	 */
118
	public function bin2hex($binaryString)
119
	{
120
		/** @noinspection PhpUndefinedNamespaceInspection @noinspection PhpUndefinedFunctionInspection */
121
		return \Sodium\bin2hex($binaryString);
122
	}
123
124
	/**
125
	 * Converts an hexdecimal string to a binary string.
126
	 *
127
	 * This is the same as PHP's hex2bin() implementation, but it is resistant to
128
	 * timing attacks.
129
	 *
130
	 * @link https://paragonie.com/book/pecl-libsodium/read/03-utilities-helpers.md#hex2bin
131
	 * @param  string $hexString The hex string to convert
132
	 * @param  string|null $ignore	(optional) Characters to ignore
133
	 * @throws \Threema\Core\Exception
134
	 * @return string
135
	 */
136
	public function hex2bin($hexString, $ignore = null)
137
	{
138
		/** @noinspection PhpUndefinedNamespaceInspection @noinspection PhpUndefinedFunctionInspection */
139
		return \Sodium\hex2bin($hexString, $ignore);
140
	}
141
142
143
	/**
144
	 * Compares two strings in a secure way.
145
	 *
146
	 * This is the same as PHP's strcmp() implementation, but it is resistant to
147
	 * timing attacks.
148
	 *
149
	 * @link https://paragonie.com/book/pecl-libsodium/read/03-utilities-helpers.md#compare
150
	 * @param  string $str1 The first string
151
	 * @param  string $str2 The second string
152
	 * @return bool
153
	 */
154
	public function stringCompare($str1, $str2)
155
	{
156
		// check variable type manually
157
		if (!is_string($str1) || !is_string($str2)) {
158
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type of the parent method Threema\MsgApi\Tools\CryptTool::stringCompare of type integer.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
159
		}
160
161
		/** @noinspection PhpUndefinedNamespaceInspection @noinspection PhpUndefinedFunctionInspection */
162
		return \Sodium\memcmp($str1, $str2) === 0;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return \Sodium\memcmp($str1, $str2) === 0; (boolean) is incompatible with the return type of the parent method Threema\MsgApi\Tools\CryptTool::stringCompare of type integer.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
163
	}
164
165
	/**
166
	 * Unsets/removes a variable.
167
	 *
168
	 * Important: When using PHPv7, make sure to have at least version 1.0.1 of
169
	 * the Libsodium PECL (libsodium-php) installed. Otherwise this falls back to
170
	 * the (insecure) PHP method of removing a variable.
171
	 *
172
	 * @link https://paragonie.com/book/pecl-libsodium/read/03-utilities-helpers.md#memzero
173
	 * @param  string $var A variable, passed by reference
174
	 */
175
	public function removeVar(&$var)
176
	{
177
		// check if version is compatible
178
		if (version_compare(PHP_VERSION, '7.0', '>=') &&
179
			version_compare(\Sodium\version_string(), '1.0.1', '>=') # TODO: Fix: Check version of PHP ext. & not of Libsodium itself
180
		) {
181
			// if not, fall back to PHP implementation
182
			return parent::removeVar($var);
183
		}
184
185
		/** @noinspection PhpUndefinedNamespaceInspection @noinspection PhpUndefinedFunctionInspection */
186
		return \Sodium\memzero($var);
187
	}
188
189
	/**
190
	 * Check if implementation supported
191
	 * @return bool
192
	 */
193
	public function isSupported() {
194
		return true === extension_loaded('libsodium')
195
			&& false === method_exists('Sodium', 'sodium_version_string');
196
	}
197
198
	/**
199
	 * Validate crypt tool
200
	 *
201
	 * @return bool
202
	 * @throws Exception
203
	 */
204
	public function validate() {
205
		if(false === $this->isSupported()) {
206
			throw new Exception('Sodium implementation not supported');
207
		}
208
		return true;
209
	}
210
211
	/**
212
	 * @return string
213
	 */
214
	public function getName() {
215
		return 'sodium';
216
	}
217
218
	/**
219
	 * Description of the CryptTool
220
	 * @return string
221
	 */
222
	public function getDescription() {
223
		/** @noinspection PhpUndefinedNamespaceInspection @noinspection PhpUndefinedFunctionInspection */
224
		return 'Sodium implementation '.\Sodium\version_string();
225
	}
226
}
227