Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.73% covered (success)
97.73%
43 / 44
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
TsaPolicyValue
97.73% covered (success)
97.73%
43 / 44
66.67% covered (warning)
66.67%
2 / 3
21
0.00% covered (danger)
0.00%
0 / 1
 defaults
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 decode
97.22% covered (success)
97.22%
35 / 36
0.00% covered (danger)
0.00%
0 / 1
19
 encode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4/**
5 * SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
6 * SPDX-License-Identifier: AGPL-3.0-or-later
7 */
8
9namespace OCA\Libresign\Service\Policy\Provider\Tsa;
10
11final class TsaPolicyValue {
12    public const string DEFAULT_HASH_ALGORITHM = 'SHA256';
13    /**
14     * Algorithms offered for the timestamp query. SHA1 is left out on purpose:
15     * authorities reject it, which is what issue #8145 reports.
16     *
17     * @var string[]
18     */
19    public const array HASH_ALGORITHMS = ['SHA256', 'SHA384', 'SHA512'];
20
21    /** @return array{url: string, policy_oid: string, auth_type: string, username: string, hash_algorithm: string} */
22    public static function defaults(): array {
23        return [
24            'url' => '',
25            'policy_oid' => '',
26            'auth_type' => 'none',
27            'username' => '',
28            'hash_algorithm' => self::DEFAULT_HASH_ALGORITHM,
29        ];
30    }
31
32    /** @return array{url: string, policy_oid: string, auth_type: string, username: string, hash_algorithm: string} */
33    public static function decode(mixed $rawValue): array {
34        if (is_string($rawValue)) {
35            $decoded = json_decode($rawValue, true);
36            if (is_array($decoded)) {
37                $rawValue = $decoded;
38            }
39        }
40
41        if (!is_array($rawValue)) {
42            return self::defaults();
43        }
44
45        $url = isset($rawValue['url']) && is_string($rawValue['url'])
46            ? trim($rawValue['url'])
47            : '';
48
49        $policyOid = isset($rawValue['policy_oid']) && is_string($rawValue['policy_oid'])
50            ? trim($rawValue['policy_oid'])
51            : '';
52
53        if ($policyOid !== '' && !preg_match('/^[0-9]+(\.[0-9]+)*$/', $policyOid)) {
54            $policyOid = '';
55        }
56
57        $authType = isset($rawValue['auth_type']) && is_string($rawValue['auth_type'])
58            ? trim($rawValue['auth_type'])
59            : 'none';
60        if (!in_array($authType, ['none', 'basic'], true)) {
61            $authType = 'none';
62        }
63
64        $username = isset($rawValue['username']) && is_string($rawValue['username'])
65            ? trim($rawValue['username'])
66            : '';
67
68        if ($authType !== 'basic') {
69            $username = '';
70        }
71
72        $hashAlgorithm = isset($rawValue['hash_algorithm']) && is_string($rawValue['hash_algorithm'])
73            ? strtoupper(trim($rawValue['hash_algorithm']))
74            : self::DEFAULT_HASH_ALGORITHM;
75        if (!in_array($hashAlgorithm, self::HASH_ALGORITHMS, true)) {
76            $hashAlgorithm = self::DEFAULT_HASH_ALGORITHM;
77        }
78
79        return [
80            'url' => $url,
81            'policy_oid' => $policyOid,
82            'auth_type' => $authType,
83            'username' => $username,
84            'hash_algorithm' => $hashAlgorithm,
85        ];
86    }
87
88    public static function encode(mixed $rawValue): string {
89        return json_encode(self::decode($rawValue), JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
90    }
91}