aboutsummaryrefslogtreecommitdiffstats
path: root/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsMac.java
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsMac.java')
-rw-r--r--libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsMac.java181
1 files changed, 181 insertions, 0 deletions
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsMac.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsMac.java
new file mode 100644
index 000000000..33eb7f7b3
--- /dev/null
+++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/TlsMac.java
@@ -0,0 +1,181 @@
+package org.spongycastle.crypto.tls;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.digests.LongDigest;
+import org.spongycastle.crypto.macs.HMac;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.util.Arrays;
+
+/**
+ * A generic TLS MAC implementation, acting as an HMAC based on some underlying Digest.
+ */
+public class TlsMac
+{
+ protected TlsContext context;
+ protected byte[] secret;
+ protected Mac mac;
+ protected int digestBlockSize;
+ protected int digestOverhead;
+ protected int macLength;
+
+ /**
+ * Generate a new instance of an TlsMac.
+ *
+ * @param context the TLS client context
+ * @param digest The digest to use.
+ * @param key A byte-array where the key for this MAC is located.
+ * @param keyOff The number of bytes to skip, before the key starts in the buffer.
+ * @param len The length of the key.
+ */
+ public TlsMac(TlsContext context, Digest digest, byte[] key, int keyOff, int keyLen)
+ {
+ this.context = context;
+
+ KeyParameter keyParameter = new KeyParameter(key, keyOff, keyLen);
+
+ this.secret = Arrays.clone(keyParameter.getKey());
+
+ // TODO This should check the actual algorithm, not rely on the engine type
+ if (digest instanceof LongDigest)
+ {
+ this.digestBlockSize = 128;
+ this.digestOverhead = 16;
+ }
+ else
+ {
+ this.digestBlockSize = 64;
+ this.digestOverhead = 8;
+ }
+
+ if (TlsUtils.isSSL(context))
+ {
+ this.mac = new SSL3Mac(digest);
+
+ // TODO This should check the actual algorithm, not assume based on the digest size
+ if (digest.getDigestSize() == 20)
+ {
+ /*
+ * NOTE: When SHA-1 is used with the SSL 3.0 MAC, the secret + input pad is not
+ * digest block-aligned.
+ */
+ this.digestOverhead = 4;
+ }
+ }
+ else
+ {
+ this.mac = new HMac(digest);
+
+ // NOTE: The input pad for HMAC is always a full digest block
+ }
+
+ this.mac.init(keyParameter);
+
+ this.macLength = mac.getMacSize();
+ if (context.getSecurityParameters().truncatedHMac)
+ {
+ this.macLength = Math.min(this.macLength, 10);
+ }
+ }
+
+ /**
+ * @return the MAC write secret
+ */
+ public byte[] getMACSecret()
+ {
+ return this.secret;
+ }
+
+ /**
+ * @return The output length of this MAC.
+ */
+ public int getSize()
+ {
+ return macLength;
+ }
+
+ /**
+ * Calculate the MAC for some given data.
+ *
+ * @param type The message type of the message.
+ * @param message A byte-buffer containing the message.
+ * @param offset The number of bytes to skip, before the message starts.
+ * @param length The length of the message.
+ * @return A new byte-buffer containing the MAC value.
+ */
+ public byte[] calculateMac(long seqNo, short type, byte[] message, int offset, int length)
+ {
+ /*
+ * TODO[draft-josefsson-salsa20-tls-02] 3. Moreover, in order to accommodate MAC algorithms
+ * like UMAC that require a nonce as part of their operation, the document extends the MAC
+ * algorithm as specified in the TLS protocol. The extended MAC includes a nonce as a second
+ * parameter. MAC algorithms that do not require a nonce, such as HMAC, are assumed to
+ * ignore the nonce input value. The MAC in a GenericStreamCipher is then calculated as
+ * follows.
+ */
+
+ ProtocolVersion serverVersion = context.getServerVersion();
+ boolean isSSL = serverVersion.isSSL();
+
+ byte[] macHeader = new byte[isSSL ? 11 : 13];
+ TlsUtils.writeUint64(seqNo, macHeader, 0);
+ TlsUtils.writeUint8(type, macHeader, 8);
+ if (!isSSL)
+ {
+ TlsUtils.writeVersion(serverVersion, macHeader, 9);
+ }
+ TlsUtils.writeUint16(length, macHeader, macHeader.length - 2);
+
+ mac.update(macHeader, 0, macHeader.length);
+ mac.update(message, offset, length);
+
+ byte[] result = new byte[mac.getMacSize()];
+ mac.doFinal(result, 0);
+ return truncate(result);
+ }
+
+ public byte[] calculateMacConstantTime(long seqNo, short type, byte[] message, int offset, int length,
+ int fullLength, byte[] dummyData)
+ {
+ /*
+ * Actual MAC only calculated on 'length' bytes...
+ */
+ byte[] result = calculateMac(seqNo, type, message, offset, length);
+
+ /*
+ * ...but ensure a constant number of complete digest blocks are processed (as many as would
+ * be needed for 'fullLength' bytes of input).
+ */
+ int headerLength = TlsUtils.isSSL(context) ? 11 : 13;
+
+ // How many extra full blocks do we need to calculate?
+ int extra = getDigestBlockCount(headerLength + fullLength) - getDigestBlockCount(headerLength + length);
+
+ while (--extra >= 0)
+ {
+ mac.update(dummyData, 0, digestBlockSize);
+ }
+
+ // One more byte in case the implementation is "lazy" about processing blocks
+ mac.update(dummyData[0]);
+ mac.reset();
+
+ return result;
+ }
+
+ protected int getDigestBlockCount(int inputLength)
+ {
+ // NOTE: This calculation assumes a minimum of 1 pad byte
+ return (inputLength + digestOverhead) / digestBlockSize;
+ }
+
+ protected byte[] truncate(byte[] bs)
+ {
+ if (bs.length <= macLength)
+ {
+ return bs;
+ }
+
+ return Arrays.copyOf(bs, macLength);
+ }
+}