mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-06 15:20:54 +03:00
Support cipher chacha20-poly1305@openssh.com (#682)
* Added cipher chacha20-poly1305@openssh.com * Small refactoring and remove mutable static buffer Co-authored-by: Jeroen van Erp <jeroen@hierynomus.com>
This commit is contained in:
@@ -64,7 +64,7 @@ In the `examples` directory, there is a separate Maven project that shows how th
|
|||||||
Implementations / adapters for the following algorithms are included:
|
Implementations / adapters for the following algorithms are included:
|
||||||
|
|
||||||
ciphers::
|
ciphers::
|
||||||
`aes{128,192,256}-{cbc,ctr}`, `aes{128,256}-gcm@openssh.com`, `blowfish-{cbc,ctr}`, `3des-{cbc,ctr}`, `twofish{128,192,256}-{cbc,ctr}`, `twofish-cbc`, `serpent{128,192,256}-{cbc,ctr}`, `idea-{cbc,ctr}`, `cast128-{cbc,ctr}`, `arcfour`, `arcfour{128,256}`
|
`aes{128,192,256}-{cbc,ctr}`, `aes{128,256}-gcm@openssh.com`, `blowfish-{cbc,ctr}`, `chacha20-poly1305@openssh.com`, `3des-{cbc,ctr}`, `twofish{128,192,256}-{cbc,ctr}`, `twofish-cbc`, `serpent{128,192,256}-{cbc,ctr}`, `idea-{cbc,ctr}`, `cast128-{cbc,ctr}`, `arcfour`, `arcfour{128,256}`
|
||||||
SSHJ also supports the following extended (non official) ciphers: `camellia{128,192,256}-{cbc,ctr}`, `camellia{128,192,256}-{cbc,ctr}@openssh.org`
|
SSHJ also supports the following extended (non official) ciphers: `camellia{128,192,256}-{cbc,ctr}`, `camellia{128,192,256}-{cbc,ctr}@openssh.org`
|
||||||
|
|
||||||
key exchange::
|
key exchange::
|
||||||
|
|||||||
@@ -133,4 +133,4 @@ macs umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.
|
|||||||
|
|
||||||
TrustedUserCAKeys /etc/ssh/users_rsa_ca.pub
|
TrustedUserCAKeys /etc/ssh/users_rsa_ca.pub
|
||||||
|
|
||||||
Ciphers 3des-cbc,blowfish-cbc,aes128-cbc,aes192-cbc,aes256-cbc,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com
|
Ciphers 3des-cbc,blowfish-cbc,aes128-cbc,aes192-cbc,aes256-cbc,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,chacha20-poly1305@openssh.com
|
||||||
@@ -47,7 +47,8 @@ class CipherSpec extends IntegrationBaseSpec {
|
|||||||
BlockCiphers.AES256CBC(),
|
BlockCiphers.AES256CBC(),
|
||||||
BlockCiphers.AES256CTR(),
|
BlockCiphers.AES256CTR(),
|
||||||
GcmCiphers.AES128GCM(),
|
GcmCiphers.AES128GCM(),
|
||||||
GcmCiphers.AES256GCM()]
|
GcmCiphers.AES256GCM(),
|
||||||
|
ChachaPolyCiphers.CHACHA_POLY_OPENSSH()]
|
||||||
cipher = cipherFactory.name
|
cipher = cipherFactory.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.hierynomus.sshj.transport.cipher;
|
||||||
|
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
|
||||||
|
import java.security.spec.AlgorithmParameterSpec;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.common.SSHRuntimeException;
|
||||||
|
import net.schmizz.sshj.common.SecurityUtils;
|
||||||
|
import net.schmizz.sshj.transport.cipher.BaseCipher;
|
||||||
|
|
||||||
|
public class ChachaPolyCipher extends BaseCipher {
|
||||||
|
|
||||||
|
private static final int CHACHA_KEY_SIZE = 32;
|
||||||
|
private static final int AAD_LENGTH = 4;
|
||||||
|
private static final int POLY_TAG_LENGTH = 16;
|
||||||
|
|
||||||
|
private static final String CIPHER_CHACHA = "CHACHA";
|
||||||
|
private static final String MAC_POLY1305 = "POLY1305";
|
||||||
|
|
||||||
|
private static final byte[] POLY_KEY_INPUT = new byte[32];
|
||||||
|
|
||||||
|
private final int authSize;
|
||||||
|
|
||||||
|
private byte[] encryptedAad;
|
||||||
|
|
||||||
|
protected Mode mode;
|
||||||
|
protected javax.crypto.Cipher aadCipher;
|
||||||
|
protected javax.crypto.Mac mac;
|
||||||
|
protected java.security.Key cipherKey;
|
||||||
|
protected java.security.Key aadCipherKey;
|
||||||
|
|
||||||
|
public ChachaPolyCipher(int authSize, int bsize, String algorithm) {
|
||||||
|
super(0, bsize, algorithm, CIPHER_CHACHA);
|
||||||
|
this.authSize = authSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAuthenticationTagSize() {
|
||||||
|
return authSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSequenceNumber(long seq) {
|
||||||
|
byte[] seqAsBytes = longToBytes(seq);
|
||||||
|
AlgorithmParameterSpec ivSpec = new IvParameterSpec(seqAsBytes);
|
||||||
|
|
||||||
|
try {
|
||||||
|
cipher.init(getMode(mode), cipherKey, ivSpec);
|
||||||
|
aadCipher.init(getMode(mode), aadCipherKey, ivSpec);
|
||||||
|
} catch (GeneralSecurityException e) {
|
||||||
|
throw new SSHRuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] polyKeyBytes = cipher.update(POLY_KEY_INPUT);
|
||||||
|
cipher.update(POLY_KEY_INPUT); // this update is required to set the block counter of ChaCha to 1
|
||||||
|
try {
|
||||||
|
mac.init(getKeySpec(polyKeyBytes));
|
||||||
|
} catch (GeneralSecurityException e) {
|
||||||
|
throw new SSHRuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptedAad = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initCipher(javax.crypto.Cipher cipher, Mode mode, byte[] key, byte[] iv)
|
||||||
|
throws InvalidKeyException, InvalidAlgorithmParameterException {
|
||||||
|
this.mode = mode;
|
||||||
|
|
||||||
|
cipherKey = getKeySpec(Arrays.copyOfRange(key, 0, CHACHA_KEY_SIZE));
|
||||||
|
aadCipherKey = getKeySpec(Arrays.copyOfRange(key, CHACHA_KEY_SIZE, 2 * CHACHA_KEY_SIZE));
|
||||||
|
|
||||||
|
try {
|
||||||
|
aadCipher = SecurityUtils.getCipher(CIPHER_CHACHA);
|
||||||
|
mac = SecurityUtils.getMAC(MAC_POLY1305);
|
||||||
|
} catch (GeneralSecurityException e) {
|
||||||
|
cipher = null;
|
||||||
|
aadCipher = null;
|
||||||
|
mac = null;
|
||||||
|
throw new SSHRuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
setSequenceNumber(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateAAD(byte[] data, int offset, int length) {
|
||||||
|
if (offset != 0 || length != AAD_LENGTH) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
String.format("updateAAD called with offset %d and length %d", offset, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == Mode.Decrypt) {
|
||||||
|
encryptedAad = Arrays.copyOfRange(data, 0, AAD_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
aadCipher.update(data, 0, AAD_LENGTH, data, 0);
|
||||||
|
} catch (GeneralSecurityException e) {
|
||||||
|
throw new SSHRuntimeException("Error updating data through cipher", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateAAD(byte[] data) {
|
||||||
|
updateAAD(data, 0, AAD_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(byte[] input, int inputOffset, int inputLen) {
|
||||||
|
if (inputOffset != AAD_LENGTH) {
|
||||||
|
throw new IllegalArgumentException("updateAAD called with inputOffset " + inputOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
final int macInputLength = AAD_LENGTH + inputLen;
|
||||||
|
|
||||||
|
if (mode == Mode.Decrypt) {
|
||||||
|
byte[] macInput = new byte[macInputLength];
|
||||||
|
System.arraycopy(encryptedAad, 0, macInput, 0, AAD_LENGTH);
|
||||||
|
System.arraycopy(input, AAD_LENGTH, macInput, AAD_LENGTH, inputLen);
|
||||||
|
|
||||||
|
byte[] expectedPolyTag = mac.doFinal(macInput);
|
||||||
|
byte[] actualPolyTag = Arrays.copyOfRange(input, macInputLength, macInputLength + POLY_TAG_LENGTH);
|
||||||
|
if (!Arrays.equals(actualPolyTag, expectedPolyTag)) {
|
||||||
|
throw new SSHRuntimeException("MAC Error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
cipher.update(input, AAD_LENGTH, inputLen, input, AAD_LENGTH);
|
||||||
|
} catch (GeneralSecurityException e) {
|
||||||
|
throw new SSHRuntimeException("Error updating data through cipher", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == Mode.Encrypt) {
|
||||||
|
byte[] macInput = Arrays.copyOf(input, macInputLength);
|
||||||
|
byte[] polyTag = mac.doFinal(macInput);
|
||||||
|
System.arraycopy(polyTag, 0, input, macInputLength, POLY_TAG_LENGTH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] longToBytes(long lng) {
|
||||||
|
return new byte[] { (byte) (lng >> 56), (byte) (lng >> 48), (byte) (lng >> 40), (byte) (lng >> 32),
|
||||||
|
(byte) (lng >> 24), (byte) (lng >> 16), (byte) (lng >> 8), (byte) lng };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.hierynomus.sshj.transport.cipher;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.transport.cipher.Cipher;
|
||||||
|
|
||||||
|
public class ChachaPolyCiphers {
|
||||||
|
|
||||||
|
public static Factory CHACHA_POLY_OPENSSH() {
|
||||||
|
return new Factory(16, 512, "chacha20-poly1305@openssh.com", "ChaCha20");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory
|
||||||
|
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
|
||||||
|
|
||||||
|
private final int authSize;
|
||||||
|
private final int keySize;
|
||||||
|
private final String name;
|
||||||
|
private final String cipher;
|
||||||
|
|
||||||
|
public Factory(int authSize, int keySize, String name, String cipher) {
|
||||||
|
this.authSize = authSize;
|
||||||
|
this.keySize = keySize;
|
||||||
|
this.name = name;
|
||||||
|
this.cipher = cipher;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cipher create() {
|
||||||
|
return new ChachaPolyCipher(authSize, keySize / 8, cipher);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -145,6 +145,7 @@ final class Decoder
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int decryptLengthAAD() throws TransportException {
|
private int decryptLengthAAD() throws TransportException {
|
||||||
|
cipher.setSequenceNumber(seq + 1 & 0xffffffffL);
|
||||||
cipher.updateAAD(inputBuffer.array(), 0, 4);
|
cipher.updateAAD(inputBuffer.array(), 0, 4);
|
||||||
|
|
||||||
final int len;
|
final int len;
|
||||||
@@ -185,10 +186,6 @@ final class Decoder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// private void decryptPayload(final byte[] data, int offset, int length) {
|
|
||||||
// cipher.update(data, cipherSize, packetLength + 4 - cipherSize);
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds {@code len} bytes from {@code b} to the decoder buffer. When a packet has been successfully decoded, hooks
|
* Adds {@code len} bytes from {@code b} to the decoder buffer. When a packet has been successfully decoded, hooks
|
||||||
* in to {@link SSHPacketHandler#handle} of the {@link SSHPacketHandler} this decoder was initialized with.
|
* in to {@link SSHPacketHandler#handle} of the {@link SSHPacketHandler} this decoder was initialized with.
|
||||||
|
|||||||
@@ -57,8 +57,6 @@ final class Encoder
|
|||||||
* @param buffer the buffer to encode
|
* @param buffer the buffer to encode
|
||||||
*
|
*
|
||||||
* @return the sequence no. of encoded packet
|
* @return the sequence no. of encoded packet
|
||||||
*
|
|
||||||
* @throws TransportException
|
|
||||||
*/
|
*/
|
||||||
long encode(SSHPacket buffer) {
|
long encode(SSHPacket buffer) {
|
||||||
encodeLock.lock();
|
encodeLock.lock();
|
||||||
@@ -140,11 +138,12 @@ final class Encoder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void aeadOutgoingBuffer(Buffer buf, int offset, int len) {
|
protected void aeadOutgoingBuffer(Buffer<?> buf, int offset, int len) {
|
||||||
if (cipher == null || cipher.getAuthenticationTagSize() == 0) {
|
if (cipher == null || cipher.getAuthenticationTagSize() == 0) {
|
||||||
throw new IllegalArgumentException("AEAD mode requires an AEAD cipher");
|
throw new IllegalArgumentException("AEAD mode requires an AEAD cipher");
|
||||||
}
|
}
|
||||||
byte[] data = buf.array();
|
byte[] data = buf.array();
|
||||||
|
cipher.setSequenceNumber(seq);
|
||||||
cipher.updateWithAAD(data, offset, 4, len);
|
cipher.updateWithAAD(data, offset, 4, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -113,4 +113,9 @@ public abstract class BaseCipher
|
|||||||
updateAAD(input, offset, aadLen);
|
updateAAD(input, offset, aadLen);
|
||||||
update(input, offset + aadLen, inputLen);
|
update(input, offset + aadLen, inputLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSequenceNumber(long seq) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,4 +78,6 @@ public interface Cipher {
|
|||||||
* @param inputLen The number of bytes to update - starting at offset + aadLen
|
* @param inputLen The number of bytes to update - starting at offset + aadLen
|
||||||
*/
|
*/
|
||||||
void updateWithAAD(byte[] input, int offset, int aadLen, int inputLen);
|
void updateWithAAD(byte[] input, int offset, int aadLen, int inputLen);
|
||||||
|
|
||||||
|
void setSequenceNumber(long seq);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,4 +73,9 @@ public class NoneCipher
|
|||||||
public void updateWithAAD(byte[] input, int offset, int aadLen, int inputLen) {
|
public void updateWithAAD(byte[] input, int offset, int aadLen, int inputLen) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSequenceNumber(long seq) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2009 - SSHJ Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.hierynomus.sshj.transport;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.transport.cipher.ChachaPolyCiphers;
|
||||||
|
import net.schmizz.sshj.common.SSHRuntimeException;
|
||||||
|
import net.schmizz.sshj.transport.cipher.Cipher;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
public class ChachaPolyCipherTest {
|
||||||
|
|
||||||
|
private static final int AAD_LENGTH = 4;
|
||||||
|
private static final int POLY_TAG_LENGTH = 16;
|
||||||
|
|
||||||
|
private static final ChachaPolyCiphers.Factory FACTORY = ChachaPolyCiphers.CHACHA_POLY_OPENSSH();
|
||||||
|
private static final String PLAINTEXT = "[Secret authenticated message using Chacha20Poly1305";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncryptDecrypt() {
|
||||||
|
Cipher enc = FACTORY.create();
|
||||||
|
byte[] key = new byte[enc.getBlockSize()];
|
||||||
|
Arrays.fill(key, (byte) 1);
|
||||||
|
enc.init(Cipher.Mode.Encrypt, key, new byte[0]);
|
||||||
|
|
||||||
|
byte[] aad = new byte[AAD_LENGTH];
|
||||||
|
byte[] ptBytes = PLAINTEXT.getBytes(StandardCharsets.UTF_8);
|
||||||
|
byte[] message = new byte[AAD_LENGTH + ptBytes.length + POLY_TAG_LENGTH];
|
||||||
|
Arrays.fill(aad, (byte) 2);
|
||||||
|
System.arraycopy(aad, 0, message, 0, AAD_LENGTH);
|
||||||
|
System.arraycopy(ptBytes, 0, message, AAD_LENGTH, ptBytes.length);
|
||||||
|
|
||||||
|
enc.updateWithAAD(message, 0, AAD_LENGTH, ptBytes.length);
|
||||||
|
byte[] corrupted = message.clone();
|
||||||
|
|
||||||
|
Cipher dec = FACTORY.create();
|
||||||
|
dec.init(Cipher.Mode.Decrypt, key, new byte[0]);
|
||||||
|
dec.updateWithAAD(message, 0, AAD_LENGTH, ptBytes.length);
|
||||||
|
|
||||||
|
assertArrayEquals(aad, Arrays.copyOf(message, AAD_LENGTH));
|
||||||
|
String decodedString =
|
||||||
|
new String(Arrays.copyOfRange(message, AAD_LENGTH, AAD_LENGTH + ptBytes.length), StandardCharsets.UTF_8);
|
||||||
|
assertEquals(PLAINTEXT, decodedString);
|
||||||
|
|
||||||
|
corrupted[corrupted.length - 1] += 1;
|
||||||
|
Cipher failingDec = FACTORY.create();
|
||||||
|
failingDec.init(Cipher.Mode.Decrypt, key, new byte[0]);
|
||||||
|
try {
|
||||||
|
failingDec.updateWithAAD(corrupted, 0, AAD_LENGTH, ptBytes.length);
|
||||||
|
fail("Modified authentication tag should not validate");
|
||||||
|
} catch (SSHRuntimeException e) {
|
||||||
|
assertEquals("MAC Error", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckOnUpdateParameters() {
|
||||||
|
Cipher cipher = FACTORY.create();
|
||||||
|
try {
|
||||||
|
cipher.update(null, 8, 42);
|
||||||
|
fail("Invalid inputOffset should trigger exception");
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
assertEquals("updateAAD called with inputOffset 8", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckOnUpdateAADParameters() {
|
||||||
|
Cipher cipher = FACTORY.create();
|
||||||
|
try {
|
||||||
|
cipher.updateAAD(null, 1, AAD_LENGTH);
|
||||||
|
fail("Invalid offset should trigger exception");
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
assertEquals("updateAAD called with offset 1 and length 4", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
cipher.updateAAD(null, 0, 5);
|
||||||
|
fail("Invalid length should trigger exception");
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
assertEquals("updateAAD called with offset 0 and length 5", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user