summary refs log tree commit diff
path: root/crypto/src/crypto/generators/Poly1305KeyGenerator.cs
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/crypto/generators/Poly1305KeyGenerator.cs')
-rw-r--r--crypto/src/crypto/generators/Poly1305KeyGenerator.cs123
1 files changed, 123 insertions, 0 deletions
diff --git a/crypto/src/crypto/generators/Poly1305KeyGenerator.cs b/crypto/src/crypto/generators/Poly1305KeyGenerator.cs
new file mode 100644

index 000000000..5deb50f07 --- /dev/null +++ b/crypto/src/crypto/generators/Poly1305KeyGenerator.cs
@@ -0,0 +1,123 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /// <summary> + /// Generates keys for the Poly1305 MAC. + /// </summary> + /// <remarks> + /// 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. + /// </remarks> + /// <seealso cref="Poly1305"/> + public class Poly1305KeyGenerator + : CipherKeyGenerator + { + private const byte R_MASK_LOW_2 = (byte)0xFC; + private const byte R_MASK_HIGH_4 = (byte)0x0F; + + /// <summary> + /// Initialises the key generator. + /// </summary> + /// <remarks> + /// Poly1305 keys are always 256 bits, so the key length in the provided parameters is ignored. + /// </remarks> + protected override void engineInit(KeyGenerationParameters param) + { + // Poly1305 keys are always 256 bits + this.random = param.Random; + this.strength = 32; + } + + /// <summary> + /// 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 <see cref="Clamp(byte[])"/>. + /// </summary> + protected override byte[] engineGenerateKey() + { + byte[] key = base.engineGenerateKey(); + Clamp(key); + return key; + } + + /// <summary> + /// 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> + /// </summary> + /// <param name="key">a 32 byte key value <code>k[0] ... k[15], r[0] ... r[15]</code></param> + 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 ArgumentException("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; + } + + /// <summary> + /// 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 <see cref="Clamp(byte[])"/>. + /// </summary> + /// <param name="key">Key.</param> + /// <exception cref="System.ArgumentException">if the key is of the wrong length, or has invalid bits set + /// in the <code>r</code> portion of the key.</exception> + public static void CheckKey(byte[] key) + { + if (key.Length != 32) + { + throw new ArgumentException("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 ArgumentException("Invalid format for r portion of Poly1305 key."); + } + } + + } +} \ No newline at end of file