org.openjsse.com.sun.crypto.provider.Poly1305 Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openjsse Show documentation
Show all versions of openjsse Show documentation
OpenJSSE delivers a TLS 1.3 JSSE provider for Java SE 8
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjsse.com.sun.crypto.provider;
import java.nio.ByteBuffer;
import java.security.Key;
import java.security.InvalidKeyException;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
import java.util.Objects;
import sun.security.util.math.*;
import sun.security.util.math.intpoly.*;
/**
* This class represents the Poly1305 function defined in RFC 7539.
*
* This function is used in the implementation of ChaCha20/Poly1305
* AEAD mode.
*/
final class Poly1305 {
private static final int KEY_LENGTH = 32;
private static final int RS_LENGTH = KEY_LENGTH / 2;
private static final int BLOCK_LENGTH = 16;
private static final int TAG_LENGTH = 16;
private static final IntegerFieldModuloP ipl1305 =
new IntegerPolynomial1305();
private byte[] keyBytes;
private final byte[] block = new byte[BLOCK_LENGTH];
private int blockOffset;
private IntegerModuloP r;
private IntegerModuloP s;
private MutableIntegerModuloP a;
private final MutableIntegerModuloP n = ipl1305.get1().mutable();
Poly1305() { }
/**
* Initialize the Poly1305 object
*
* @param newKey the {@code Key} which will be used for the authentication.
* @param params this parameter is unused.
*
* @throws InvalidKeyException if {@code newKey} is {@code null} or is
* not 32 bytes in length.
*/
void engineInit(Key newKey, AlgorithmParameterSpec params)
throws InvalidKeyException {
Objects.requireNonNull(newKey, "Null key provided during init");
keyBytes = newKey.getEncoded();
if (keyBytes == null) {
throw new InvalidKeyException("Key does not support encoding");
} else if (keyBytes.length != KEY_LENGTH) {
throw new InvalidKeyException("Incorrect length for key: " +
keyBytes.length);
}
engineReset();
setRSVals();
}
/**
* Returns the length of the MAC (authentication tag).
*
* @return the length of the auth tag, which is always 16 bytes.
*/
int engineGetMacLength() {
return TAG_LENGTH;
}
/**
* Reset the Poly1305 object, discarding any current operation but
* maintaining the same key.
*/
void engineReset() {
// Clear the block and reset the offset
Arrays.fill(block, (byte)0);
blockOffset = 0;
// Discard any previous accumulator and start at zero
a = ipl1305.get0().mutable();
}
/**
* Update the MAC with bytes from a {@code ByteBuffer}
*
* @param buf the {@code ByteBuffer} containing the data to be consumed.
* Upon return the buffer's position will be equal to its limit.
*/
void engineUpdate(ByteBuffer buf) {
int remaining = buf.remaining();
while (remaining > 0) {
int bytesToWrite = Integer.min(remaining,
BLOCK_LENGTH - blockOffset);
if (bytesToWrite >= BLOCK_LENGTH) {
// If bytes to write == BLOCK_LENGTH, then we have no
// left-over data from previous updates and we can create
// the IntegerModuloP directly from the input buffer.
processBlock(buf, bytesToWrite);
} else {
// We have some left-over data from previous updates, so
// copy that into the holding block until we get a full block.
buf.get(block, blockOffset, bytesToWrite);
blockOffset += bytesToWrite;
if (blockOffset >= BLOCK_LENGTH) {
processBlock(block, 0, BLOCK_LENGTH);
blockOffset = 0;
}
}
remaining -= bytesToWrite;
}
}
/**
* Update the MAC with bytes from an array.
*
* @param input the input bytes.
* @param offset the starting index from which to update the MAC.
* @param len the number of bytes to process.
*/
void engineUpdate(byte[] input, int offset, int len) {
checkFromIndexSize(offset, len, input.length);
if (blockOffset > 0) {
// We have some left-over data from previous updates
int blockSpaceLeft = BLOCK_LENGTH - blockOffset;
if (len < blockSpaceLeft) {
System.arraycopy(input, offset, block, blockOffset, len);
blockOffset += len;
return; // block wasn't filled
} else {
System.arraycopy(input, offset, block, blockOffset,
blockSpaceLeft);
offset += blockSpaceLeft;
len -= blockSpaceLeft;
processBlock(block, 0, BLOCK_LENGTH);
blockOffset = 0;
}
}
while (len >= BLOCK_LENGTH) {
processBlock(input, offset, BLOCK_LENGTH);
offset += BLOCK_LENGTH;
len -= BLOCK_LENGTH;
}
if (len > 0) { // and len < BLOCK_LENGTH
System.arraycopy(input, offset, block, 0, len);
blockOffset = len;
}
}
/**
* Update the MAC with a single byte of input
*
* @param input the byte to update the MAC with.
*/
void engineUpdate(byte input) {
assert (blockOffset < BLOCK_LENGTH);
// we can't hold fully filled unprocessed block
block[blockOffset++] = input;
if (blockOffset == BLOCK_LENGTH) {
processBlock(block, 0, BLOCK_LENGTH);
blockOffset = 0;
}
}
/**
* Finish the authentication operation and reset the MAC for a new
* authentication operation.
*
* @return the authentication tag as a byte array.
*/
byte[] engineDoFinal() {
byte[] tag = new byte[BLOCK_LENGTH];
// Finish up: process any remaining data < BLOCK_SIZE, then
// create the tag from the resulting little-endian integer.
if (blockOffset > 0) {
processBlock(block, 0, blockOffset);
blockOffset = 0;
}
// Add in the s-half of the key to the accumulator
a.addModPowerTwo(s, tag);
// Reset for the next auth
engineReset();
return tag;
}
/**
* Process a single block of data. This should only be called
* when the block array is complete. That may not necessarily
* be a full 16 bytes if the last block has less than 16 bytes.
*/
private void processBlock(ByteBuffer buf, int len) {
n.setValue(buf, len, (byte)0x01);
a.setSum(n); // a += (n | 0x01)
a.setProduct(r); // a = (a * r) % p
}
private void processBlock(byte[] block, int offset, int length) {
checkFromIndexSize(offset, length, block.length);
n.setValue(block, offset, length, (byte)0x01);
a.setSum(n); // a += (n | 0x01)
a.setProduct(r); // a = (a * r) % p
}
/**
* Partition the authentication key into the R and S components, clamp
* the R value, and instantiate IntegerModuloP objects to R and S's
* numeric values.
*/
private void setRSVals() {
// Clamp the bytes in the "r" half of the key.
keyBytes[3] &= 15;
keyBytes[7] &= 15;
keyBytes[11] &= 15;
keyBytes[15] &= 15;
keyBytes[4] &= 252;
keyBytes[8] &= 252;
keyBytes[12] &= 252;
// Create IntegerModuloP elements from the r and s values
r = ipl1305.getElement(keyBytes, 0, RS_LENGTH, (byte)0);
s = ipl1305.getElement(keyBytes, RS_LENGTH, RS_LENGTH, (byte)0);
}
private int checkFromIndexSize(int fromIndex, int size, int length) throws IndexOutOfBoundsException {
if ((length | fromIndex | size) < 0 || size > length - fromIndex)
throw new IndexOutOfBoundsException();
return fromIndex;
}
}