Skip to content

Commit

Permalink
Merge pull request #26 from Sere-Fu/add_chacha
Browse files Browse the repository at this point in the history
Add ChaCha20(&Poly1305) Support
  • Loading branch information
dongbeiouba committed Apr 19, 2023
2 parents 1bec328 + b76363e commit af82af3
Show file tree
Hide file tree
Showing 8 changed files with 345 additions and 10 deletions.
133 changes: 133 additions & 0 deletions common/src/jni/main/cpp/conscrypt/native_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3025,6 +3025,10 @@ static jlong NativeCrypto_EVP_get_cipherbyname(JNIEnv* env, jclass, jstring algo
cipher = EVP_sm4_gcm();
} else if (strcasecmp(alg, "sm4-ccm") == 0) {
cipher = EVP_sm4_ccm();
} else if (strcasecmp(alg, "chacha20") == 0) {
cipher = EVP_chacha20();
} else if (strcasecmp(alg, "chacha20-poly1305") == 0) {
cipher = EVP_chacha20_poly1305();
} else {
JNI_TRACE("NativeCrypto_EVP_get_cipherbyname(%s) => error", alg);
return 0;
Expand Down Expand Up @@ -3528,6 +3532,113 @@ static void ccm_open(JNIEnv* env, const EVP_CIPHER* evpCipher, const uint8_t* ke
*out_len += tmplen;
}

static void poly1305_seal(JNIEnv* env, const EVP_CIPHER* evpCipher, const uint8_t* key,
const uint8_t* nonce, size_t nonce_len, size_t tag_len, uint8_t* out,
size_t* out_len, uint8_t* in, size_t in_len, const uint8_t* aad, size_t aad_len) {
UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new());
if (ctx.get() == nullptr) {
conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate cipher context");
JNI_TRACE("EVP_CIPHER_CTX_new => context allocation error");
return;
}
int tmplen = 0;

if (!EVP_CipherInit(ctx.get(), evpCipher, key, nonce, 1)) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EVP_CipherInit");
JNI_TRACE("EVP_CipherInit => error initializing cipher");
return;
}

// feed in aad
if (!EVP_CipherUpdate(ctx.get(), NULL, (int *)&tmplen, aad, aad_len)) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EVP_CipherUpdate");
JNI_TRACE("ctx=%p EVP_CipherUpdate => threw error", ctx.get());
return;
}

// feed in payload
if (!EVP_CipherUpdate(ctx.get(), out, (int *)&tmplen, in, in_len)) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EVP_CipherUpdate");
JNI_TRACE("ctx=%p EVP_CipherUpdate => threw error", ctx.get());
return;
}
*out_len += tmplen;

if (!EVP_CipherFinal(ctx.get(), out+tmplen, (int *)&tmplen)) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EVP_CipherFinal",
conscrypt::jniutil::throwBadPaddingException);
JNI_TRACE("ctx=%p EVP_CipherFinal => threw error", ctx.get());
return;
}
*out_len += tmplen;

// JCE expects tag after ciphertext
if (!EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_GET_TAG, tag_len, out+(*out_len))) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(
env, "EVP_CIPHER_CTX_ctrl(EVP_CTRL_AEAD_GET_TAG)",
conscrypt::jniutil::throwInvalidAlgorithmParameterException);
JNI_TRACE("EVP_CIPHER_CTX_ctrl => error getting tag");
return;
}
*out_len += tag_len;
}

static void poly1305_open(JNIEnv* env, const EVP_CIPHER* evpCipher, const uint8_t* key,
const uint8_t* nonce, size_t nonce_len, size_t tag_len, uint8_t* out,
size_t* out_len, uint8_t* in, size_t in_len, const uint8_t* aad, size_t aad_len) {
UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new());
if (ctx.get() == nullptr) {
conscrypt::jniutil::throwOutOfMemory(env, "Unable to allocate cipher context");
JNI_TRACE("EVP_CIPHER_CTX_new => context allocation error");
return;
}
int tmplen = 0;

if (!EVP_CipherInit(ctx.get(), evpCipher, NULL, NULL, 0)) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EVP_CipherInit");
JNI_TRACE("EVP_CipherInit => error initializing cipher");
return;
}

// JCE expects tag after ciphertext
if (!EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_TAG, tag_len, in+in_len-tag_len)) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(
env, "EVP_CIPHER_CTX_ctrl(EVP_CTRL_AEAD_SET_TAG)",
conscrypt::jniutil::throwInvalidAlgorithmParameterException);
JNI_TRACE("EVP_CIPHER_CTX_ctrl => error setting tag");
return;
}

if (!EVP_CipherInit(ctx.get(), NULL, key, nonce, 0)) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EVP_CipherInit");
JNI_TRACE("EVP_CipherInit => error initializing cipher");
return;
}

// feed in aad
if (!EVP_CipherUpdate(ctx.get(), NULL, (int *)&tmplen, aad, aad_len)) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EVP_CipherUpdate");
JNI_TRACE("ctx=%p EVP_CipherUpdate => threw error", ctx.get());
return;
}

// feed in encrypted payload (tag stripped)
if (!EVP_CipherUpdate(ctx.get(), out, (int *)&tmplen, in, in_len-tag_len)) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EVP_CipherUpdate");
JNI_TRACE("ctx=%p EVP_CipherUpdate => threw error", ctx.get());
return;
}
*out_len += tmplen;

if (!EVP_CipherFinal(ctx.get(), out + tmplen, (int *)&tmplen)) {
conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "EVP_CipherFinal",
conscrypt::jniutil::throwBadPaddingException);
JNI_TRACE("ctx=%p EVP_CipherFinal => threw error", ctx.get());
return;
}
*out_len += tmplen;
}

typedef void (*evp_aead_op_func)(JNIEnv* env, const EVP_CIPHER* evpCipher, const uint8_t* key,
const uint8_t* nonce, size_t nonce_len, size_t tag_len, uint8_t* out,
size_t* out_len, uint8_t* in, size_t in_len, const uint8_t* aad, size_t aad_len);
Expand Down Expand Up @@ -3644,6 +3755,26 @@ static jint NativeCrypto_EVP_CIPHER_CTX_ccm_open(JNIEnv* env, jclass, jlong evpC
inArray, inOffset, inLength, aadArray, ccm_open);
}

static jint NativeCrypto_EVP_CIPHER_CTX_poly1305_seal(JNIEnv* env, jclass, jlong evpCipherRef,
jbyteArray keyArray, jbyteArray nonceArray,
jint tagLen, jbyteArray outArray, jint outOffset,
jbyteArray inArray, jint inOffset, jint inLength,
jbyteArray aadArray) {
CHECK_ERROR_QUEUE_ON_RETURN;
return evp_aead_op(env, evpCipherRef, keyArray, nonceArray, tagLen, outArray, outOffset,
inArray, inOffset, inLength, aadArray, poly1305_seal);
}

static jint NativeCrypto_EVP_CIPHER_CTX_poly1305_open(JNIEnv* env, jclass, jlong evpCipherRef,
jbyteArray keyArray, jbyteArray nonceArray,
jint tagLen, jbyteArray outArray, jint outOffset,
jbyteArray inArray, jint inOffset, jint inLength,
jbyteArray aadArray) {
CHECK_ERROR_QUEUE_ON_RETURN;
return evp_aead_op(env, evpCipherRef, keyArray, nonceArray, tagLen, outArray, outOffset,
inArray, inOffset, inLength, aadArray, poly1305_open);
}

static jlong NativeCrypto_CMAC_CTX_new(JNIEnv* env, jclass) {
CHECK_ERROR_QUEUE_ON_RETURN;
JNI_TRACE("CMAC_CTX_new");
Expand Down Expand Up @@ -10310,6 +10441,8 @@ static JNINativeMethod sNativeCryptoMethods[] = {
CONSCRYPT_NATIVE_METHOD(EVP_CIPHER_CTX_gcm_open, "(J[B[BI[BI[BII[B)I"),
CONSCRYPT_NATIVE_METHOD(EVP_CIPHER_CTX_ccm_seal, "(J[B[BI[BI[BII[B)I"),
CONSCRYPT_NATIVE_METHOD(EVP_CIPHER_CTX_ccm_open, "(J[B[BI[BI[BII[B)I"),
CONSCRYPT_NATIVE_METHOD(EVP_CIPHER_CTX_poly1305_seal, "(J[B[BI[BI[BII[B)I"),
CONSCRYPT_NATIVE_METHOD(EVP_CIPHER_CTX_poly1305_open, "(J[B[BI[BI[BII[B)I"),
CONSCRYPT_NATIVE_METHOD(HMAC_CTX_new, "()J"),
CONSCRYPT_NATIVE_METHOD(HMAC_CTX_free, "(J)V"),
CONSCRYPT_NATIVE_METHOD(HMAC_Init_ex, "(" REF_HMAC_CTX "[BJ)V"),
Expand Down
8 changes: 8 additions & 0 deletions common/src/main/java/org/conscrypt/NativeCrypto.java
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,14 @@ static native int EVP_CIPHER_CTX_ccm_open(long evpCipher, byte[] key, byte[] non
byte[] out, int outOffset, byte[] in, int inOffset, int inLength, byte[] ad)
throws ShortBufferException, BadPaddingException;

static native int EVP_CIPHER_CTX_poly1305_seal(long evpCipher, byte[] key, byte[] nonce, int tagLengthInBytes,
byte[] out, int outOffset, byte[] in, int inOffset, int inLength, byte[] ad)
throws ShortBufferException, BadPaddingException;

static native int EVP_CIPHER_CTX_poly1305_open(long evpCipher, byte[] key, byte[] nonce, int tagLengthInBytes,
byte[] out, int outOffset, byte[] in, int inOffset, int inLength, byte[] ad)
throws ShortBufferException, BadPaddingException;

// --- CMAC functions ------------------------------------------------------

static native long CMAC_CTX_new();
Expand Down
79 changes: 79 additions & 0 deletions common/src/main/java/org/conscrypt/OpenSSLAeadCipher.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;

@Internal
public abstract class OpenSSLAeadCipher extends OpenSSLCipher {
Expand Down Expand Up @@ -92,6 +93,9 @@ protected OpenSSLAeadCipher(Mode mode) {
case CCM:
engine = new CCMEngine();
break;
case POLY1305:
engine = new POLY1305Engine();
break;
default:
break;
}
Expand Down Expand Up @@ -398,6 +402,7 @@ protected AlgorithmParameters getParameters() {
return null;
}

@Override
protected AlgorithmParameterSpec getParameterSpec(AlgorithmParameters params)
throws InvalidAlgorithmParameterException {
if (params != null) {
Expand Down Expand Up @@ -477,6 +482,7 @@ protected AlgorithmParameters getParameters() {
return null;
}

@Override
protected AlgorithmParameterSpec getParameterSpec(AlgorithmParameters params)
throws InvalidAlgorithmParameterException {
if (params != null) {
Expand All @@ -490,4 +496,77 @@ protected AlgorithmParameterSpec getParameterSpec(AlgorithmParameters params)
return null;
}
}

class POLY1305Engine extends AeadEngine {
@Override
int seal(byte[] out, int outOffset)
throws ShortBufferException, BadPaddingException {
return NativeCrypto.EVP_CIPHER_CTX_poly1305_seal(
evpCipher, encodedKey, iv, tagLenInBytes, out, outOffset, buf, 0, bufCount, aad);
}

@Override
int open(
byte[] out, int outOffset) throws ShortBufferException, BadPaddingException {
return NativeCrypto.EVP_CIPHER_CTX_poly1305_open(
evpCipher, encodedKey, iv, tagLenInBytes, out, outOffset, buf, 0, bufCount, aad);
}

@Override
void setupParams(AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException {
final int ivLenBytes;
if (params == null) {
throw new InvalidAlgorithmParameterException("Aead Cipher must be initialized with params");
} else {
if (!(params instanceof IvParameterSpec)) {
throw new InvalidAlgorithmParameterException("Must be IvParameterSpec");
} else {
iv = ((IvParameterSpec) params).getIV();
}
}

ivLenBytes = iv.length;
if (ivLenBytes != 12) {
throw new InvalidAlgorithmParameterException(
"Iv length must be 12; was " + ivLenBytes);
}

tagLenInBytes = 16;
}

@Override
void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
if (mode != Mode.POLY1305) {
throw new NoSuchAlgorithmException("Mode must be POLY1305");
}
}

@Override
protected AlgorithmParameters getParameters() {
if (iv != null && iv.length > 0) {
try {
AlgorithmParameters params = AlgorithmParameters.getInstance("ChaCha20");
params.init(new IvParameterSpec(iv));
return params;
} catch (NoSuchAlgorithmException | InvalidParameterSpecException e) {
return null;
}
}
return null;
}

@Override
protected AlgorithmParameterSpec getParameterSpec(AlgorithmParameters params)
throws InvalidAlgorithmParameterException {
if (params != null) {
try {
return params.getParameterSpec(IvParameterSpec.class);
} catch (InvalidParameterSpecException e) {
throw new InvalidAlgorithmParameterException(
"Params must be convertible to IvParameterSpec", e);
}
}
return null;
}
}
}
40 changes: 40 additions & 0 deletions common/src/main/java/org/conscrypt/OpenSSLAeadCipherChaCha20.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.conscrypt;

import java.security.InvalidKeyException;
import java.util.Locale;

public class OpenSSLAeadCipherChaCha20 extends OpenSSLAeadCipher {

public OpenSSLAeadCipherChaCha20() {
super(Mode.POLY1305);
}

@Override
String getCipherName(int keyLength, Mode mode) {
return "chacha20-" + mode.toString().toLowerCase(Locale.US);
}

@Override
String getBaseCipherName() {
return "ChaCha20";
}

@Override
void checkSupportedKeySize(int keySize) throws InvalidKeyException {
if (keySize != 32) {
throw new InvalidKeyException("Unsupported key size: " + keySize
+ " bytes (must be 32)");
}
}

@Override
int getCipherBlockSize() {
return 0;
}

@Override
boolean allowsNonceReuse() {
return true;
}

}
2 changes: 2 additions & 0 deletions common/src/main/java/org/conscrypt/OpenSSLEvpCipher.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ public abstract class OpenSSLEvpCipher extends OpenSSLCipher {

private boolean finalUsed;

protected OpenSSLEvpCipher() {}

protected OpenSSLEvpCipher(Mode mode, Padding padding) {
super(mode, padding);
}
Expand Down
58 changes: 58 additions & 0 deletions common/src/main/java/org/conscrypt/OpenSSLEvpCipherChaCha20.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2023 The Tongsuo Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://github.com/Tongsuo-Project/Tongsuo/blob/master/LICENSE.txt
*/

package org.conscrypt;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.NoSuchPaddingException;

public class OpenSSLEvpCipherChaCha20 extends OpenSSLEvpCipher {

public OpenSSLEvpCipherChaCha20() {}

@Override
String getCipherName(int keySize, Mode mode) {
return "chacha20";
}

@Override
String getBaseCipherName() {
return "ChaCha20";
}

@Override
void checkSupportedKeySize(int keySize) throws InvalidKeyException {
if (keySize != 32) {
throw new InvalidKeyException("Unsupported key size: " + keySize
+ " bytes (must be 32)");
}
}

@Override
void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
if (mode != Mode.NONE) {
throw new NoSuchAlgorithmException("Mode must be NONE");
}
}

@Override
void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
if (padding != Padding.NOPADDING) {
throw new NoSuchPaddingException("Must be NoPadding");
}
}

@Override
int getCipherBlockSize() {
return 0;
}

}
Loading

0 comments on commit af82af3

Please sign in to comment.