001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2021, Connect2id Ltd and contributors. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.jose.crypto.impl; 019 020 021import com.google.crypto.tink.subtle.XChaCha20Poly1305; 022 023import com.nimbusds.jose.JOSEException; 024import com.nimbusds.jose.util.ByteUtils; 025import com.nimbusds.jose.util.Container; 026 027import net.jcip.annotations.ThreadSafe; 028 029import javax.crypto.*; 030import java.security.*; 031 032 033/** 034 * This class defines the XChaCha20 stream cipher as well as the use of the 035 * Poly1305 authenticator. 036 * 037 * <p>The eXtended-nonce ChaCha cipher construction (XChaCha) allows for 038 * ChaCha-based cipher suites to accept a 192-bit nonce with similar guarantees 039 * to the original construction, except with a much lower probability of nonce 040 * misuse occurring. 041 * 042 * <p>This class is thread-safe. 043 * 044 * @author Alexander Martynov 045 * @version 2021-08-04 046 * @see <a href="https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha-03">XChaCha: 047 * eXtended-nonce ChaCha and AEAD_XChaCha20_Poly1305</a> 048 */ 049@ThreadSafe 050public class XC20P { 051 052 053 /** 054 * The standard authentication tag length (128 bits). 055 */ 056 public static final int AUTH_TAG_BIT_LENGTH = 128; 057 058 059 /** 060 * The standard Initialisation Vector (IV) length (192 bits). 061 */ 062 public static final int IV_BIT_LENGTH = 192; 063 064 065 /** 066 * Encrypts the specified plain text using XChaCha20_Poly1305. 067 * 068 * @param secretKey The AES key. Must not be {@code null}. 069 * @param plainText The plain text. Must not be {@code null}. 070 * @param ivContainer The initialisation vector (IV). 071 * This is output parameter. On output, it carries 072 * the nonce the cipher actually used. 073 * @param authData The authenticated data. Must not be {@code null}. 074 * 075 * @return The authenticated cipher text. 076 * 077 * @throws JOSEException If encryption failed. 078 */ 079 public static AuthenticatedCipherText encryptAuthenticated(final SecretKey secretKey, 080 final Container<byte[]> ivContainer, 081 final byte[] plainText, 082 final byte[] authData) 083 throws JOSEException { 084 085 final XChaCha20Poly1305 aead; 086 087 try { 088 aead = new XChaCha20Poly1305(secretKey.getEncoded()); 089 090 } catch (InvalidKeyException e) { 091 throw new JOSEException("Invalid XChaCha20Poly1305 key: " + e.getMessage(), e); 092 } 093 094 final byte[] cipherOutput; 095 096 try { 097 cipherOutput = aead.encrypt(plainText, authData); 098 099 } catch (GeneralSecurityException e) { 100 throw new JOSEException("Couldn't encrypt with XChaCha20Poly1305: " + e.getMessage(), e); 101 } 102 103 final int tagPos = cipherOutput.length - ByteUtils.byteLength(AUTH_TAG_BIT_LENGTH); 104 final int cipherTextPos = ByteUtils.byteLength(IV_BIT_LENGTH); 105 106 byte[] iv = ByteUtils.subArray(cipherOutput, 0, cipherTextPos); 107 byte[] cipherText = ByteUtils.subArray(cipherOutput, cipherTextPos, tagPos - cipherTextPos); 108 byte[] authTag = ByteUtils.subArray(cipherOutput, tagPos, ByteUtils.byteLength(AUTH_TAG_BIT_LENGTH)); 109 110 // set nonce 111 ivContainer.set(iv); 112 113 return new AuthenticatedCipherText(cipherText, authTag); 114 } 115 116 117 /** 118 * Decrypts the specified cipher text using XChaCha20_Poly1305. 119 * 120 * @param secretKey The AES key. Must not be {@code null}. 121 * @param iv The initialisation vector (IV). Must not be 122 * {@code null}. 123 * @param cipherText The cipher text. Must not be {@code null}. 124 * @param authData The authenticated data. Must not be {@code null}. 125 * @param authTag The authentication tag. Must not be {@code null}. 126 * 127 * @return The decrypted plain text. 128 * 129 * @throws JOSEException If decryption failed. 130 */ 131 public static byte[] decryptAuthenticated(final SecretKey secretKey, 132 final byte[] iv, 133 final byte[] cipherText, 134 final byte[] authData, 135 final byte[] authTag) 136 throws JOSEException { 137 138 final XChaCha20Poly1305 aead; 139 140 try { 141 aead = new XChaCha20Poly1305(secretKey.getEncoded()); 142 143 } catch (InvalidKeyException e) { 144 throw new JOSEException("Invalid XChaCha20Poly1305 key: " + e.getMessage(), e); 145 } 146 147 final byte[] cipherInput = ByteUtils.concat(iv, cipherText, authTag); 148 149 try { 150 return aead.decrypt(cipherInput, authData); 151 152 } catch (GeneralSecurityException e) { 153 154 throw new JOSEException("XChaCha20Poly1305 decryption failed: " + e.getMessage(), e); 155 } 156 } 157}