8140466: ChaCha20 and Poly1305 TLS Cipher Suites
Reviewed-by: xuelei, mullan
This commit is contained in:
parent
b72ab42e49
commit
962e755c3a
@ -70,6 +70,9 @@ enum CipherSuite {
|
||||
TLS_AES_256_GCM_SHA384(
|
||||
0x1302, true, "TLS_AES_256_GCM_SHA384",
|
||||
ProtocolVersion.PROTOCOLS_OF_13, B_AES_256_GCM_IV, H_SHA384),
|
||||
TLS_CHACHA20_POLY1305_SHA256(
|
||||
0x1303, true, "TLS_CHACHA20_POLY1305_SHA256",
|
||||
ProtocolVersion.PROTOCOLS_OF_13, B_CC20_P1305, H_SHA256),
|
||||
|
||||
// Suite B compliant cipher suites, see RFC 6460.
|
||||
//
|
||||
@ -87,11 +90,22 @@ enum CipherSuite {
|
||||
ProtocolVersion.PROTOCOLS_OF_12,
|
||||
K_ECDHE_ECDSA, B_AES_128_GCM, M_NULL, H_SHA256),
|
||||
|
||||
// Not suite B, but we want it to position the suite early in the list
|
||||
// of 1.2 suites.
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256(
|
||||
0xCCA9, true, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", "",
|
||||
ProtocolVersion.PROTOCOLS_OF_12,
|
||||
K_ECDHE_ECDSA, B_CC20_P1305, M_NULL, H_SHA256),
|
||||
|
||||
// AES_256(GCM)
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384(
|
||||
0xC030, true, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "",
|
||||
ProtocolVersion.PROTOCOLS_OF_12,
|
||||
K_ECDHE_RSA, B_AES_256_GCM, M_NULL, H_SHA384),
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256(
|
||||
0xCCA8, true, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", "",
|
||||
ProtocolVersion.PROTOCOLS_OF_12,
|
||||
K_ECDHE_RSA, B_CC20_P1305, M_NULL, H_SHA256),
|
||||
TLS_RSA_WITH_AES_256_GCM_SHA384(
|
||||
0x009D, true, "TLS_RSA_WITH_AES_256_GCM_SHA384", "",
|
||||
ProtocolVersion.PROTOCOLS_OF_12,
|
||||
@ -108,6 +122,10 @@ enum CipherSuite {
|
||||
0x009F, true, "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "",
|
||||
ProtocolVersion.PROTOCOLS_OF_12,
|
||||
K_DHE_RSA, B_AES_256_GCM, M_NULL, H_SHA384),
|
||||
TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256(
|
||||
0xCCAA, true, "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", "",
|
||||
ProtocolVersion.PROTOCOLS_OF_12,
|
||||
K_DHE_RSA, B_CC20_P1305, M_NULL, H_SHA256),
|
||||
TLS_DHE_DSS_WITH_AES_256_GCM_SHA384(
|
||||
0x00A3, true, "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", "",
|
||||
ProtocolVersion.PROTOCOLS_OF_12,
|
||||
@ -480,8 +498,6 @@ enum CipherSuite {
|
||||
|
||||
// Definition of the CipherSuites that are not supported but the names
|
||||
// are known.
|
||||
TLS_CHACHA20_POLY1305_SHA256( // TLS 1.3
|
||||
"TLS_CHACHA20_POLY1305_SHA256", 0x1303),
|
||||
TLS_AES_128_CCM_SHA256( // TLS 1.3
|
||||
"TLS_AES_128_CCM_SHA256", 0x1304),
|
||||
TLS_AES_128_CCM_8_SHA256( // TLS 1.3
|
||||
|
@ -129,6 +129,11 @@ final class JsseJce {
|
||||
*/
|
||||
static final String CIPHER_AES_GCM = "AES/GCM/NoPadding";
|
||||
|
||||
/**
|
||||
* JCE transformation string for ChaCha20-Poly1305
|
||||
*/
|
||||
static final String CIPHER_CHACHA20_POLY1305 = "ChaCha20-Poly1305";
|
||||
|
||||
/**
|
||||
* JCA identifier string for DSA, i.e. a DSA with SHA-1.
|
||||
*/
|
||||
|
@ -329,6 +329,32 @@ enum SSLCipher {
|
||||
new T13GcmWriteCipherGenerator(),
|
||||
ProtocolVersion.PROTOCOLS_OF_13
|
||||
)
|
||||
})),
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
B_CC20_P1305(CIPHER_CHACHA20_POLY1305, AEAD_CIPHER, 32, 32, 12,
|
||||
12, true, false,
|
||||
(Map.Entry<ReadCipherGenerator,
|
||||
ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<ReadCipherGenerator, ProtocolVersion[]>(
|
||||
new T12CC20P1305ReadCipherGenerator(),
|
||||
ProtocolVersion.PROTOCOLS_OF_12
|
||||
),
|
||||
new SimpleImmutableEntry<ReadCipherGenerator, ProtocolVersion[]>(
|
||||
new T13CC20P1305ReadCipherGenerator(),
|
||||
ProtocolVersion.PROTOCOLS_OF_13
|
||||
)
|
||||
}),
|
||||
(Map.Entry<WriteCipherGenerator,
|
||||
ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<WriteCipherGenerator, ProtocolVersion[]>(
|
||||
new T12CC20P1305WriteCipherGenerator(),
|
||||
ProtocolVersion.PROTOCOLS_OF_12
|
||||
),
|
||||
new SimpleImmutableEntry<WriteCipherGenerator, ProtocolVersion[]>(
|
||||
new T13CC20P1305WriteCipherGenerator(),
|
||||
ProtocolVersion.PROTOCOLS_OF_13
|
||||
)
|
||||
}));
|
||||
|
||||
// descriptive name including key size, e.g. AES/128
|
||||
@ -2082,6 +2108,549 @@ enum SSLCipher {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class T12CC20P1305ReadCipherGenerator
|
||||
implements ReadCipherGenerator {
|
||||
|
||||
@Override
|
||||
public SSLReadCipher createCipher(SSLCipher sslCipher,
|
||||
Authenticator authenticator, ProtocolVersion protocolVersion,
|
||||
String algorithm, Key key, AlgorithmParameterSpec params,
|
||||
SecureRandom random) throws GeneralSecurityException {
|
||||
return new CC20P1305ReadCipher(authenticator, protocolVersion,
|
||||
sslCipher, algorithm, key, params, random);
|
||||
}
|
||||
|
||||
static final class CC20P1305ReadCipher extends SSLReadCipher {
|
||||
private final Cipher cipher;
|
||||
private final int tagSize;
|
||||
private final Key key;
|
||||
private final byte[] iv;
|
||||
private final SecureRandom random;
|
||||
|
||||
CC20P1305ReadCipher(Authenticator authenticator,
|
||||
ProtocolVersion protocolVersion,
|
||||
SSLCipher sslCipher, String algorithm,
|
||||
Key key, AlgorithmParameterSpec params,
|
||||
SecureRandom random) throws GeneralSecurityException {
|
||||
super(authenticator, protocolVersion);
|
||||
this.cipher = JsseJce.getCipher(algorithm);
|
||||
this.tagSize = sslCipher.tagSize;
|
||||
this.key = key;
|
||||
this.iv = ((IvParameterSpec)params).getIV();
|
||||
this.random = random;
|
||||
|
||||
// DON'T initialize the cipher for AEAD!
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plaintext decrypt(byte contentType, ByteBuffer bb,
|
||||
byte[] sequence) throws GeneralSecurityException {
|
||||
if (bb.remaining() <= tagSize) {
|
||||
throw new BadPaddingException(
|
||||
"Insufficient buffer remaining for AEAD cipher " +
|
||||
"fragment (" + bb.remaining() + "). Needs to be " +
|
||||
"more than tag size (" + tagSize + ")");
|
||||
}
|
||||
|
||||
byte[] sn = sequence;
|
||||
if (sn == null) {
|
||||
sn = authenticator.sequenceNumber();
|
||||
}
|
||||
byte[] nonce = new byte[iv.length];
|
||||
System.arraycopy(sn, 0, nonce, nonce.length - sn.length,
|
||||
sn.length);
|
||||
for (int i = 0; i < nonce.length; i++) {
|
||||
nonce[i] ^= iv[i];
|
||||
}
|
||||
|
||||
// initialize the AEAD cipher with the unique IV
|
||||
AlgorithmParameterSpec spec = new IvParameterSpec(nonce);
|
||||
try {
|
||||
cipher.init(Cipher.DECRYPT_MODE, key, spec, random);
|
||||
} catch (InvalidKeyException |
|
||||
InvalidAlgorithmParameterException ikae) {
|
||||
// unlikely to happen
|
||||
throw new RuntimeException(
|
||||
"invalid key or spec in AEAD mode", ikae);
|
||||
}
|
||||
|
||||
// update the additional authentication data
|
||||
byte[] aad = authenticator.acquireAuthenticationBytes(
|
||||
contentType, bb.remaining() - tagSize, sequence);
|
||||
cipher.updateAAD(aad);
|
||||
|
||||
// DON'T decrypt the nonce_explicit for AEAD mode. The buffer
|
||||
// position has moved out of the nonce_explicit range.
|
||||
int len = bb.remaining();
|
||||
int pos = bb.position();
|
||||
ByteBuffer dup = bb.duplicate();
|
||||
try {
|
||||
len = cipher.doFinal(dup, bb);
|
||||
} catch (IllegalBlockSizeException ibse) {
|
||||
// unlikely to happen
|
||||
throw new RuntimeException(
|
||||
"Cipher error in AEAD mode \"" + ibse.getMessage() +
|
||||
" \"in JCE provider " + cipher.getProvider().getName());
|
||||
} catch (ShortBufferException sbe) {
|
||||
// catch BouncyCastle buffering error
|
||||
throw new RuntimeException("Cipher buffering error in " +
|
||||
"JCE provider " + cipher.getProvider().getName(), sbe);
|
||||
}
|
||||
// reset the limit to the end of the decrypted data
|
||||
bb.position(pos);
|
||||
bb.limit(pos + len);
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) {
|
||||
SSLLogger.fine(
|
||||
"Plaintext after DECRYPTION", bb.duplicate());
|
||||
}
|
||||
|
||||
return new Plaintext(contentType,
|
||||
ProtocolVersion.NONE.major, ProtocolVersion.NONE.minor,
|
||||
-1, -1L, bb.slice());
|
||||
}
|
||||
|
||||
@Override
|
||||
void dispose() {
|
||||
if (cipher != null) {
|
||||
try {
|
||||
cipher.doFinal();
|
||||
} catch (Exception e) {
|
||||
// swallow all types of exceptions.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
int estimateFragmentSize(int packetSize, int headerSize) {
|
||||
return packetSize - headerSize - tagSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class T12CC20P1305WriteCipherGenerator
|
||||
implements WriteCipherGenerator {
|
||||
@Override
|
||||
public SSLWriteCipher createCipher(SSLCipher sslCipher,
|
||||
Authenticator authenticator, ProtocolVersion protocolVersion,
|
||||
String algorithm, Key key, AlgorithmParameterSpec params,
|
||||
SecureRandom random) throws GeneralSecurityException {
|
||||
return new CC20P1305WriteCipher(authenticator, protocolVersion,
|
||||
sslCipher, algorithm, key, params, random);
|
||||
}
|
||||
|
||||
private static final class CC20P1305WriteCipher extends SSLWriteCipher {
|
||||
private final Cipher cipher;
|
||||
private final int tagSize;
|
||||
private final Key key;
|
||||
private final byte[] iv;
|
||||
private final SecureRandom random;
|
||||
|
||||
CC20P1305WriteCipher(Authenticator authenticator,
|
||||
ProtocolVersion protocolVersion,
|
||||
SSLCipher sslCipher, String algorithm,
|
||||
Key key, AlgorithmParameterSpec params,
|
||||
SecureRandom random) throws GeneralSecurityException {
|
||||
super(authenticator, protocolVersion);
|
||||
this.cipher = JsseJce.getCipher(algorithm);
|
||||
this.tagSize = sslCipher.tagSize;
|
||||
this.key = key;
|
||||
this.iv = ((IvParameterSpec)params).getIV();
|
||||
this.random = random;
|
||||
|
||||
keyLimitCountdown = cipherLimits.getOrDefault(
|
||||
algorithm.toUpperCase() + ":" + tag[0], 0L);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
|
||||
SSLLogger.fine("algorithm = " + algorithm.toUpperCase() +
|
||||
":" + tag[0] + "\ncountdown value = " +
|
||||
keyLimitCountdown);
|
||||
}
|
||||
if (keyLimitCountdown > 0) {
|
||||
keyLimitEnabled = true;
|
||||
}
|
||||
|
||||
// DON'T initialize the cipher for AEAD!
|
||||
}
|
||||
|
||||
@Override
|
||||
public int encrypt(byte contentType,
|
||||
ByteBuffer bb) {
|
||||
byte[] sn = authenticator.sequenceNumber();
|
||||
byte[] nonce = new byte[iv.length];
|
||||
System.arraycopy(sn, 0, nonce, nonce.length - sn.length,
|
||||
sn.length);
|
||||
for (int i = 0; i < nonce.length; i++) {
|
||||
nonce[i] ^= iv[i];
|
||||
}
|
||||
|
||||
// initialize the AEAD cipher for the unique IV
|
||||
AlgorithmParameterSpec spec = new IvParameterSpec(nonce);
|
||||
try {
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key, spec, random);
|
||||
} catch (InvalidKeyException |
|
||||
InvalidAlgorithmParameterException ikae) {
|
||||
// unlikely to happen
|
||||
throw new RuntimeException(
|
||||
"invalid key or spec in AEAD mode", ikae);
|
||||
}
|
||||
|
||||
// Update the additional authentication data, using the
|
||||
// implicit sequence number of the authenticator.
|
||||
byte[] aad = authenticator.acquireAuthenticationBytes(
|
||||
contentType, bb.remaining(), null);
|
||||
cipher.updateAAD(aad);
|
||||
|
||||
// DON'T encrypt the nonce for AEAD mode.
|
||||
int len = bb.remaining();
|
||||
int pos = bb.position();
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) {
|
||||
SSLLogger.fine(
|
||||
"Plaintext before ENCRYPTION",
|
||||
bb.duplicate());
|
||||
}
|
||||
|
||||
ByteBuffer dup = bb.duplicate();
|
||||
int outputSize = cipher.getOutputSize(dup.remaining());
|
||||
if (outputSize > bb.remaining()) {
|
||||
// Need to expand the limit of the output buffer for
|
||||
// the authentication tag.
|
||||
//
|
||||
// DON'T worry about the buffer's capacity, we have
|
||||
// reserved space for the authentication tag.
|
||||
bb.limit(pos + outputSize);
|
||||
}
|
||||
|
||||
try {
|
||||
len = cipher.doFinal(dup, bb);
|
||||
} catch (IllegalBlockSizeException |
|
||||
BadPaddingException | ShortBufferException ibse) {
|
||||
// unlikely to happen
|
||||
throw new RuntimeException(
|
||||
"Cipher error in AEAD mode in JCE provider " +
|
||||
cipher.getProvider().getName(), ibse);
|
||||
}
|
||||
|
||||
if (len != outputSize) {
|
||||
throw new RuntimeException(
|
||||
"Cipher buffering error in JCE provider " +
|
||||
cipher.getProvider().getName());
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
@Override
|
||||
void dispose() {
|
||||
if (cipher != null) {
|
||||
try {
|
||||
cipher.doFinal();
|
||||
} catch (Exception e) {
|
||||
// swallow all types of exceptions.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
int getExplicitNonceSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
int calculateFragmentSize(int packetLimit, int headerSize) {
|
||||
return packetLimit - headerSize - tagSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
int calculatePacketSize(int fragmentSize, int headerSize) {
|
||||
return fragmentSize + headerSize + tagSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class T13CC20P1305ReadCipherGenerator
|
||||
implements ReadCipherGenerator {
|
||||
|
||||
@Override
|
||||
public SSLReadCipher createCipher(SSLCipher sslCipher,
|
||||
Authenticator authenticator, ProtocolVersion protocolVersion,
|
||||
String algorithm, Key key, AlgorithmParameterSpec params,
|
||||
SecureRandom random) throws GeneralSecurityException {
|
||||
return new CC20P1305ReadCipher(authenticator, protocolVersion,
|
||||
sslCipher, algorithm, key, params, random);
|
||||
}
|
||||
|
||||
static final class CC20P1305ReadCipher extends SSLReadCipher {
|
||||
private final Cipher cipher;
|
||||
private final int tagSize;
|
||||
private final Key key;
|
||||
private final byte[] iv;
|
||||
private final SecureRandom random;
|
||||
|
||||
CC20P1305ReadCipher(Authenticator authenticator,
|
||||
ProtocolVersion protocolVersion,
|
||||
SSLCipher sslCipher, String algorithm,
|
||||
Key key, AlgorithmParameterSpec params,
|
||||
SecureRandom random) throws GeneralSecurityException {
|
||||
super(authenticator, protocolVersion);
|
||||
this.cipher = JsseJce.getCipher(algorithm);
|
||||
this.tagSize = sslCipher.tagSize;
|
||||
this.key = key;
|
||||
this.iv = ((IvParameterSpec)params).getIV();
|
||||
this.random = random;
|
||||
|
||||
// DON'T initialize the cipher for AEAD!
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plaintext decrypt(byte contentType, ByteBuffer bb,
|
||||
byte[] sequence) throws GeneralSecurityException {
|
||||
// An implementation may receive an unencrypted record of type
|
||||
// change_cipher_spec consisting of the single byte value 0x01
|
||||
// at any time after the first ClientHello message has been
|
||||
// sent or received and before the peer's Finished message has
|
||||
// been received and MUST simply drop it without further
|
||||
// processing.
|
||||
if (contentType == ContentType.CHANGE_CIPHER_SPEC.id) {
|
||||
return new Plaintext(contentType,
|
||||
ProtocolVersion.NONE.major, ProtocolVersion.NONE.minor,
|
||||
-1, -1L, bb.slice());
|
||||
}
|
||||
|
||||
if (bb.remaining() <= tagSize) {
|
||||
throw new BadPaddingException(
|
||||
"Insufficient buffer remaining for AEAD cipher " +
|
||||
"fragment (" + bb.remaining() + "). Needs to be " +
|
||||
"more than tag size (" + tagSize + ")");
|
||||
}
|
||||
|
||||
byte[] sn = sequence;
|
||||
if (sn == null) {
|
||||
sn = authenticator.sequenceNumber();
|
||||
}
|
||||
byte[] nonce = new byte[iv.length];
|
||||
System.arraycopy(sn, 0, nonce, nonce.length - sn.length,
|
||||
sn.length);
|
||||
for (int i = 0; i < nonce.length; i++) {
|
||||
nonce[i] ^= iv[i];
|
||||
}
|
||||
|
||||
// initialize the AEAD cipher with the unique IV
|
||||
AlgorithmParameterSpec spec = new IvParameterSpec(nonce);
|
||||
try {
|
||||
cipher.init(Cipher.DECRYPT_MODE, key, spec, random);
|
||||
} catch (InvalidKeyException |
|
||||
InvalidAlgorithmParameterException ikae) {
|
||||
// unlikely to happen
|
||||
throw new RuntimeException(
|
||||
"invalid key or spec in AEAD mode", ikae);
|
||||
}
|
||||
|
||||
// Update the additional authentication data, using the
|
||||
// implicit sequence number of the authenticator.
|
||||
byte[] aad = authenticator.acquireAuthenticationBytes(
|
||||
contentType, bb.remaining(), sn);
|
||||
cipher.updateAAD(aad);
|
||||
|
||||
int len = bb.remaining();
|
||||
int pos = bb.position();
|
||||
ByteBuffer dup = bb.duplicate();
|
||||
try {
|
||||
len = cipher.doFinal(dup, bb);
|
||||
} catch (IllegalBlockSizeException ibse) {
|
||||
// unlikely to happen
|
||||
throw new RuntimeException(
|
||||
"Cipher error in AEAD mode \"" + ibse.getMessage() +
|
||||
" \"in JCE provider " + cipher.getProvider().getName());
|
||||
} catch (ShortBufferException sbe) {
|
||||
// catch BouncyCastle buffering error
|
||||
throw new RuntimeException("Cipher buffering error in " +
|
||||
"JCE provider " + cipher.getProvider().getName(), sbe);
|
||||
}
|
||||
// reset the limit to the end of the decrypted data
|
||||
bb.position(pos);
|
||||
bb.limit(pos + len);
|
||||
|
||||
// remove inner plaintext padding
|
||||
int i = bb.limit() - 1;
|
||||
for (; i > 0 && bb.get(i) == 0; i--) {
|
||||
// blank
|
||||
}
|
||||
if (i < (pos + 1)) {
|
||||
throw new BadPaddingException(
|
||||
"Incorrect inner plaintext: no content type");
|
||||
}
|
||||
contentType = bb.get(i);
|
||||
bb.limit(i);
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) {
|
||||
SSLLogger.fine(
|
||||
"Plaintext after DECRYPTION", bb.duplicate());
|
||||
}
|
||||
|
||||
return new Plaintext(contentType,
|
||||
ProtocolVersion.NONE.major, ProtocolVersion.NONE.minor,
|
||||
-1, -1L, bb.slice());
|
||||
}
|
||||
|
||||
@Override
|
||||
void dispose() {
|
||||
if (cipher != null) {
|
||||
try {
|
||||
cipher.doFinal();
|
||||
} catch (Exception e) {
|
||||
// swallow all types of exceptions.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
int estimateFragmentSize(int packetSize, int headerSize) {
|
||||
return packetSize - headerSize - tagSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class T13CC20P1305WriteCipherGenerator
|
||||
implements WriteCipherGenerator {
|
||||
@Override
|
||||
public SSLWriteCipher createCipher(SSLCipher sslCipher,
|
||||
Authenticator authenticator, ProtocolVersion protocolVersion,
|
||||
String algorithm, Key key, AlgorithmParameterSpec params,
|
||||
SecureRandom random) throws GeneralSecurityException {
|
||||
return new CC20P1305WriteCipher(authenticator, protocolVersion,
|
||||
sslCipher, algorithm, key, params, random);
|
||||
}
|
||||
|
||||
private static final class CC20P1305WriteCipher extends SSLWriteCipher {
|
||||
private final Cipher cipher;
|
||||
private final int tagSize;
|
||||
private final Key key;
|
||||
private final byte[] iv;
|
||||
private final SecureRandom random;
|
||||
|
||||
CC20P1305WriteCipher(Authenticator authenticator,
|
||||
ProtocolVersion protocolVersion,
|
||||
SSLCipher sslCipher, String algorithm,
|
||||
Key key, AlgorithmParameterSpec params,
|
||||
SecureRandom random) throws GeneralSecurityException {
|
||||
super(authenticator, protocolVersion);
|
||||
this.cipher = JsseJce.getCipher(algorithm);
|
||||
this.tagSize = sslCipher.tagSize;
|
||||
this.key = key;
|
||||
this.iv = ((IvParameterSpec)params).getIV();
|
||||
this.random = random;
|
||||
|
||||
keyLimitCountdown = cipherLimits.getOrDefault(
|
||||
algorithm.toUpperCase() + ":" + tag[0], 0L);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
|
||||
SSLLogger.fine("algorithm = " + algorithm.toUpperCase() +
|
||||
":" + tag[0] + "\ncountdown value = " +
|
||||
keyLimitCountdown);
|
||||
}
|
||||
if (keyLimitCountdown > 0) {
|
||||
keyLimitEnabled = true;
|
||||
}
|
||||
|
||||
// DON'T initialize the cipher for AEAD!
|
||||
}
|
||||
|
||||
@Override
|
||||
public int encrypt(byte contentType,
|
||||
ByteBuffer bb) {
|
||||
byte[] sn = authenticator.sequenceNumber();
|
||||
byte[] nonce = new byte[iv.length];
|
||||
System.arraycopy(sn, 0, nonce, nonce.length - sn.length,
|
||||
sn.length);
|
||||
for (int i = 0; i < nonce.length; i++) {
|
||||
nonce[i] ^= iv[i];
|
||||
}
|
||||
|
||||
// initialize the AEAD cipher for the unique IV
|
||||
AlgorithmParameterSpec spec = new IvParameterSpec(nonce);
|
||||
try {
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key, spec, random);
|
||||
} catch (InvalidKeyException |
|
||||
InvalidAlgorithmParameterException ikae) {
|
||||
// unlikely to happen
|
||||
throw new RuntimeException(
|
||||
"invalid key or spec in AEAD mode", ikae);
|
||||
}
|
||||
|
||||
// Update the additional authentication data, using the
|
||||
// implicit sequence number of the authenticator.
|
||||
int outputSize = cipher.getOutputSize(bb.remaining());
|
||||
byte[] aad = authenticator.acquireAuthenticationBytes(
|
||||
contentType, outputSize, sn);
|
||||
cipher.updateAAD(aad);
|
||||
|
||||
int len = bb.remaining();
|
||||
int pos = bb.position();
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) {
|
||||
SSLLogger.fine(
|
||||
"Plaintext before ENCRYPTION",
|
||||
bb.duplicate());
|
||||
}
|
||||
|
||||
ByteBuffer dup = bb.duplicate();
|
||||
if (outputSize > bb.remaining()) {
|
||||
// Need to expand the limit of the output buffer for
|
||||
// the authentication tag.
|
||||
//
|
||||
// DON'T worry about the buffer's capacity, we have
|
||||
// reserved space for the authentication tag.
|
||||
bb.limit(pos + outputSize);
|
||||
}
|
||||
|
||||
try {
|
||||
len = cipher.doFinal(dup, bb);
|
||||
} catch (IllegalBlockSizeException |
|
||||
BadPaddingException | ShortBufferException ibse) {
|
||||
// unlikely to happen
|
||||
throw new RuntimeException(
|
||||
"Cipher error in AEAD mode in JCE provider " +
|
||||
cipher.getProvider().getName(), ibse);
|
||||
}
|
||||
|
||||
if (len != outputSize) {
|
||||
throw new RuntimeException(
|
||||
"Cipher buffering error in JCE provider " +
|
||||
cipher.getProvider().getName());
|
||||
}
|
||||
|
||||
if (keyLimitEnabled) {
|
||||
keyLimitCountdown -= len;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
@Override
|
||||
void dispose() {
|
||||
if (cipher != null) {
|
||||
try {
|
||||
cipher.doFinal();
|
||||
} catch (Exception e) {
|
||||
// swallow all types of exceptions.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
int getExplicitNonceSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
int calculateFragmentSize(int packetLimit, int headerSize) {
|
||||
return packetLimit - headerSize - tagSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
int calculatePacketSize(int fragmentSize, int headerSize) {
|
||||
return fragmentSize + headerSize + tagSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addMac(MAC signer,
|
||||
ByteBuffer destination, byte contentType) {
|
||||
if (signer.macAlg().size != 0) {
|
||||
@ -2367,4 +2936,3 @@ enum SSLCipher {
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,8 @@ public enum CipherSuite {
|
||||
0x1302, Protocol.TLSV1_3, Protocol.TLSV1_3),
|
||||
TLS_AES_128_GCM_SHA256(
|
||||
0x1301, Protocol.TLSV1_3, Protocol.TLSV1_3),
|
||||
TLS_CHACHA20_POLY1305_SHA256(
|
||||
0x1303, Protocol.TLSV1_3, Protocol.TLSV1_3),
|
||||
TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256(
|
||||
0xCCAA, Protocol.TLSV1_2, Protocol.TLSV1_2),
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256(
|
||||
|
@ -182,7 +182,8 @@ abstract public class SSLEngineTestCase {
|
||||
|
||||
private static final String[] TLS13_CIPHERS = {
|
||||
"TLS_AES_256_GCM_SHA384",
|
||||
"TLS_AES_128_GCM_SHA256"
|
||||
"TLS_AES_128_GCM_SHA256",
|
||||
"TLS_CHACHA20_POLY1305_SHA256"
|
||||
};
|
||||
|
||||
private static final String[] SUPPORTED_NON_KRB_CIPHERS;
|
||||
|
Loading…
Reference in New Issue
Block a user