Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
45 / 45
100.00% covered (success)
100.00%
1 / 1
CRAP
100.00% covered (success)
100.00%
1 / 1
FormatV2
100.00% covered (success)
100.00%
45 / 45
100.00% covered (success)
100.00%
1 / 1
27
100.00% covered (success)
100.00%
1 / 1
 isValid
100.00% covered (success)
100.00%
45 / 45
100.00% covered (success)
100.00%
1 / 1
27
1<?php
2/**
3 * PrivateBin
4 *
5 * a zero-knowledge paste bin
6 *
7 * @link      https://github.com/PrivateBin/PrivateBin
8 * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
9 * @license   https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
10 * @version   1.7.0
11 */
12
13namespace PrivateBin;
14
15/**
16 * FormatV2
17 *
18 * Provides validation function for version 2 format of pastes & comments.
19 */
20class FormatV2
21{
22    /**
23     * version 2 format validator
24     *
25     * Checks if the given array is a proper version 2 formatted, encrypted message.
26     *
27     * @access public
28     * @static
29     * @param  array $message
30     * @param  bool  $isComment
31     * @return bool
32     */
33    public static function isValid($message, $isComment = false)
34    {
35        $required_keys = array('adata', 'v', 'ct');
36        if ($isComment) {
37            $required_keys[] = 'pasteid';
38            $required_keys[] = 'parentid';
39        } else {
40            $required_keys[] = 'meta';
41        }
42
43        // Make sure no additionnal keys were added.
44        if (count(array_keys($message)) != count($required_keys)) {
45            return false;
46        }
47
48        // Make sure required fields are present.
49        foreach ($required_keys as $k) {
50            if (!array_key_exists($k, $message)) {
51                return false;
52            }
53        }
54
55        // Make sure adata is an array.
56        if (!is_array($message['adata'])) {
57            return false;
58        }
59
60        $cipherParams = $isComment ? $message['adata'] : $message['adata'][0];
61
62        // Make sure some fields are base64 data:
63        // - initialization vector
64        if (!base64_decode($cipherParams[0], true)) {
65            return false;
66        }
67        // - salt
68        if (!base64_decode($cipherParams[1], true)) {
69            return false;
70        }
71        // - cipher text
72        if (!($ct = base64_decode($message['ct'], true))) {
73            return false;
74        }
75
76        // Make sure some fields have a reasonable size:
77        // - initialization vector
78        if (strlen($cipherParams[0]) > 24) {
79            return false;
80        }
81        // - salt
82        if (strlen($cipherParams[1]) > 14) {
83            return false;
84        }
85
86        // Make sure some fields contain no unsupported values:
87        // - version
88        if (!(is_int($message['v']) || is_float($message['v'])) || (float) $message['v'] < 2) {
89            return false;
90        }
91        // - iterations, refuse less then 10000 iterations (minimum NIST recommendation)
92        if (!is_int($cipherParams[2]) || $cipherParams[2] <= 10000) {
93            return false;
94        }
95        // - key size
96        if (!in_array($cipherParams[3], array(128, 192, 256), true)) {
97            return false;
98        }
99        // - tag size
100        if (!in_array($cipherParams[4], array(64, 96, 128), true)) {
101            return false;
102        }
103        // - algorithm, must be AES
104        if ($cipherParams[5] !== 'aes') {
105            return false;
106        }
107        // - mode
108        if (!in_array($cipherParams[6], array('ctr', 'cbc', 'gcm'), true)) {
109            return false;
110        }
111        // - compression
112        if (!in_array($cipherParams[7], array('zlib', 'none'), true)) {
113            return false;
114        }
115
116        // Reject data if entropy is too low
117        if (strlen($ct) > strlen(gzdeflate($ct))) {
118            return false;
119        }
120
121        // require only the key 'expire' in the metadata of pastes
122        if (!$isComment && (
123            count($message['meta']) === 0 ||
124            !array_key_exists('expire', $message['meta']) ||
125            count($message['meta']) > 1
126        )) {
127            return false;
128        }
129
130        return true;
131    }
132}