Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
VerpAddressGenerator
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
2 / 2
2
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 generateVERP
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace MediaWiki\Extension\BounceHandler;
4
5use MediaWiki\WikiMap\WikiMap;
6
7/**
8 * Class VerpAddressGenerator
9 *
10 * Generates a VERP return path address of the form
11 * wikiId-base36( $UserID )-base36( $Timestamp )-hash( $algorithm, $key, $prefix )@$email_domain
12 * for every recipient address
13 *
14 * @file
15 * @ingroup Extensions
16 * @author Tony Thomas, Kunal Mehta, Jeff Green
17 * @license GPL-2.0-or-later
18 */
19class VerpAddressGenerator {
20    /**
21     * @var string
22     */
23    protected $prefix;
24    /**
25     * @var string
26     */
27    protected $algorithm;
28
29    /**
30     * @var string
31     */
32    protected $secretKey;
33
34    /**
35     * @var string
36     */
37    protected $domain;
38
39    /**
40     * @param string $prefix
41     * @param string $algorithm
42     * @param string $secretKey
43     * @param string $domain
44     */
45    public function __construct( $prefix, $algorithm, $secretKey, $domain ) {
46        $this->prefix = $prefix;
47        $this->algorithm = $algorithm;
48        $this->secretKey = $secretKey;
49        $this->domain = $domain;
50    }
51
52    /**
53     * Generate VERP address
54     * The generated hash is cut down to 12 ( 96 bits ) instead of the full 120 bits.
55     * For attacks attempting to recover the hmac key, this makes the attackers job harder by giving
56     * them less information to work from.
57     * This makes brute force attacks easier. An attacker would be able to brute force the signature
58     * by sending an average of 2^95 emails to us. We would (hopefully) notice that.
59     * This would make finding a collision slightly easier if the secret key was known,
60     * but the constraints on each segment (wiki id must be valid, timestamp needs to be within a
61     * certain limit), combined with the difficulty of finding collisions when the key is unknown,
62     * makes this virtually impossible.
63     *
64     * @param int $uid user-id of the failing user
65     * @return string $ReturnPath address
66     */
67    public function generateVERP( $uid ) {
68        // Get the time in Unix timestamp to compare with seconds
69        $timeNow = wfTimestamp();
70        $email_domain = $this->domain;
71        // Creating the VERP address prefix as wikiId-base36( $UserID )-base36( $Timestamp )
72        // and the generated VERP return path is of the form :
73        // wikiId-base36( $UserID )-base36( $Timestamp )-hash( $algorithm, $key, $prefix )@$email_domain
74        // We dont want repeating '-' in our WikiId
75        $wikiId = str_replace( '-', '.', WikiMap::getCurrentWikiId() );
76        $email_prefix = $this->prefix . '-' . $wikiId . '-' . base_convert( (string)$uid, 10, 36 ) .
77            '-' . base_convert( $timeNow, 10, 36 );
78        $verp_hash = base64_encode(
79            substr( hash_hmac( $this->algorithm, $email_prefix, $this->secretKey, true ), 0, 12 )
80        );
81
82        return $email_prefix . '-' . $verp_hash . '@' . $email_domain;
83    }
84}