Tag3Packet   A
last analyzed

Complexity

Total Complexity 11

Size/Duplication

Total Lines 171
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
dl 0
loc 171
ccs 49
cts 49
cp 1
rs 10
c 0
b 0
f 0
wmc 11

3 Methods

Rating   Name   Duplication   Size   Complexity  
A generate() 0 12 2
A __construct() 0 4 1
B parse() 0 56 8
1
<?php
2
3
/*
4
 * This file is part of the PHP EcryptFS library.
5
 * (c) 2017 by Dennis Birkholz
6
 * All rights reserved.
7
 * For the license to use this library, see the provided LICENSE file.
8
 */
9
10
namespace Iqb\Ecryptfs;
11
12
/**
13
 * Symmetric-Key Encrypted Session-Key Packet (Tag 3)
14
 *
15
 * @author Dennis Birkholz <[email protected]>
16
 * @link https://tools.ietf.org/html/rfc2440#section-5.3 OpenPGP Message Format: Symmetric-Key Encrypted Session-Key Packets (Tag 3)
17
 * @link https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/tree/fs/ecryptfs/keystore.c?h=v4.11.3#n1360 parse_tag_3_packet
18
 * @link https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/tree/fs/ecryptfs/keystore.c?h=v4.11.3#n2184 write_tag_3_packet
19
 */
20
class Tag3Packet
21
{
22
    /**
23
     * @link https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/tree/fs/ecryptfs/ecryptfs_kernel.h?h=v4.11.3#n140
24
     */
25
    const PACKET_TYPE = 0x8C;
26
27
    /**
28
     * @link https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/tree/fs/ecryptfs/keystore.c?h=v4.11.3#n1455
29
     */
30
    const PACKET_VERSION = 0x04;
31
32
    /**
33
     * @link https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/tree/fs/ecryptfs/keystore.c?h=v4.11.3#n1478
34
     */
35
    const S2L_IDENTIFIER = 0x03;
36
37
    /**
38
     * @link https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/tree/fs/ecryptfs/keystore.c?h=v4.11.3#n1485
39
     */
40
    const HASH_MD5_IDENTIFIER = 0x01;
41
42
    /**
43
     * 65536 iterations
44
     * @link https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/tree/fs/ecryptfs/keystore.c?h=v4.11.3#n2392
45
     */
46
    const HASH_DEFAULT_ITERATIONS = 0x60;
47
48
    /**
49
     * @link https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/tree/include/linux/ecryptfs.h#n32
50
     */
51
    const MAX_ENCRYPTED_KEY_BYTES = 512;
52
53
    /**
54
     * @var int
55
     */
56
    public $packetSize;
57
58
    /**
59
     * @var int
60
     */
61
    public $encryptedKeySize;
62
63
    /**
64
     * @var int
65
     */
66
    public $version = 0x04;
67
68
    /**
69
     * @var int
70
     */
71
    public $cipherCode = 0;
72
73
    /**
74
     * @var int
75
     */
76
    public $stringToKeySpecifier = 0x03;
77
78
    /**
79
     * @var int
80
     */
81
    public $hashIdentifier = 0;
82
83
    /**
84
     * Salt as a hex string
85
     * @var string
86
     */
87
    public $salt;
88
89
    /**
90
     * @var int
91
     */
92
    public $hashIterations = 0;
93
94
    /**
95
     * Encrypted key as binary string
96
     * @var string
97
     */
98
    public $encryptedKey;
99
100
101 38
    public function __construct(string $encryptedKey, int $cipherType = ECRYPTFS_DEFAULT_CIPHER)
0 ignored issues
show
Bug introduced by
The constant Iqb\Ecryptfs\ECRYPTFS_DEFAULT_CIPHER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
102
    {
103 38
        $this->encryptedKey = $encryptedKey;
104 38
        $this->cipherCode = $cipherType;
105 38
    }
106
107
108
    /**
109
     * @link https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/tree/fs/ecryptfs/keystore.c?h=v4.11.3#n2184
110
     */
111 1
    public function generate() : string
112
    {
113
        return
114 1
              \chr(Tag3Packet::PACKET_TYPE)
115 1
            . Util::generateTagPacketLength(\strlen($this->encryptedKey) + ECRYPTFS_SALT_SIZE + 5)
116 1
            . \chr(Tag3Packet::PACKET_VERSION)
117 1
            . \chr($this->cipherCode)
118 1
            . \chr(Tag3Packet::S2L_IDENTIFIER)
119 1
            . \chr(Tag3Packet::HASH_MD5_IDENTIFIER)
120 1
            . ($this->salt ?: \random_bytes(ECRYPTFS_SALT_SIZE))
121 1
            . \chr(Tag3Packet::HASH_DEFAULT_ITERATIONS)
122 1
            . $this->encryptedKey
123
        ;
124
    }
125
126
127
    /**
128
     * Try to parse a Tag3 packet from the supplied data string.
129
     * If the parsing was successfully, $pos will be incremented to point after the parsed data.
130
     *
131
     * Only encryptedKey and cipherCode are used, all other fields are not used.
132
     *
133
     * @link https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/tree/fs/ecryptfs/keystore.c?h=v4.11.3#n1360
134
     */
135 44
    public static function parse(string $data, int &$pos = 0) : self
136
    {
137 44
        $cur = $pos;
138
139 44
        if (\ord($data[$cur]) !== self::PACKET_TYPE) {
140 1
            throw new ParseException("Expected packet type marker 0x" . \dechex(self::PACKET_TYPE) . " but found 0x" . \bin2hex($data[$cur]));
141
        }
142 43
        $cur++;
143
144 43
        $packetSize = Util::parseTagPacketLength($data, $cur);
145 43
        if ($packetSize < ECRYPTFS_SALT_SIZE + 5) {
146 1
            throw new ParseException('Body size too small');
147
        }
148
149 42
        $encryptedKeySize = $packetSize - ECRYPTFS_SALT_SIZE - 5;
150 42
        if ($encryptedKeySize > self::MAX_ENCRYPTED_KEY_BYTES) {
151 1
            throw new ParseException('Expected key size too large');
152
        }
153
154 41
        $version = \ord($data[$cur++]);
155 41
        if ($version !== self::PACKET_VERSION) {
156 1
            throw new ParseException('Invalid version number 0x' . \dechex($version));
157
        }
158
159 40
        $cipherCode = \ord($data[$cur++]);
160 40
        if (!\array_key_exists($cipherCode, CryptoEngineInterface::CIPHER_KEY_SIZES)) {
161 1
            throw new ParseException('Invalid cipher code 0x' . \dechex($cipherCode));
162
        }
163
164 39
        $stringToKeySpecifier = \ord($data[$cur++]);
165 39
        if ($stringToKeySpecifier !== self::S2L_IDENTIFIER) {
166 1
            throw new ParseException('Only S2K ID 3 is currently supported');
167
        }
168
169 38
        $hashIdentifier = \ord($data[$cur++]);
170 38
        if ($hashIdentifier !== self::HASH_MD5_IDENTIFIER) {
171 1
            throw new ParseException('Only MD5 as hashing algorithm supported here');
172
        }
173
174 37
        $salt = \substr($data, $cur, ECRYPTFS_SALT_SIZE);
175 37
        $cur += ECRYPTFS_SALT_SIZE;
176
177
        /* This conversion was taken straight from RFC2440 */
178 37
	    $hashIterations = (16 + (\ord($data[$cur]) & 15)) << ((\ord($data[$cur]) >> 4) + 6);
179 37
        $cur++;
180
181 37
        $encryptedKey = \substr($data, $cur, $encryptedKeySize);
182 37
        $cur += $encryptedKeySize;
183
184 37
        $tag = new self($encryptedKey, $cipherCode);
185 37
        $tag->salt = $salt;
186 37
        $tag->hashIdentifier = $hashIdentifier;
187 37
        $tag->hashIterations = $hashIterations;
188
189 37
        $pos = $cur;
190 37
        return $tag;
191
    }
192
}
193