summaryrefslogtreecommitdiffstats
path: root/bcprov/src/main/java/org/bouncycastle/crypto/generators/Poly1305KeyGenerator.java
blob: 59eee39e603f854a98fd01ed785e09c02e5dd757 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package org.bouncycastle.crypto.generators;

import org.bouncycastle.crypto.CipherKeyGenerator;
import org.bouncycastle.crypto.KeyGenerationParameters;
import org.bouncycastle.crypto.macs.Poly1305;

/**
 * Generates keys for the Poly1305 MAC.
 * <p>
 * Poly1305 keys are 256 bit keys consisting of a 128 bit secret key used for the underlying block
 * cipher followed by a 128 bit {@code r} value used for the polynomial portion of the Mac. <br>
 * The {@code r} value has a specific format with some bits required to be cleared, resulting in an
 * effective 106 bit key. <br>
 * A separately generated 256 bit key can be modified to fit the Poly1305 key format by using the
 * {@link #clamp(byte[])} method to clear the required bits.
 *
 * @see Poly1305
 */
public class Poly1305KeyGenerator
    extends CipherKeyGenerator
{
    private static final byte R_MASK_LOW_2 = (byte)0xFC;
    private static final byte R_MASK_HIGH_4 = (byte)0x0F;

    /**
     * Initialises the key generator.<br>
     * Poly1305 keys are always 256 bits, so the key length in the provided parameters is ignored.
     */
    public void init(KeyGenerationParameters param)
    {
        // Poly1305 keys are always 256 bits
        super.init(new KeyGenerationParameters(param.getRandom(), 256));
    }

    /**
     * Generates a 256 bit key in the format required for Poly1305 - e.g.
     * <code>k[0] ... k[15], r[0] ... r[15]</code> with the required bits in <code>r</code> cleared
     * as per {@link #clamp(byte[])}.
     */
    public byte[] generateKey()
    {
        final byte[] key = super.generateKey();
        clamp(key);
        return key;
    }

    /**
     * Modifies an existing 32 byte key value to comply with the requirements of the Poly1305 key by
     * clearing required bits in the <code>r</code> (second 16 bytes) portion of the key.<br>
     * Specifically:
     * <ul>
     * <li>r[3], r[7], r[11], r[15] have top four bits clear (i.e., are {0, 1, . . . , 15})</li>
     * <li>r[4], r[8], r[12] have bottom two bits clear (i.e., are in {0, 4, 8, . . . , 252})</li>
     * </ul>
     *
     * @param key a 32 byte key value <code>k[0] ... k[15], r[0] ... r[15]</code>
     */
    public static void clamp(byte[] key)
    {
        /*
         * Key is k[0] ... k[15], r[0] ... r[15] as per poly1305_aes_clamp in ref impl.
         */
        if (key.length != 32)
        {
            throw new IllegalArgumentException("Poly1305 key must be 256 bits.");
        }

        /*
         * r[3], r[7], r[11], r[15] have top four bits clear (i.e., are {0, 1, . . . , 15})
         */
        key[19] &= R_MASK_HIGH_4;
        key[23] &= R_MASK_HIGH_4;
        key[27] &= R_MASK_HIGH_4;
        key[31] &= R_MASK_HIGH_4;

        /*
         * r[4], r[8], r[12] have bottom two bits clear (i.e., are in {0, 4, 8, . . . , 252}).
         */
        key[20] &= R_MASK_LOW_2;
        key[24] &= R_MASK_LOW_2;
        key[28] &= R_MASK_LOW_2;
    }

    /**
     * Checks a 32 byte key for compliance with the Poly1305 key requirements, e.g.
     * <code>k[0] ... k[15], r[0] ... r[15]</code> with the required bits in <code>r</code> cleared
     * as per {@link #clamp(byte[])}.
     *
     * @throws IllegalArgumentException if the key is of the wrong length, or has invalid bits set
     *             in the <code>r</code> portion of the key.
     */
    public static void checkKey(byte[] key)
    {
        if (key.length != 32)
        {
            throw new IllegalArgumentException("Poly1305 key must be 256 bits.");
        }

        checkMask(key[19], R_MASK_HIGH_4);
        checkMask(key[23], R_MASK_HIGH_4);
        checkMask(key[27], R_MASK_HIGH_4);
        checkMask(key[31], R_MASK_HIGH_4);

        checkMask(key[20], R_MASK_LOW_2);
        checkMask(key[24], R_MASK_LOW_2);
        checkMask(key[28], R_MASK_LOW_2);
    }

    private static void checkMask(byte b, byte mask)
    {
        if ((b & (~mask)) != 0)
        {
            throw new IllegalArgumentException("Invalid format for r portion of Poly1305 key.");
        }
    }

}