Completed
Pull Request — master (#18)
by Frederik
01:29
created

DkimSignatureV1::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 21
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 21
c 0
b 0
f 0
ccs 0
cts 20
cp 0
rs 9.3142
cc 2
eloc 16
nc 2
nop 6
crap 6
1
<?php
2
declare(strict_types=1);
3
4
namespace Genkgo\Mail\Header;
5
6
use Genkgo\Mail\Header\Dkim\CanonicalizeBodyInterface;
7
use Genkgo\Mail\Header\Dkim\CanonicalizeHeaderInterface;
8
use Genkgo\Mail\Header\Dkim\SignatureInterface;
9
use Genkgo\Mail\HeaderInterface;
10
use Genkgo\Mail\MessageInterface;
11
12
final class DkimSignatureV1 implements HeaderInterface
13
{
14
    /**
15
     *
16
     */
17
    private CONST HEADER_NAME = 'DKIM-Signature';
18
    /**
19
     * @var MessageInterface
20
     */
21
    private $message;
22
    /**
23
     * @var CanonicalizeBodyInterface
24
     */
25
    private $canonicalizeBody;
26
    /**
27
     * @var CanonicalizeHeaderInterface
28
     */
29
    private $canonicalizeHeader;
30
    /**
31
     * @var SignatureInterface
32
     */
33
    private $signing;
34
    /**
35
     * @var string
36
     */
37
    private $domain;
38
    /**
39
     * @var string
40
     */
41
    private $selector;
42
    /**
43
     * @var array
44
     */
45
    private CONST HEADERS_IGNORED = [
46
        'return-path' => true,
47
        'received' => true,
48
        'comments' => true,
49
        'keywords' => true,
50
        'bcc' => true,
51
        'resent-bcc' => true,
52
    ];
53
54
    /**
55
     * @param MessageInterface $message
56
     * @param CanonicalizeBodyInterface $canonicalizeBody
57
     * @param CanonicalizeHeaderInterface $canonicalizeHeader
58
     * @param SignatureInterface $signing
59
     * @param string $domain
60
     * @param string $selector
61
     */
62
    public function __construct(
63
        MessageInterface $message,
64
        CanonicalizeBodyInterface $canonicalizeBody,
65
        CanonicalizeHeaderInterface $canonicalizeHeader,
66
        SignatureInterface $signing,
67
        string $domain,
68
        string $selector
69
    ) {
70
        if (!$message->hasHeader('from')) {
71
            throw new \InvalidArgumentException(
72
                'In order add a DKIM signature the message MUST include a From header'
73
            );
74
        }
75
76
        $this->message = $message;
77
        $this->canonicalizeBody = $canonicalizeBody;
78
        $this->canonicalizeHeader = $canonicalizeHeader;
79
        $this->signing = $signing;
80
        $this->domain = $domain;
81
        $this->selector = $selector;
82
    }
83
84
    /**
85
     * @return HeaderName
86
     */
87
    public function getName(): HeaderName
88
    {
89
        return new HeaderName(self::HEADER_NAME);
90
    }
91
92
    /**
93
     * @return HeaderValue
94
     */
95
    public function getValue(): HeaderValue
96
    {
97
        $bodyHash = $this->signing->hashBody(
98
            $this->canonicalizeBody->canonicalize((string) $this->message->getBody())
99
        );
100
101
        $headerCanonicalized = [];
102
        $headerNames = [];
103
        foreach ($this->message->getHeaders() as $headers) {
104
            /** @var HeaderInterface $header */
105
            foreach ($headers as $header) {
106
                $headerName = strtolower((string)$header->getName());
107
108
                if (isset(self::HEADERS_IGNORED[$headerName])) {
109
                    break;
110
                }
111
112
                // make sure we sign only the last header in the list
113
                $headerCanonicalized[$headerName] = $this->canonicalizeHeader->canonicalize($header);
114
                $headerNames[$headerName] = (string)$header->getName();
115
            }
116
        }
117
118
        $canonicalizedParameter = [$this->canonicalizeHeader::getName(), $this->canonicalizeBody::getName()];
119
120
        $headerValue = (new HeaderValue('v=1'))
121
            ->withParameter($this->signing->createAlgorithmParameters())
122
            ->withParameter(new HeaderValueParameter('c', implode('/', $canonicalizedParameter)))
123
            ->withParameter(new HeaderValueParameter('q', 'dns/txt'))
124
            ->withParameter(new HeaderValueParameter('s', $this->selector))
125
            ->withParameter(new HeaderValueParameter('d', $this->domain, true))
126
            ->withParameter(new HeaderValueParameter('h', implode(' : ', $headerNames), true))
127
            ->withParameter(new HeaderValueParameter('bh', base64_encode($bodyHash), true))
128
            ->withParameter(new HeaderValueParameter('b', '', true));
129
130
        $headerCanonicalized[strtolower(self::HEADER_NAME)] = $this->canonicalizeHeader->canonicalize(
131
            new GenericHeader(self::HEADER_NAME, (string) $headerValue)
132
        );
133
134
        $signature = base64_encode($this->signing->signHeaders(implode('', $headerCanonicalized)));
135
        return $headerValue->withParameter(new HeaderValueParameter('b', trim($signature), true));
136
    }
137
}