AESCBCHS   A
last analyzed

Complexity

Total Complexity 13

Size/Duplication

Total Lines 135
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
dl 0
loc 135
c 0
b 0
f 0
wmc 13
lcom 1
cbo 0
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A encryptContent() 0 15 2
A decryptContent() 0 17 3
A calculateAuthenticationTag() 0 19 2
A isTagValid() 0 4 1
getHashAlgorithm() 0 1 ?
A getIVSize() 0 4 1
A getMode() 0 4 1
A checkKeyLength() 0 6 2
A allowedKeyTypes() 0 4 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2014-2017 Spomky-Labs
9
 *
10
 * This software may be modified and distributed under the terms
11
 * of the MIT license.  See the LICENSE file for details.
12
 */
13
14
namespace Jose\Component\Encryption\Algorithm\ContentEncryption;
15
16
use Jose\Component\Encryption\Algorithm\ContentEncryptionAlgorithmInterface;
17
18
/**
19
 * Class AESCBCHS.
20
 */
21
abstract class AESCBCHS implements ContentEncryptionAlgorithmInterface
22
{
23
    /**
24
     * {@inheritdoc}
25
     */
26
    public function allowedKeyTypes(): array
27
    {
28
        return ['oct'];
29
    }
30
31
    /**
32
     * {@inheritdoc}
33
     */
34
    public function encryptContent(string $data, string $cek, string $iv, ?string $aad, string $encoded_protected_header, ?string &$tag): string
35
    {
36
        $keyLength = mb_strlen($cek, '8bit');
37
        $this->checkKeyLength($keyLength);
38
        $k = mb_substr($cek, $keyLength / 2, null, '8bit');
39
40
        $cyphertext = openssl_encrypt($data, $this->getMode($keyLength), $k, OPENSSL_RAW_DATA, $iv);
41
        if (false === $cyphertext) {
42
            throw new \RuntimeException('Unable to encrypt.');
43
        }
44
45
        $tag = $this->calculateAuthenticationTag($cyphertext, $cek, $iv, $aad, $encoded_protected_header);
46
47
        return $cyphertext;
48
    }
49
50
    /**
51
     * @param string      $data
52
     * @param string      $cek
53
     * @param string      $iv
54
     * @param string      $aad
55
     * @param string      $encoded_protected_header
56
     * @param string|null $aad
57
     * @param string      $tag
58
     *
59
     * @return string
60
     */
61
    public function decryptContent(string $data, string $cek, string $iv, ?string $aad, string $encoded_protected_header, string $tag): string
62
    {
63
        $keyLength = mb_strlen($cek, '8bit');
64
        $this->checkKeyLength($keyLength);
65
66
        if (!$this->isTagValid($data, $cek, $iv, $aad, $encoded_protected_header, $tag)) {
67
            throw new \InvalidArgumentException('Unable to verify the tag.');
68
        }
69
        $k = mb_substr($cek, $keyLength / 2, null, '8bit');
70
71
        $plaintext = openssl_decrypt($data, self::getMode($keyLength), $k, OPENSSL_RAW_DATA, $iv);
72
        if (false === $plaintext) {
73
            throw new \RuntimeException('Unable to decrypt.');
74
        }
75
76
        return $plaintext;
77
    }
78
79
    /**
80
     * @param string      $encrypted_data
81
     * @param string      $cek
82
     * @param string      $iv
83
     * @param null|string $aad
84
     * @param string      $encoded_header
85
     *
86
     * @return string
87
     */
88
    protected function calculateAuthenticationTag(string $encrypted_data, string $cek, string $iv, ?string $aad, string $encoded_header): string
89
    {
90
        $calculated_aad = $encoded_header;
91
        if (null !== $aad) {
92
            $calculated_aad .= '.'.$aad;
93
        }
94
        $mac_key = mb_substr($cek, 0, mb_strlen($cek, '8bit') / 2, '8bit');
95
        $auth_data_length = mb_strlen($encoded_header, '8bit');
96
97
        $secured_input = implode('', [
98
            $calculated_aad,
99
            $iv,
100
            $encrypted_data,
101
            pack('N2', ($auth_data_length / 2147483647) * 8, ($auth_data_length % 2147483647) * 8), // str_pad(dechex($auth_data_length), 4, "0", STR_PAD_LEFT)
102
        ]);
103
        $hash = hash_hmac($this->getHashAlgorithm(), $secured_input, $mac_key, true);
104
105
        return  mb_substr($hash, 0, mb_strlen($hash, '8bit') / 2, '8bit');
106
    }
107
108
    /**
109
     * @param string      $authentication_tag
110
     * @param string      $encoded_header
111
     * @param string      $encrypted_data
112
     * @param string      $cek
113
     * @param string      $iv
114
     * @param string|null $aad
115
     *
116
     * @return bool
117
     */
118
    protected function isTagValid(string $encrypted_data, string $cek, string $iv, ?string $aad, string $encoded_header, string $authentication_tag): bool
119
    {
120
        return $authentication_tag === $this->calculateAuthenticationTag($encrypted_data, $cek, $iv, $aad, $encoded_header);
121
    }
122
123
    /**
124
     * @return string
125
     */
126
    abstract protected function getHashAlgorithm(): string;
127
128
    /**
129
     * @return int
130
     */
131
    public function getIVSize(): int
132
    {
133
        return 128;
134
    }
135
136
    /**
137
     * @param int $keyLength
138
     *
139
     * @return string
140
     */
141
    private function getMode(int $keyLength): string
142
    {
143
        return sprintf('aes-%d-cbc', 8 * $keyLength / 2);
144
    }
145
146
    /**
147
     * @param int $keyLength
148
     */
149
    private function checkKeyLength(int $keyLength)
150
    {
151
        if (!in_array($keyLength, [32, 48, 64])) {
152
            throw new \InvalidArgumentException('Invalid key length. Allowed sizes are 256, 384 and 512 bits.');
153
        }
154
    }
155
}
156