Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ChaCha20(&Poly1305) Support #26

Merged
merged 1 commit into from
Apr 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

缺少EVP_CipherFinal()

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

补上了

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