| 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
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 |