Jwe::encode()   B
last analyzed

Complexity

Conditions 6
Paths 4

Size

Total Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 4
nop 6
dl 0
loc 36
rs 8.7217
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the tmilos/jose-jwt package.
5
 *
6
 * (c) Milos Tomic <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Tmilos\JoseJwt;
13
14
use Tmilos\JoseJwt\Context\Context;
15
use Tmilos\JoseJwt\Error\JoseJwtException;
16
use Tmilos\JoseJwt\Util\StringUtils;
17
use Tmilos\JoseJwt\Util\UrlSafeB64Encoder;
18
19
class Jwe
20
{
21
    private function __construct()
22
    {
23
    }
24
25
    /**
26
     * @param Context $context
27
     * @param         $payload
28
     * @param         $key
29
     * @param         $jweAlgorithm
30
     * @param         $jweEncryption
31
     * @param array   $extraHeaders
32
     *
33
     * @return string
34
     */
35
    public static function encode(Context $context, $payload, $key, $jweAlgorithm, $jweEncryption, array $extraHeaders = [])
36
    {
37
        if (empty($payload) || (is_string($payload) && trim($payload) == '')) {
38
            throw new JoseJwtException('Payload can not be empty');
39
        }
40
        $algorithm = $context->jweAlgorithms()->get($jweAlgorithm);
41
        if (null === $algorithm) {
42
            throw new JoseJwtException(sprintf('Invalid or unsupported algorithm "%s"', $jweAlgorithm));
43
        }
44
        $encryption = $context->jweEncryptions()->get($jweEncryption);
45
        if (null === $encryption) {
46
            throw new JoseJwtException(sprintf('Invalid or unsupported encryption "%s"', $jweEncryption));
47
        }
48
49
        $header = array_merge([
50
            'alg' => $jweAlgorithm,
51
            'enc' => $jweEncryption,
52
            'typ' => 'JWT',
53
        ], $extraHeaders);
54
55
        list($cek, $encryptedCek) = $algorithm->wrapNewKey($encryption->getKeySize(), $key, $header);
56
57
        $payloadString = StringUtils::payload2string($payload, $context->jsonMapper());
58
59
        $headerString = json_encode($header);
60
        $aad = UrlSafeB64Encoder::encode($headerString);
61
        $parts = $encryption->encrypt($aad, $payloadString, $cek);
62
63
        return implode('.', [
64
            UrlSafeB64Encoder::encode($headerString),
65
            UrlSafeB64Encoder::encode($encryptedCek),
66
            UrlSafeB64Encoder::encode($parts[0]),
67
            UrlSafeB64Encoder::encode($parts[1]),
68
            UrlSafeB64Encoder::encode($parts[2]),
69
        ]);
70
    }
71
72
    /**
73
     * @param Context         $context
74
     * @param string          $token
75
     * @param string|resource $key
76
     *
77
     * @return string
78
     */
79
    public static function decode(Context $context, $token, $key)
80
    {
81
        $parts = self::getParts($token);
82
        $decodedParts = self::decodeParts($parts);
83
84
        $headerString = $decodedParts[0];
85
        $encryptedCek = $decodedParts[1];
86
        $iv = $decodedParts[2];
87
        $cipherText = $decodedParts[3];
88
        $authTag = $decodedParts[4];
89
90
        $header = json_decode($headerString, true);
91
        if (null === $header) {
92
            throw new JoseJwtException('Invalid header');
93
        }
94
95
        $algorithm = $context->jweAlgorithms()->get($header['alg']);
96
        $encryption = $context->jweEncryptions()->get($header['enc']);
97
98
        $cek = $algorithm->unwrap($encryptedCek, $key, $encryption->getKeySize(), $header);
99
        $aad = $parts[0];
100
101
        $plainText = $encryption->decrypt($aad, $cek, $iv, $cipherText, $authTag);
102
103
        return $plainText;
104
    }
105
106
    /**
107
     * @param string $token
108
     *
109
     * @return string
110
     */
111
    public static function header($token)
112
    {
113
        $parts = self::getParts($token);
114
        $decodedParts = self::decodeParts($parts);
115
116
        $headerString = $decodedParts[0];
117
118
        $header = json_decode($headerString, true);
119
        if (null === $header) {
120
            throw new JoseJwtException('Invalid header');
121
        }
122
123
        return $header;
124
    }
125
126
    /**
127
     * @param array $parts [header, encryptedCek, iv, cipherText, authTag]
128
     *
129
     * @return array
130
     */
131
    private static function decodeParts(array $parts)
132
    {
133
        if (count($parts) != 5) {
134
            throw new JoseJwtException('Invalid JWE token');
135
        }
136
137
        $decodedParts = [];
138
        foreach ($parts as $part) {
139
            $decodedParts[] = UrlSafeB64Encoder::decode($part);
140
        }
141
142
        return $decodedParts;
143
    }
144
145
    /**
146
     * @param string $token
147
     *
148
     * @return array [header, encryptedCek, iv, cipherText, authTag]
149
     */
150
    private static function getParts($token)
151
    {
152
        if (empty($token) || trim($token) === '') {
153
            throw new JoseJwtException('Incoming token expected to be in compact serialization form, but is empty');
154
        }
155
156
        $parts = explode('.', $token);
157
        if (count($parts) != 5) {
158
            throw new JoseJwtException('Invalid JWE token');
159
        }
160
161
        return $parts;
162
    }
163
}
164