1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.crypto.provider; 27 28 import java.nio.ByteBuffer; 29 import java.security.Key; 30 import java.security.InvalidKeyException; 31 import java.security.spec.AlgorithmParameterSpec; 32 import java.util.Arrays; 33 import java.util.Objects; 34 35 import sun.security.util.math.*; 36 import sun.security.util.math.intpoly.*; 37 38 /** 39 * This class represents the Poly1305 function defined in RFC7539. 40 * 41 * <p>This function is used in the implementation of ChaCha20/Poly1305 42 * AEAD mode. 43 * 44 * @since 10 45 */ 46 public final class Poly1305 { 47 48 private static final int KEY_LENGTH = 32; 49 private static final int RS_LENGTH = KEY_LENGTH / 2; 50 private static final int BLOCK_LENGTH = 16; 51 private static final int TAG_LENGTH = 16; 52 53 private static final IntegerFieldModuloP ipl1305 = 54 new IntegerPolynomial1305(); 55 56 private Key key; 57 private byte[] keyBytes; 58 private final byte[] block = new byte[BLOCK_LENGTH]; 59 private int blockOffset; 60 61 private IntegerModuloP r; 62 private IntegerModuloP s; 63 private MutableIntegerModuloP a; 64 private final MutableIntegerModuloP n = ipl1305.get1().mutable(); 65 66 public Poly1305() { } 67 68 /** 69 * Initialize the Poly1305 object 70 * 71 * @param newKey the {@code Key} which will be used for the authentication. 72 * @param params this parameter is unused. 73 * 74 * @throws InvalidKeyException if {@code newKey} is {@code null} or is 75 * not 32 bytes in length. 76 */ 77 void engineInit(Key newKey, AlgorithmParameterSpec params) 78 throws InvalidKeyException { 79 key = Objects.requireNonNull(newKey, "Null key provided during init"); 80 keyBytes = key.getEncoded(); 81 if (keyBytes == null) { 82 throw new InvalidKeyException("Key does not support encoding"); 83 } else if (keyBytes.length != KEY_LENGTH) { 84 throw new InvalidKeyException("Incorrect length for key: " + 85 keyBytes.length); 86 } 87 88 engineReset(); 89 setRSVals(); 90 } 91 92 /** 93 * Returns the length of the MAC (authentication tag). 94 * 95 * @return the length of the auth tag, which is always 16 bytes. 96 */ 97 int engineGetMacLength() { 98 return TAG_LENGTH; 99 } 100 101 /** 102 * Reset the Poly1305 object, discarding any current operation but 103 * maintaining the same key. 104 */ 105 void engineReset() { 106 // Clear the block and reset the offset 107 Arrays.fill(block, (byte)0); 108 blockOffset = 0; 109 // Discard any previous accumulator and start at zero 110 a = ipl1305.get0().mutable(); 111 } 112 113 /** 114 * Update the MAC with bytes from a {@code ByteBuffer} 115 * 116 * @param buf the {@code ByteBuffer} containing the data to be consumed. 117 * Upon return the buffer's position will be equal to its limit. 118 */ 119 void engineUpdate(ByteBuffer buf) { 120 int remaining = buf.remaining(); 121 while (remaining > 0) { 122 int bytesToWrite = Integer.min(remaining, 123 BLOCK_LENGTH - blockOffset); 124 125 if (bytesToWrite >= BLOCK_LENGTH) { 126 // If bytes to write == BLOCK_LENGTH, then we have no 127 // left-over data from previous updates and we can create 128 // the IntegerModuloP directly from the input buffer. 129 processBlock(buf, bytesToWrite); 130 } else { 131 // We have some left-over data from previous updates, so 132 // copy that into the holding block until we get a full block. 133 buf.get(block, blockOffset, bytesToWrite); 134 blockOffset += bytesToWrite; 135 136 if (blockOffset >= BLOCK_LENGTH) { 137 processBlock(block, 0, BLOCK_LENGTH); 138 blockOffset = 0; 139 } 140 } 141 142 remaining -= bytesToWrite; 143 } 144 } 145 146 /** 147 * Update the MAC with bytes from an array. 148 * 149 * @param input the input bytes. 150 * @param offset the starting index from which to update the MAC. 151 * @param len the number of bytes to process. 152 */ 153 void engineUpdate(byte[] input, int offset, int len) { 154 Objects.checkFromIndexSize(offset, len, input.length); 155 if (blockOffset > 0) { 156 // We have some left-over data from previous updates 157 int blockSpaceLeft = BLOCK_LENGTH - blockOffset; 158 if (len < blockSpaceLeft) { 159 System.arraycopy(input, offset, block, blockOffset, len); 160 blockOffset += len; 161 return; // block wasn't filled 162 } else { 163 System.arraycopy(input, offset, block, blockOffset, 164 blockSpaceLeft); 165 offset += blockSpaceLeft; 166 len -= blockSpaceLeft; 167 processBlock(block, 0, BLOCK_LENGTH); 168 blockOffset = 0; 169 } 170 } 171 while (len >= BLOCK_LENGTH) { 172 processBlock(input, offset, BLOCK_LENGTH); 173 offset += BLOCK_LENGTH; 174 len -= BLOCK_LENGTH; 175 } 176 if (len > 0) { // and len < BLOCK_LENGTH 177 System.arraycopy(input, offset, block, 0, len); 178 blockOffset = len; 179 } 180 } 181 182 /** 183 * Update the MAC with a single byte of input 184 * 185 * @param input the byte to update the MAC with. 186 */ 187 void engineUpdate(byte input) { 188 assert (blockOffset < BLOCK_LENGTH); 189 // we can't hold fully filled unprocessed block 190 block[blockOffset++] = input; 191 192 if (blockOffset == BLOCK_LENGTH) { 193 processBlock(block, 0, BLOCK_LENGTH); 194 blockOffset = 0; 195 } 196 } 197 198 199 /** 200 * Finish the authentication operation and reset the MAC for a new 201 * authentication operation. 202 * 203 * @return the authentication tag as a byte array. 204 */ 205 byte[] engineDoFinal() { 206 byte[] tag = new byte[BLOCK_LENGTH]; 207 208 // Finish up: process any remaining data < BLOCK_SIZE, then 209 // create the tag from the resulting little-endian integer. 210 if (blockOffset > 0) { 211 processBlock(block, 0, blockOffset); 212 blockOffset = 0; 213 } 214 215 // Add in the s-half of the key to the accumulator 216 a.addModPowerTwo(s, tag); 217 218 // Reset for the next auth 219 engineReset(); 220 return tag; 221 } 222 223 /** 224 * Process a single block of data. This should only be called 225 * when the block array is complete. That may not necessarily 226 * be a full 16 bytes if the last block has less than 16 bytes. 227 */ 228 private void processBlock(ByteBuffer buf, int len) { 229 n.setValue(buf, len, (byte)0x01); 230 a.setSum(n); // a += (n | 0x01) 231 a.setProduct(r); // a = (a * r) % p 232 } 233 234 private void processBlock(byte[] block, int offset, int length) { 235 Objects.checkFromIndexSize(offset, length, block.length); 236 n.setValue(block, offset, length, (byte)0x01); 237 a.setSum(n); // a += (n | 0x01) 238 a.setProduct(r); // a = (a * r) % p 239 } 240 241 /** 242 * Partition the authentication key into the R and S components, clamp 243 * the R value, and instantiate IntegerModuloP objects to R and S's 244 * numeric values. 245 */ 246 private void setRSVals() { 247 // Clamp the bytes in the "r" half of the key. 248 keyBytes[3] &= 15; 249 keyBytes[7] &= 15; 250 keyBytes[11] &= 15; 251 keyBytes[15] &= 15; 252 keyBytes[4] &= 252; 253 keyBytes[8] &= 252; 254 keyBytes[12] &= 252; 255 256 // Create IntegerModuloP elements from the r and s values 257 r = ipl1305.getElement(keyBytes, 0, RS_LENGTH, (byte)0); 258 s = ipl1305.getElement(keyBytes, RS_LENGTH, RS_LENGTH, (byte)0); 259 } 260 }