8255410: Add ChaCha20 and Poly1305 support to SunPKCS11 provider

Reviewed-by: jnimeh
This commit is contained in:
Valerie Peng 2021-04-29 21:02:41 +00:00
parent 46b4a145a6
commit 5d8c1cc8a0
15 changed files with 1562 additions and 117 deletions

View File

@ -42,28 +42,38 @@ import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
/**
* P11 AEAD Cipher implementation class. This class currently supports
* AES with GCM mode.
* AES cipher in GCM mode and CHACHA20-POLY1305 cipher.
*
* Note that AEAD modes do not use padding, so this class does not have
* its own padding impl. In addition, NSS CKM_AES_GCM only supports single-part
* encryption/decryption, thus the current impl uses PKCS#11 C_Encrypt/C_Decrypt
* calls and buffers data until doFinal is called.
*
* Note that PKCS#11 standard currently only supports GCM and CCM AEAD modes.
* There are no provisions for other AEAD modes yet.
* its own padding impl. In addition, some vendors such as NSS may not support
* multi-part encryption/decryption for AEAD cipher algorithms, thus the
* current impl uses PKCS#11 C_Encrypt/C_Decrypt calls and buffers data until
* doFinal is called.
*
* @since 13
*/
final class P11AEADCipher extends CipherSpi {
// mode constant for GCM mode
private static final int MODE_GCM = 10;
// supported AEAD algorithms/transformations
private enum Transformation {
AES_GCM("AES", "GCM", "NOPADDING", 16, 16),
CHACHA20_POLY1305("CHACHA20", "NONE", "NOPADDING", 12, 16);
// default constants for GCM
private static final int GCM_DEFAULT_TAG_LEN = 16;
private static final int GCM_DEFAULT_IV_LEN = 16;
final String keyAlgo;
final String mode;
final String padding;
final int defIvLen; // in bytes
final int defTagLen; // in bytes
private static final String ALGO = "AES";
Transformation(String keyAlgo, String mode, String padding,
int defIvLen, int defTagLen) {
this.keyAlgo = keyAlgo;
this.mode = mode;
this.padding = padding;
this.defIvLen = defIvLen;
this.defTagLen = defTagLen;
}
}
// token instance
private final Token token;
@ -71,10 +81,10 @@ final class P11AEADCipher extends CipherSpi {
// mechanism id
private final long mechanism;
// mode, one of MODE_* above
private final int blockMode;
// type of this AEAD cipher, one of Transformation enum above
private final Transformation type;
// acceptable key size, -1 if more than 1 key sizes are accepted
// acceptable key size in bytes, -1 if more than 1 key sizes are accepted
private final int fixedKeySize;
// associated session, if any
@ -111,99 +121,129 @@ final class P11AEADCipher extends CipherSpi {
this.mechanism = mechanism;
String[] algoParts = algorithm.split("/");
if (algoParts.length != 3) {
throw new ProviderException("Unsupported Transformation format: " +
algorithm);
}
if (!algoParts[0].startsWith("AES")) {
throw new ProviderException("Only support AES for AEAD cipher mode");
}
int index = algoParts[0].indexOf('_');
if (index != -1) {
// should be well-formed since we specify what we support
fixedKeySize = Integer.parseInt(algoParts[0].substring(index+1)) >> 3;
if (algoParts[0].startsWith("AES")) {
// for AES_GCM, need 3 parts
if (algoParts.length != 3) {
throw new AssertionError("Invalid Transformation format: " +
algorithm);
}
int index = algoParts[0].indexOf('_');
if (index != -1) {
// should be well-formed since we specify what we support
fixedKeySize = Integer.parseInt(algoParts[0].substring(index+1)) >> 3;
} else {
fixedKeySize = -1;
}
this.type = Transformation.AES_GCM;
engineSetMode(algoParts[1]);
try {
engineSetPadding(algoParts[2]);
} catch (NoSuchPaddingException e) {
throw new NoSuchAlgorithmException();
}
} else if (algoParts[0].equals("ChaCha20-Poly1305")) {
fixedKeySize = 32;
this.type = Transformation.CHACHA20_POLY1305;
if (algoParts.length > 3) {
throw new AssertionError(
"Invalid Transformation format: " + algorithm);
} else {
if (algoParts.length > 1) {
engineSetMode(algoParts[1]);
}
try {
if (algoParts.length > 2) {
engineSetPadding(algoParts[2]);
}
} catch (NoSuchPaddingException e) {
throw new NoSuchAlgorithmException();
}
}
} else {
fixedKeySize = -1;
}
this.blockMode = parseMode(algoParts[1]);
if (!algoParts[2].equals("NoPadding")) {
throw new ProviderException("Only NoPadding is supported for AEAD cipher mode");
throw new AssertionError("Unsupported transformation " + algorithm);
}
}
@Override
protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
// Disallow change of mode for now since currently it's explicitly
// defined in transformation strings
throw new NoSuchAlgorithmException("Unsupported mode " + mode);
}
private int parseMode(String mode) throws NoSuchAlgorithmException {
mode = mode.toUpperCase(Locale.ENGLISH);
int result;
if (mode.equals("GCM")) {
result = MODE_GCM;
} else {
if (!mode.toUpperCase(Locale.ENGLISH).equals(type.mode)) {
throw new NoSuchAlgorithmException("Unsupported mode " + mode);
}
return result;
}
// see JCE spec
@Override
protected void engineSetPadding(String padding)
throws NoSuchPaddingException {
// Disallow change of padding for now since currently it's explicitly
// defined in transformation strings
throw new NoSuchPaddingException("Unsupported padding " + padding);
if (!padding.toUpperCase(Locale.ENGLISH).equals(type.padding)) {
throw new NoSuchPaddingException("Unsupported padding " + padding);
}
}
// see JCE spec
@Override
protected int engineGetBlockSize() {
return 16; // constant; only AES is supported
return switch (type) {
case AES_GCM -> 16;
case CHACHA20_POLY1305 -> 0;
default -> throw new AssertionError("Unsupported type " + type);
};
}
// see JCE spec
@Override
protected int engineGetOutputSize(int inputLen) {
return doFinalLength(inputLen);
}
// see JCE spec
@Override
protected byte[] engineGetIV() {
return (iv == null) ? null : iv.clone();
}
// see JCE spec
protected AlgorithmParameters engineGetParameters() {
if (encrypt && iv == null && tagLen == -1) {
switch (blockMode) {
case MODE_GCM:
iv = new byte[GCM_DEFAULT_IV_LEN];
tagLen = GCM_DEFAULT_TAG_LEN;
break;
default:
throw new ProviderException("Unsupported mode");
}
random.nextBytes(iv);
}
try {
AlgorithmParameterSpec spec;
String apAlgo;
switch (blockMode) {
case MODE_GCM:
apAlgo = "GCM";
String apAlgo;
AlgorithmParameterSpec spec = null;
switch (type) {
case AES_GCM:
apAlgo = "GCM";
if (encrypt && iv == null && tagLen == -1) {
iv = new byte[type.defIvLen];
tagLen = type.defTagLen;
random.nextBytes(iv);
}
if (iv != null) {
spec = new GCMParameterSpec(tagLen << 3, iv);
break;
default:
throw new ProviderException("Unsupported mode");
}
AlgorithmParameters params =
AlgorithmParameters.getInstance(apAlgo);
params.init(spec);
return params;
} catch (GeneralSecurityException e) {
// NoSuchAlgorithmException, NoSuchProviderException
// InvalidParameterSpecException
throw new ProviderException("Could not encode parameters", e);
}
break;
case CHACHA20_POLY1305:
if (encrypt && iv == null) {
iv = new byte[type.defIvLen];
random.nextBytes(iv);
}
apAlgo = "ChaCha20-Poly1305";
if (iv != null) {
spec = new IvParameterSpec(iv);
}
break;
default:
throw new AssertionError("Unsupported type " + type);
}
if (spec != null) {
try {
AlgorithmParameters params =
AlgorithmParameters.getInstance(apAlgo);
params.init(spec);
return params;
} catch (GeneralSecurityException e) {
// NoSuchAlgorithmException, NoSuchProviderException
// InvalidParameterSpecException
throw new ProviderException("Could not encode parameters", e);
}
}
return null;
}
// see JCE spec
@ -231,20 +271,30 @@ final class P11AEADCipher extends CipherSpi {
updateCalled = false;
byte[] ivValue = null;
int tagLen = -1;
if (params != null) {
switch (blockMode) {
case MODE_GCM:
if (!(params instanceof GCMParameterSpec)) {
throw new InvalidAlgorithmParameterException
("Only GCMParameterSpec is supported");
switch (type) {
case AES_GCM:
if (params != null) {
if (!(params instanceof GCMParameterSpec)) {
throw new InvalidAlgorithmParameterException
("Only GCMParameterSpec is supported");
}
ivValue = ((GCMParameterSpec) params).getIV();
tagLen = ((GCMParameterSpec) params).getTLen() >> 3;
}
break;
case CHACHA20_POLY1305:
if (params != null) {
if (!(params instanceof IvParameterSpec)) {
throw new InvalidAlgorithmParameterException
("Only IvParameterSpec is supported");
}
ivValue = ((IvParameterSpec) params).getIV();
tagLen = type.defTagLen;
}
ivValue = ((GCMParameterSpec) params).getIV();
tagLen = ((GCMParameterSpec) params).getTLen() >> 3;
break;
default:
throw new ProviderException("Unsupported mode");
}
}
throw new AssertionError("Unsupported type " + type);
};
implInit(opmode, key, ivValue, tagLen, sr);
}
@ -260,13 +310,17 @@ final class P11AEADCipher extends CipherSpi {
try {
AlgorithmParameterSpec paramSpec = null;
if (params != null) {
switch (blockMode) {
case MODE_GCM:
switch (type) {
case AES_GCM:
paramSpec =
params.getParameterSpec(GCMParameterSpec.class);
break;
case CHACHA20_POLY1305:
paramSpec =
params.getParameterSpec(IvParameterSpec.class);
break;
default:
throw new ProviderException("Unsupported mode");
throw new AssertionError("Unsupported type " + type);
}
}
engineInit(opmode, key, paramSpec, sr);
@ -285,15 +339,16 @@ final class P11AEADCipher extends CipherSpi {
key.getEncoded().length) != fixedKeySize) {
throw new InvalidKeyException("Key size is invalid");
}
P11Key newKey = P11SecretKeyFactory.convertKey(token, key, ALGO);
P11Key newKey = P11SecretKeyFactory.convertKey(token, key,
type.keyAlgo);
switch (opmode) {
case Cipher.ENCRYPT_MODE:
encrypt = true;
requireReinit = Arrays.equals(iv, lastEncIv) &&
(newKey == lastEncKey);
if (requireReinit) {
throw new InvalidAlgorithmParameterException
("Cannot reuse iv for GCM encryption");
throw new InvalidAlgorithmParameterException(
"Cannot reuse the same key and iv pair");
}
break;
case Cipher.DECRYPT_MODE:
@ -309,16 +364,22 @@ final class P11AEADCipher extends CipherSpi {
if (sr != null) {
this.random = sr;
}
if (iv == null && tagLen == -1) {
// generate default values
switch (blockMode) {
case MODE_GCM:
iv = new byte[GCM_DEFAULT_IV_LEN];
switch (type) {
case AES_GCM:
iv = new byte[type.defIvLen];
this.random.nextBytes(iv);
tagLen = GCM_DEFAULT_TAG_LEN;
tagLen = type.defTagLen;
break;
case CHACHA20_POLY1305:
iv = new byte[type.defIvLen];
this.random.nextBytes(iv);
tagLen = type.defTagLen;
break;
default:
throw new ProviderException("Unsupported mode");
throw new AssertionError("Unsupported type " + type);
}
}
this.iv = iv;
@ -382,7 +443,7 @@ final class P11AEADCipher extends CipherSpi {
}
if (requireReinit) {
throw new IllegalStateException
("Must use either different key or iv for GCM encryption");
("Must use either different key or iv");
}
token.ensureValid();
@ -392,13 +453,17 @@ final class P11AEADCipher extends CipherSpi {
long p11KeyID = p11Key.getKeyID();
try {
CK_MECHANISM mechWithParams;
switch (blockMode) {
case MODE_GCM:
switch (type) {
case AES_GCM:
mechWithParams = new CK_MECHANISM(mechanism,
new CK_GCM_PARAMS(tagLen << 3, iv, aad));
break;
case CHACHA20_POLY1305:
mechWithParams = new CK_MECHANISM(mechanism,
new CK_SALSA20_CHACHA20_POLY1305_PARAMS(iv, aad));
break;
default:
throw new ProviderException("Unsupported mode: " + blockMode);
throw new AssertionError("Unsupported type: " + type);
}
if (session == null) {
session = token.getOpSession();
@ -431,10 +496,13 @@ final class P11AEADCipher extends CipherSpi {
if (encrypt) {
result += tagLen;
} else {
// PKCS11Exception: CKR_BUFFER_TOO_SMALL
//result -= tagLen;
// In earlier NSS versions, AES_GCM would report
// CKR_BUFFER_TOO_SMALL error if minus tagLen
if (type == Transformation.CHACHA20_POLY1305) {
result -= tagLen;
}
}
return result;
return (result > 0? result : 0);
}
// reset the states to the pre-initialized values
@ -492,7 +560,7 @@ final class P11AEADCipher extends CipherSpi {
}
if (requireReinit) {
throw new IllegalStateException
("Must use either different key or iv for GCM encryption");
("Must use either different key or iv for encryption");
}
if (p11Key == null) {
throw new IllegalStateException("Need to initialize Cipher first");
@ -595,6 +663,7 @@ final class P11AEADCipher extends CipherSpi {
if (outLen < requiredOutLen) {
throw new ShortBufferException();
}
boolean doCancel = true;
try {
ensureInitialized();
@ -712,6 +781,7 @@ final class P11AEADCipher extends CipherSpi {
outAddr, outArray, outOfs, outLen);
doCancel = false;
}
inBuffer.position(inBuffer.limit());
outBuffer.position(outBuffer.position() + k);
return k;
} catch (PKCS11Exception e) {
@ -748,8 +818,8 @@ final class P11AEADCipher extends CipherSpi {
} else if (errorCode == CKR_ENCRYPTED_DATA_INVALID ||
// Solaris-specific
errorCode == CKR_GENERAL_ERROR) {
throw (BadPaddingException)
(new BadPaddingException(e.toString()).initCause(e));
throw (AEADBadTagException)
(new AEADBadTagException(e.toString()).initCause(e));
}
}
@ -772,7 +842,7 @@ final class P11AEADCipher extends CipherSpi {
@Override
protected int engineGetKeySize(Key key) throws InvalidKeyException {
int n = P11SecretKeyFactory.convertKey
(token, key, ALGO).length();
(token, key, type.keyAlgo).length();
return n;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -236,6 +236,10 @@ final class P11KeyGenerator extends KeyGeneratorSpi {
keySize = adjustKeySize(128, range);
keyType = CKK_BLOWFISH;
break;
case (int)CKM_CHACHA20_KEY_GEN:
keySize = 256;
keyType = CKK_CHACHA20;
break;
case (int)CKM_SHA_1_KEY_GEN:
keySize = adjustKeySize(160, range);
keyType = CKK_SHA_1_HMAC;

View File

@ -68,6 +68,7 @@ final class P11SecretKeyFactory extends SecretKeyFactorySpi {
addKeyType("DESede", CKK_DES3);
addKeyType("AES", CKK_AES);
addKeyType("Blowfish", CKK_BLOWFISH);
addKeyType("ChaCha20", CKK_CHACHA20);
// we don't implement RC2 or IDEA, but we want to be able to generate
// keys for those SSL/TLS ciphersuites.
@ -237,6 +238,10 @@ final class P11SecretKeyFactory extends SecretKeyFactorySpi {
P11KeyGenerator.checkKeySize(CKM_BLOWFISH_KEY_GEN, n,
token);
break;
case (int)CKK_CHACHA20:
keyLength = P11KeyGenerator.checkKeySize(
CKM_CHACHA20_KEY_GEN, n, token);
break;
case (int)CKK_GENERIC_SECRET:
case (int)PCKK_TLSPREMASTER:
case (int)PCKK_TLSRSAPREMASTER:

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -42,6 +42,8 @@ import javax.security.auth.callback.ConfirmationCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.TextOutputCallback;
import com.sun.crypto.provider.ChaCha20Poly1305Parameters;
import sun.security.util.Debug;
import sun.security.util.ResourcesMgr;
import static sun.security.util.SecurityConstants.PROVIDER_VER;
@ -606,6 +608,8 @@ public final class SunPKCS11 extends AuthProvider {
m(CKM_AES_KEY_GEN));
d(KG, "Blowfish", P11KeyGenerator,
m(CKM_BLOWFISH_KEY_GEN));
d(KG, "ChaCha20", P11KeyGenerator,
m(CKM_CHACHA20_KEY_GEN));
d(KG, "HmacMD5", P11KeyGenerator, // 1.3.6.1.5.5.8.1.1
m(CKM_GENERIC_SECRET_KEY_GEN));
dA(KG, "HmacSHA1", P11KeyGenerator,
@ -655,6 +659,10 @@ public final class SunPKCS11 extends AuthProvider {
d(AGP, "GCM", "sun.security.util.GCMParameters",
m(CKM_AES_GCM));
dA(AGP, "ChaCha20-Poly1305",
"com.sun.crypto.provider.ChaCha20Poly1305Parameters",
m(CKM_CHACHA20_POLY1305));
d(KA, "DH", P11KeyAgreement,
dhAlias,
m(CKM_DH_PKCS_DERIVE));
@ -671,6 +679,8 @@ public final class SunPKCS11 extends AuthProvider {
m(CKM_AES_CBC));
d(SKF, "Blowfish", P11SecretKeyFactory,
m(CKM_BLOWFISH_CBC));
d(SKF, "ChaCha20", P11SecretKeyFactory,
m(CKM_CHACHA20_POLY1305));
// XXX attributes for Ciphers (supported modes, padding)
dA(CIP, "ARCFOUR", P11Cipher,
@ -732,6 +742,9 @@ public final class SunPKCS11 extends AuthProvider {
d(CIP, "Blowfish/CBC/PKCS5Padding", P11Cipher,
m(CKM_BLOWFISH_CBC));
dA(CIP, "ChaCha20-Poly1305", P11AEADCipher,
m(CKM_CHACHA20_POLY1305));
d(CIP, "RSA/ECB/PKCS1Padding", P11RSACipher,
List.of("RSA"),
m(CKM_RSA_PKCS));
@ -1156,7 +1169,8 @@ public final class SunPKCS11 extends AuthProvider {
} else if (type == CIP) {
if (algorithm.startsWith("RSA")) {
return new P11RSACipher(token, algorithm, mechanism);
} else if (algorithm.endsWith("GCM/NoPadding")) {
} else if (algorithm.endsWith("GCM/NoPadding") ||
algorithm.startsWith("ChaCha20-Poly1305")) {
return new P11AEADCipher(token, algorithm, mechanism);
} else {
return new P11Cipher(token, algorithm, mechanism);
@ -1209,6 +1223,8 @@ public final class SunPKCS11 extends AuthProvider {
return new sun.security.util.ECParameters();
} else if (algorithm == "GCM") {
return new sun.security.util.GCMParameters();
} else if (algorithm == "ChaCha20-Poly1305") {
return new ChaCha20Poly1305Parameters(); // from SunJCE
} else {
throw new NoSuchAlgorithmException("Unsupported algorithm: "
+ algorithm);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
*/
/* Copyright (c) 2002 Graz University of Technology. All rights reserved.
@ -155,6 +155,11 @@ public class CK_MECHANISM {
init(mechanism, params);
}
public CK_MECHANISM(long mechanism,
CK_SALSA20_CHACHA20_POLY1305_PARAMS params) {
init(mechanism, params);
}
// For PSS. the parameter may be set multiple times, use the
// CK_MECHANISM(long) constructor and setParameter(CK_RSA_PKCS_PSS_PARAMS)
// methods instead of creating yet another constructor

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.pkcs11.wrapper;
import java.util.HexFormat;
/**
* This class represents the necessary parameters required by the
* CKM_CHACHA20_POLY1305 and CKM_SALSA20_POLY1305 mechanisms as defined in
* CK_SALSA20_CHACHA20_POLY1305_PARAMS structure.<p>
* <B>PKCS#11 structure:</B>
* <PRE>
* typedef struct CK_SALSA20_CHACHA20_POLY1305_PARAMS {
* CK_BYTE_PTR pNonce;
* CK_ULONG ulNonceLen;
* CK_BYTE_PTR pAAD;
* CK_ULONG ulAADLen;
* } CK_SALSA20_CHACHA20_POLY1305_PARAMS;
* </PRE>
*
* @since 17
*/
public class CK_SALSA20_CHACHA20_POLY1305_PARAMS {
private final byte[] nonce;
private final byte[] aad;
public CK_SALSA20_CHACHA20_POLY1305_PARAMS(byte[] nonce, byte[] aad) {
this.nonce = nonce;
this.aad = aad;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(Constants.INDENT);
sb.append("Nonce: ");
sb.append((nonce == null? "null" :
"0x" + HexFormat.of().formatHex(nonce)));
sb.append(Constants.NEWLINE);
sb.append(Constants.INDENT);
sb.append("AAD: ");
sb.append((aad == null? "null" : "0x" + HexFormat.of().formatHex(aad)));
return sb.toString();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
*/
/* Copyright (c) 2002 Graz University of Technology. All rights reserved.
@ -1159,6 +1159,73 @@ cleanup:
return NULL;
}
/*
* converts the Java CK_SALSA20_CHACHA20_POLY1305_PARAMS object to a
* CK_SALSA20_CHACHA20_POLY1305_PARAMS pointer
*
* @param env - used to call JNI functions to get the Java classes and objects
* @param jParam - the Java CK_SALSA20_CHACHA20_POLY1305_PARAMS object to
* convert
* @param pLength - length of the allocated memory of the returned pointer
* @return pointer to the new CK_SALSA20_CHACHA20_POLY1305_PARAMS structure
*/
CK_SALSA20_CHACHA20_POLY1305_PARAMS_PTR
jSalsaChaChaPolyParamsToCKSalsaChaChaPolyParamPtr(
JNIEnv *env, jobject jParam, CK_ULONG *pLength)
{
CK_SALSA20_CHACHA20_POLY1305_PARAMS_PTR ckParamPtr;
jclass jParamsClass;
jfieldID fieldID;
jobject jNonce, jAad;
if (pLength != NULL) {
*pLength = 0;
}
// retrieve java values
jParamsClass = (*env)->FindClass(env,
CLASS_SALSA20_CHACHA20_POLY1305_PARAMS);
if (jParamsClass == NULL) { return NULL; }
if (!(*env)->IsInstanceOf(env, jParam, jParamsClass)) {
return NULL;
}
fieldID = (*env)->GetFieldID(env, jParamsClass, "nonce", "[B");
if (fieldID == NULL) { return NULL; }
jNonce = (*env)->GetObjectField(env, jParam, fieldID);
fieldID = (*env)->GetFieldID(env, jParamsClass, "aad", "[B");
if (fieldID == NULL) { return NULL; }
jAad = (*env)->GetObjectField(env, jParam, fieldID);
// allocate memory for CK_SALSA20_CHACHA20_POLY1305_PARAMS pointer
ckParamPtr = calloc(1, sizeof(CK_SALSA20_CHACHA20_POLY1305_PARAMS));
if (ckParamPtr == NULL) {
throwOutOfMemoryError(env, 0);
return NULL;
}
// populate using java values
jByteArrayToCKByteArray(env, jNonce, &(ckParamPtr->pNonce),
&(ckParamPtr->ulNonceLen));
if ((*env)->ExceptionCheck(env)) {
goto cleanup;
}
jByteArrayToCKByteArray(env, jAad, &(ckParamPtr->pAAD),
&(ckParamPtr->ulAADLen));
if ((*env)->ExceptionCheck(env)) {
goto cleanup;
}
if (pLength != NULL) {
*pLength = sizeof(CK_SALSA20_CHACHA20_POLY1305_PARAMS);
}
return ckParamPtr;
cleanup:
free(ckParamPtr->pNonce);
free(ckParamPtr->pAAD);
free(ckParamPtr);
return NULL;
}
/*
* converts a Java CK_MECHANISM object into a CK_MECHANISM pointer
* pointer.
@ -1437,6 +1504,11 @@ CK_VOID_PTR jMechParamToCKMechParamPtrSlow(JNIEnv *env, jobject jParam,
case CKM_AES_CCM:
ckpParamPtr = jCCMParamsToCKCCMParamPtr(env, jParam, ckpLength);
break;
case CKM_CHACHA20_POLY1305:
ckpParamPtr =
jSalsaChaChaPolyParamsToCKSalsaChaChaPolyParamPtr(env,
jParam, ckpLength);
break;
case CKM_RSA_PKCS_OAEP:
ckpParamPtr = jRsaPkcsOaepParamToCKRsaPkcsOaepParamPtr(env, jParam, ckpLength);
break;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
*/
/* Copyright (c) 2002 Graz University of Technology. All rights reserved.
@ -323,6 +323,11 @@ void freeCKMechanismPtr(CK_MECHANISM_PTR mechPtr) {
free(((CK_CCM_PARAMS*)tmp)->pNonce);
free(((CK_CCM_PARAMS*)tmp)->pAAD);
break;
case CKM_CHACHA20_POLY1305:
TRACE0("[ CK_SALSA20_CHACHA20_POLY1305_PARAMS ]\n");
free(((CK_SALSA20_CHACHA20_POLY1305_PARAMS*)tmp)->pNonce);
free(((CK_SALSA20_CHACHA20_POLY1305_PARAMS*)tmp)->pAAD);
break;
case CKM_TLS_PRF:
case CKM_NSS_TLS_PRF_GENERAL:
TRACE0("[ CK_TLS_PRF_PARAMS ]\n");

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
*/
/* Copyright (c) 2002 Graz University of Technology. All rights reserved.
@ -256,6 +256,8 @@ void printDebug(const char *format, ...);
#define CLASS_AES_CTR_PARAMS "sun/security/pkcs11/wrapper/CK_AES_CTR_PARAMS"
#define CLASS_GCM_PARAMS "sun/security/pkcs11/wrapper/CK_GCM_PARAMS"
#define CLASS_CCM_PARAMS "sun/security/pkcs11/wrapper/CK_CCM_PARAMS"
#define CLASS_SALSA20_CHACHA20_POLY1305_PARAMS \
"sun/security/pkcs11/wrapper/CK_SALSA20_CHACHA20_POLY1305_PARAMS"
#define CLASS_RSA_PKCS_PSS_PARAMS "sun/security/pkcs11/wrapper/CK_RSA_PKCS_PSS_PARAMS"
#define CLASS_RSA_PKCS_OAEP_PARAMS "sun/security/pkcs11/wrapper/CK_RSA_PKCS_OAEP_PARAMS"

View File

@ -0,0 +1,321 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8255410
* @library /test/lib ..
* @modules jdk.crypto.cryptoki
* @run main/othervm TestChaChaPoly
* @summary test for PKCS#11 ChaCha20-Poly1305 Cipher.
*/
import java.nio.ByteBuffer;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.GeneralSecurityException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.spec.InvalidParameterSpecException;
import java.util.Arrays;
import java.util.HexFormat;
import javax.crypto.Cipher;
import javax.crypto.spec.ChaCha20ParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.NoSuchPaddingException;
import jdk.test.lib.Utils;
public class TestChaChaPoly extends PKCS11Test {
private static final byte[] NONCE
= HexFormat.of().parseHex("012345670123456701234567");
private static final SecretKeySpec KEY = new SecretKeySpec(
HexFormat.of().parseHex("0123456701234567012345670123456701234567012345670123456701234567"),
"ChaCha20");
private static final ChaCha20ParameterSpec CHACHA20_PARAM_SPEC
= new ChaCha20ParameterSpec(NONCE, 0);
private static final IvParameterSpec IV_PARAM_SPEC
= new IvParameterSpec(NONCE);
private static final String ALGO = "ChaCha20-Poly1305";
private static final SecureRandom RAND = new SecureRandom();
private static Provider p;
@Override
public void main(Provider p) throws Exception {
System.out.println("Testing " + p.getName());
try {
Cipher.getInstance(ALGO, p);
} catch (NoSuchAlgorithmException nsae) {
System.out.println("Skip; no support for " + ALGO);
return;
}
this.p = p;
testTransformations();
testInit();
testAEAD();
testGetBlockSize();
testGetIV();
testInterop("SunJCE");
}
private static void testTransformations() throws Exception {
System.out.println("== transformations ==");
checkTransformation(p, ALGO, true);
checkTransformation(p, ALGO + "/None/NoPadding", true);
checkTransformation(p, ALGO + "/ECB/NoPadding", false);
checkTransformation(p, ALGO + "/None/PKCS5Padding", false);
}
private static void checkTransformation(Provider p, String t,
boolean expected) throws Exception {
try {
Cipher.getInstance(t, p);
if (!expected) {
throw new RuntimeException( "Should reject transformation: " +
t);
} else {
System.out.println("Accepted transformation: " + t);
}
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
if (!expected) {
System.out.println("Rejected transformation: " + t);
} else {
throw new RuntimeException("Should accept transformation: " +
t, e);
}
}
}
private static void testInit() throws Exception {
testInitOnCrypt(Cipher.ENCRYPT_MODE);
testInitOnCrypt(Cipher.DECRYPT_MODE);
}
private static void testInitOnCrypt(int opMode) throws Exception {
System.out.println("== init (" + getOpModeName(opMode) + ") ==");
// Need to acquire new Cipher object as ChaCha20-Poly1305 cipher
// disallow reusing the same key and iv pair
Cipher.getInstance(ALGO, p).init(opMode, KEY, IV_PARAM_SPEC);
Cipher c = Cipher.getInstance(ALGO, p);
c.init(opMode, KEY, IV_PARAM_SPEC, RAND);
AlgorithmParameters params = c.getParameters();
Cipher.getInstance(ALGO, p).init(opMode, KEY, params, RAND);
try {
// try with invalid param
Cipher.getInstance(ALGO, p).init(opMode, KEY, CHACHA20_PARAM_SPEC);
throw new RuntimeException("Should reject non-IvparameterSpec");
} catch (InvalidAlgorithmParameterException e) {
System.out.println("Expected IAPE - " + e);
}
}
private static void testAEAD() throws Exception {
byte[] expectedPt = HexFormat.of().parseHex("01234567");
byte[] ct = testUpdateAAD(Cipher.ENCRYPT_MODE, expectedPt);
byte[] pt = testUpdateAAD(Cipher.DECRYPT_MODE, ct);
if (pt != null && !Arrays.equals(pt, expectedPt)) {
System.out.println("ciphertext: " + Arrays.toString(ct));
System.out.println("plaintext: " + Arrays.toString(pt));
throw new RuntimeException("AEAD failed");
}
}
private static byte[] testUpdateAAD(int opMode, byte[] input)
throws Exception {
String opModeName = getOpModeName(opMode);
System.out.println("== updateAAD (" + opModeName + ") ==");
byte[] aad = HexFormat.of().parseHex("0000");
ByteBuffer aadBuf = ByteBuffer.wrap(aad);
Cipher ccp = Cipher.getInstance(ALGO, p);
try {
ccp.updateAAD(aadBuf);
throw new RuntimeException(
"Should throw ISE for setting AAD on uninit'ed Cipher");
} catch (IllegalStateException e) {
System.out.println("Expected ISE - " + e);
}
ccp.init(opMode, KEY, IV_PARAM_SPEC);
ccp.update(input);
try {
ccp.updateAAD(aad);
throw new RuntimeException(
"Should throw ISE for setting AAD after update");
} catch (IllegalStateException e) {
System.out.println("Expected ISE - " + e);
}
ccp.init(opMode, KEY, IV_PARAM_SPEC);
ccp.updateAAD(aadBuf);
return ccp.doFinal(input);
}
private static void testGetBlockSize() throws Exception {
testGetBlockSize(Cipher.ENCRYPT_MODE);
testGetBlockSize(Cipher.DECRYPT_MODE);
}
private static void testGetBlockSize(int opMode) throws Exception {
System.out.println("== getBlockSize (" + getOpModeName(opMode) + ") ==");
Cipher c = Cipher.getInstance(ALGO, p);
if (c.getBlockSize() != 0) {
throw new RuntimeException("Block size must be 0");
}
}
private static void testGetIV() throws Exception {
testGetIV(Cipher.ENCRYPT_MODE);
testGetIV(Cipher.DECRYPT_MODE);
}
private static void testGetIV(int opMode) throws Exception {
System.out.println("== getIv (" + getOpModeName(opMode) + ") ==");
try {
Cipher.getInstance(ALGO, p).getIV();
Cipher.getInstance(ALGO, p).getParameters();
} catch (Exception e) {
throw new RuntimeException("Should not throw ex", e);
}
// first init w/ key only
AlgorithmParameters params = null;
for (int i = 0; i < 6; i++) {
System.out.println("IV test# " + i);
Cipher c = Cipher.getInstance(ALGO, p);
byte[] expectedIV = NONCE;
try {
switch (i) {
case 0 -> {
c.init(opMode, KEY);
expectedIV = null; // randomly-generated
}
case 1 -> {
c.init(opMode, KEY, RAND);
expectedIV = null; // randomly-generated
}
case 2 -> {
c.init(opMode, KEY, IV_PARAM_SPEC);
params = c.getParameters();
if (params == null) {
throw new RuntimeException("Params should not be null");
}
}
case 3 -> c.init(opMode, KEY, IV_PARAM_SPEC, RAND);
case 4 -> c.init(opMode, KEY, params);
case 5 -> c.init(opMode, KEY, params, RAND);
}
checkIV(c, expectedIV);
System.out.println("=> Passed");
} catch (GeneralSecurityException e) {
if (opMode == Cipher.DECRYPT_MODE && i < 2) {
System.out.println("=> Passed: Expected Ex thrown");
} else {
throw new RuntimeException("Should not throw ex", e);
}
}
}
}
private static void checkIV(Cipher c, byte[] expectedIv) {
// the specified cipher has been initialized; the returned IV and
// AlgorithmParameters object should be non-null
byte[] iv = c.getIV();
AlgorithmParameters params = c.getParameters();
// fail if either is null
if (iv == null || params == null) {
throw new RuntimeException("getIV()/getParameters() should " +
"not return null");
}
// check iv matches if not null
if (expectedIv != null && !Arrays.equals(expectedIv, iv)) {
throw new RuntimeException("IV should match expected value");
}
try {
byte[] iv2 = params.getParameterSpec(IvParameterSpec.class).getIV();
if (!Arrays.equals(iv, iv2)) {
throw new RuntimeException("IV values should be consistent");
}
} catch (InvalidParameterSpecException ipe) {
// should never happen
throw new AssertionError();
}
}
private static void testInterop(String interopProv) throws Exception {
testInterop(Cipher.getInstance(ALGO, p),
Cipher.getInstance(ALGO, interopProv));
testInterop(Cipher.getInstance(ALGO, interopProv),
Cipher.getInstance(ALGO, p));
}
private static void testInterop(Cipher encCipher, Cipher decCipher)
throws Exception {
System.out.println("Interop: " + encCipher.getProvider().getName() +
" -> " + encCipher.getProvider().getName());
byte[] pt = HexFormat.of().parseHex("012345678901234567890123456789");
encCipher.init(Cipher.ENCRYPT_MODE, KEY);
byte[] ct = encCipher.doFinal(pt);
decCipher.init(Cipher.DECRYPT_MODE, KEY, encCipher.getParameters());
byte[] pt2 = decCipher.doFinal(ct);
if (!Arrays.equals(pt, pt2)) {
System.out.println("HexDump/pt: " + HexFormat.of().formatHex(pt));
System.out.println("HexDump/pt2: " + HexFormat.of().formatHex(pt2));
throw new RuntimeException("Recovered data should match");
}
System.out.println("=> Passed");
}
private static String getOpModeName(int opMode) {
switch (opMode) {
case Cipher.ENCRYPT_MODE:
return "ENCRYPT";
case Cipher.DECRYPT_MODE:
return "DECRYPT";
case Cipher.WRAP_MODE:
return "WRAP";
case Cipher.UNWRAP_MODE:
return "UNWRAP";
default:
return "";
}
}
public static void main(String[] args) throws Exception {
main(new TestChaChaPoly(), args);
}
}

View File

@ -0,0 +1,233 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8255410
* @library /test/lib ..
* @modules jdk.crypto.cryptoki
* @build jdk.test.lib.Convert
* @run main/othervm TestChaChaPolyKAT
* @summary ChaCha20-Poly1305 Cipher Implementation (KAT)
*/
import java.util.*;
import java.security.GeneralSecurityException;
import java.security.Provider;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.spec.ChaCha20ParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.AEADBadTagException;
import java.nio.ByteBuffer;
import jdk.test.lib.Convert;
public class TestChaChaPolyKAT extends PKCS11Test {
public static class TestData {
public TestData(String name, String keyStr, String nonceStr, int ctr,
int dir, String inputStr, String aadStr, String outStr) {
testName = Objects.requireNonNull(name);
HexFormat hex = HexFormat.of();
key = hex.parseHex(Objects.requireNonNull(keyStr));
nonce = hex.parseHex(Objects.requireNonNull(nonceStr));
if ((counter = ctr) < 0) {
throw new IllegalArgumentException(
"counter must be 0 or greater");
}
direction = dir;
if ((direction != Cipher.ENCRYPT_MODE) &&
(direction != Cipher.DECRYPT_MODE)) {
throw new IllegalArgumentException(
"Direction must be ENCRYPT_MODE or DECRYPT_MODE");
}
input = hex.parseHex(Objects.requireNonNull(inputStr));
aad = (aadStr != null) ? hex.parseHex(aadStr) : null;
expOutput = hex.parseHex(Objects.requireNonNull(outStr));
}
public final String testName;
public final byte[] key;
public final byte[] nonce;
public final int counter;
public final int direction;
public final byte[] input;
public final byte[] aad;
public final byte[] expOutput;
}
private static final String ALGO = "ChaCha20-Poly1305";
public static final List<TestData> aeadTestList =
new LinkedList<TestData>() {{
add(new TestData("RFC 7539 Sample AEAD Test Vector",
"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
"070000004041424344454647",
1, Cipher.ENCRYPT_MODE,
"4c616469657320616e642047656e746c656d656e206f662074686520636c6173" +
"73206f66202739393a204966204920636f756c64206f6666657220796f75206f" +
"6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73" +
"637265656e20776f756c642062652069742e",
"50515253c0c1c2c3c4c5c6c7",
"d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d6" +
"3dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b36" +
"92ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc" +
"3ff4def08e4b7a9de576d26586cec64b61161ae10b594f09e26a7e902ecbd060" +
"0691"));
add(new TestData("RFC 7539 A.5 Sample Decryption",
"1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0",
"000000000102030405060708",
1, Cipher.DECRYPT_MODE,
"64a0861575861af460f062c79be643bd5e805cfd345cf389f108670ac76c8cb2" +
"4c6cfc18755d43eea09ee94e382d26b0bdb7b73c321b0100d4f03b7f355894cf" +
"332f830e710b97ce98c8a84abd0b948114ad176e008d33bd60f982b1ff37c855" +
"9797a06ef4f0ef61c186324e2b3506383606907b6a7c02b0f9f6157b53c867e4" +
"b9166c767b804d46a59b5216cde7a4e99040c5a40433225ee282a1b0a06c523e" +
"af4534d7f83fa1155b0047718cbc546a0d072b04b3564eea1b422273f548271a" +
"0bb2316053fa76991955ebd63159434ecebb4e466dae5a1073a6727627097a10" +
"49e617d91d361094fa68f0ff77987130305beaba2eda04df997b714d6c6f2c29" +
"a6ad5cb4022b02709beead9d67890cbb22392336fea1851f38",
"f33388860000000000004e91",
"496e7465726e65742d4472616674732061726520647261667420646f63756d65" +
"6e74732076616c696420666f722061206d6178696d756d206f6620736978206d" +
"6f6e74687320616e64206d617920626520757064617465642c207265706c6163" +
"65642c206f72206f62736f6c65746564206279206f7468657220646f63756d65" +
"6e747320617420616e792074696d652e20497420697320696e617070726f7072" +
"6961746520746f2075736520496e7465726e65742d4472616674732061732072" +
"65666572656e6365206d6174657269616c206f7220746f206369746520746865" +
"6d206f74686572207468616e206173202fe2809c776f726b20696e2070726f67" +
"726573732e2fe2809d"));
}};
@Override
public void main(Provider p) throws Exception {
System.out.println("Testing " + p.getName());
try {
Cipher.getInstance(ALGO, p);
} catch (NoSuchAlgorithmException nsae) {
System.out.println("Skip; no support for " + ALGO);
return;
}
int testsPassed = 0;
int testNumber = 0;
System.out.println("----- AEAD Tests -----");
for (TestData test : aeadTestList) {
System.out.println("*** Test " + ++testNumber + ": " +
test.testName);
if (runAEADTest(p, test)) {
testsPassed++;
}
}
System.out.println();
System.out.println("Total tests: " + testNumber +
", Passed: " + testsPassed + ", Failed: " +
(testNumber - testsPassed));
if (testsPassed != testNumber) {
throw new RuntimeException("One or more tests failed. " +
"Check output for details");
}
}
private static boolean runAEADTest(Provider p, TestData testData)
throws GeneralSecurityException {
boolean result = false;
Cipher mambo = Cipher.getInstance(ALGO, p);
SecretKeySpec mamboKey = new SecretKeySpec(testData.key, "ChaCha20");
IvParameterSpec mamboSpec = new IvParameterSpec(testData.nonce);
mambo.init(testData.direction, mamboKey, mamboSpec);
byte[] out = new byte[mambo.getOutputSize(testData.input.length)];
int outOff = 0;
try {
mambo.updateAAD(testData.aad);
outOff += mambo.update(testData.input, 0, testData.input.length,
out, outOff);
outOff += mambo.doFinal(out, outOff);
} catch (AEADBadTagException abte) {
// If we get a bad tag or derive a tag mismatch, log it
// and register it as a failure
System.out.println("FAIL: " + abte);
return false;
}
if (!Arrays.equals(out, 0, outOff, testData.expOutput, 0, outOff)) {
System.out.println("ERROR - Output Mismatch!");
System.out.println("Expected:\n" +
dumpHexBytes(testData.expOutput, 16, "\n", " "));
System.out.println("Actual:\n" +
dumpHexBytes(out, 16, "\n", " "));
System.out.println();
} else {
result = true;
}
return result;
}
/**
* Dump the hex bytes of a buffer into string form.
*
* @param data The array of bytes to dump to stdout.
* @param itemsPerLine The number of bytes to display per line
* if the {@code lineDelim} character is blank then all bytes
* will be printed on a single line.
* @param lineDelim The delimiter between lines
* @param itemDelim The delimiter between bytes
*
* @return The hexdump of the byte array
*/
private static String dumpHexBytes(byte[] data, int itemsPerLine,
String lineDelim, String itemDelim) {
return dumpHexBytes(ByteBuffer.wrap(data), itemsPerLine, lineDelim,
itemDelim);
}
private static String dumpHexBytes(ByteBuffer data, int itemsPerLine,
String lineDelim, String itemDelim) {
StringBuilder sb = new StringBuilder();
if (data != null) {
data.mark();
int i = 0;
while (data.remaining() > 0) {
if (i % itemsPerLine == 0 && i != 0) {
sb.append(lineDelim);
}
sb.append(String.format("%02X", data.get())).append(itemDelim);
i++;
}
data.reset();
}
return sb.toString();
}
public static void main(String[] args) throws Exception {
main(new TestChaChaPolyKAT(), args);
}
}

View File

@ -0,0 +1,268 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8255410
* @library /test/lib ..
* @run main/othervm TestChaChaPolyNoReuse
* @summary Test PKCS#11 ChaCha20-Poly1305 Cipher Implementation
* (key/nonce reuse check)
*/
import java.util.*;
import javax.crypto.Cipher;
import java.security.spec.AlgorithmParameterSpec;
import java.security.Provider;
import java.security.NoSuchAlgorithmException;
import javax.crypto.spec.ChaCha20ParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.AEADBadTagException;
import javax.crypto.SecretKey;
import java.security.InvalidKeyException;
import java.security.InvalidAlgorithmParameterException;
public class TestChaChaPolyNoReuse extends PKCS11Test {
private static final String KEY_ALGO = "ChaCha20";
private static final String CIPHER_ALGO = "ChaCha20-Poly1305";
/**
* Basic TestMethod interface definition
*/
public interface TestMethod {
/**
* Runs the actual test case
*
* @param provider the provider to provide the requested Cipher obj.
*
* @return true if the test passes, false otherwise.
*/
boolean run(Provider p);
}
public static class TestData {
public TestData(String name, String keyStr, String nonceStr, int ctr,
int dir, String inputStr, String aadStr, String outStr) {
testName = Objects.requireNonNull(name);
HexFormat hex = HexFormat.of();
key = hex.parseHex(keyStr);
nonce = hex.parseHex(nonceStr);
if ((counter = ctr) < 0) {
throw new IllegalArgumentException(
"counter must be 0 or greater");
}
direction = dir;
if (direction != Cipher.ENCRYPT_MODE) {
throw new IllegalArgumentException(
"Direction must be ENCRYPT_MODE");
}
input = hex.parseHex(inputStr);
aad = (aadStr != null) ? hex.parseHex(aadStr) : null;
expOutput = hex.parseHex(outStr);
}
public final String testName;
public final byte[] key;
public final byte[] nonce;
public final int counter;
public final int direction;
public final byte[] input;
public final byte[] aad;
public final byte[] expOutput;
}
public static final List<TestData> aeadTestList =
new LinkedList<TestData>() {{
add(new TestData("RFC 7539 Sample AEAD Test Vector",
"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
"070000004041424344454647",
1, Cipher.ENCRYPT_MODE,
"4c616469657320616e642047656e746c656d656e206f662074686520636c6173" +
"73206f66202739393a204966204920636f756c64206f6666657220796f75206f" +
"6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73" +
"637265656e20776f756c642062652069742e",
"50515253c0c1c2c3c4c5c6c7",
"d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d6" +
"3dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b36" +
"92ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc" +
"3ff4def08e4b7a9de576d26586cec64b61161ae10b594f09e26a7e902ecbd060" +
"0691"));
}};
/**
* Make sure we do not use this Cipher object without initializing it
* at all
*/
public static final TestMethod noInitTest = new TestMethod() {
@Override
public boolean run(Provider p) {
System.out.println("----- No Init Test -----");
try {
Cipher cipher = Cipher.getInstance(CIPHER_ALGO, p);
TestData testData = aeadTestList.get(0);
// Attempting to use the cipher without initializing it
// should throw an IllegalStateException
try {
cipher.updateAAD(testData.aad);
throw new RuntimeException(
"Expected IllegalStateException not thrown");
} catch (IllegalStateException ise) {
// Do nothing, this is what we expected to happen
}
} catch (Exception exc) {
System.out.println("Unexpected exception: " + exc);
exc.printStackTrace();
return false;
}
return true;
}
};
/**
* Attempt to run two full encryption operations without an init in
* between.
*/
public static final TestMethod encTwiceNoInit = new TestMethod() {
@Override
public boolean run(Provider p) {
System.out.println("----- Encrypt 2nd time without init -----");
try {
AlgorithmParameterSpec spec;
Cipher cipher = Cipher.getInstance(CIPHER_ALGO, p);
TestData testData = aeadTestList.get(0);
spec = new IvParameterSpec(testData.nonce);
SecretKey key = new SecretKeySpec(testData.key, KEY_ALGO);
// Initialize and encrypt
cipher.init(testData.direction, key, spec);
cipher.updateAAD(testData.aad);
cipher.doFinal(testData.input);
System.out.println("First encryption complete");
// Now attempt to encrypt again without changing the key/IV
// This should fail.
try {
cipher.updateAAD(testData.aad);
} catch (IllegalStateException ise) {
// Do nothing, this is what we expected to happen
}
try {
cipher.doFinal(testData.input);
throw new RuntimeException(
"Expected IllegalStateException not thrown");
} catch (IllegalStateException ise) {
// Do nothing, this is what we expected to happen
}
} catch (Exception exc) {
System.out.println("Unexpected exception: " + exc);
exc.printStackTrace();
return false;
}
return true;
}
};
/**
* Encrypt once successfully, then attempt to init with the same
* key and nonce.
*/
public static final TestMethod encTwiceInitSameParams = new TestMethod() {
@Override
public boolean run(Provider p) {
System.out.println("----- Encrypt, then init with same params " +
"-----");
try {
AlgorithmParameterSpec spec;
Cipher cipher = Cipher.getInstance(CIPHER_ALGO, p);
TestData testData = aeadTestList.get(0);
spec = new IvParameterSpec(testData.nonce);
SecretKey key = new SecretKeySpec(testData.key, KEY_ALGO);
// Initialize then encrypt
cipher.init(testData.direction, key, spec);
cipher.updateAAD(testData.aad);
cipher.doFinal(testData.input);
System.out.println("First encryption complete");
// Initializing after the completed encryption with
// the same key and nonce should fail.
try {
cipher.init(testData.direction, key, spec);
throw new RuntimeException(
"Expected IKE or IAPE not thrown");
} catch (InvalidKeyException |
InvalidAlgorithmParameterException e) {
// Do nothing, this is what we expected to happen
}
} catch (Exception exc) {
System.out.println("Unexpected exception: " + exc);
exc.printStackTrace();
return false;
}
return true;
}
};
public static final List<TestMethod> testMethodList =
Arrays.asList(noInitTest, encTwiceNoInit, encTwiceInitSameParams);
@Override
public void main(Provider p) throws Exception {
System.out.println("Testing " + p.getName());
try {
Cipher.getInstance(CIPHER_ALGO, p);
} catch (NoSuchAlgorithmException nsae) {
System.out.println("Skip; no support for " + CIPHER_ALGO);
return;
}
int testsPassed = 0;
int testNumber = 0;
for (TestMethod tm : testMethodList) {
testNumber++;
boolean result = tm.run(p);
System.out.println("Result: " + (result ? "PASS" : "FAIL"));
if (result) {
testsPassed++;
}
}
System.out.println("Total Tests: " + testNumber +
", Tests passed: " + testsPassed);
if (testsPassed < testNumber) {
throw new RuntimeException(
"Not all tests passed. See output for failure info");
}
}
public static void main(String[] args) throws Exception {
main(new TestChaChaPolyNoReuse(), args);
}
}

View File

@ -0,0 +1,164 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8255410
* @summary Check ChaCha20-Poly1305 cipher output size
* @library /test/lib ..
* @build jdk.test.lib.Convert
* @run main TestChaChaPolyOutputSize
*/
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.SecureRandom;
import java.security.Provider;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.spec.ChaCha20ParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class TestChaChaPolyOutputSize extends PKCS11Test {
private static final SecureRandom SR = new SecureRandom();
private static final SecretKeySpec KEY = new SecretKeySpec(new byte[32],
"ChaCha20");
private static final String ALGO = "ChaCha20-Poly1305";
public static void main(String args[]) throws Exception {
main(new TestChaChaPolyOutputSize(), args);
}
@Override
public void main(Provider p) throws GeneralSecurityException {
System.out.println("Testing " + p.getName());
try {
Cipher.getInstance(ALGO, p);
} catch (NoSuchAlgorithmException nsae) {
System.out.println("Skip; no support for " + ALGO);
return;
}
testGetOutSize(p);
testMultiPartAEADDec(p);
}
private static void testGetOutSize(Provider p)
throws GeneralSecurityException {
Cipher ccp = Cipher.getInstance(ALGO, p);
ccp.init(Cipher.ENCRYPT_MODE, KEY,
new IvParameterSpec(getRandBuf(12)));
// Encryption lengths are calculated as the input length plus the tag
// length (16).
testOutLen(ccp, 0, 16);
testOutLen(ccp, 5, 21);
testOutLen(ccp, 5120, 5136);
// perform an update, then test with a final block
byte[] input = new byte[5120];
SR.nextBytes(input);
byte[] updateOut = ccp.update(input);
testOutLen(ccp, 1024, 1040 +
(5120 - (updateOut == null? 0 : updateOut.length)));
// Decryption lengths are handled differently for AEAD mode. The length
// should be zero for anything up to and including the first 16 bytes
// (since that's the tag). Anything above that should be the input
// length plus any unprocessed input (via update calls), minus the
// 16 byte tag.
ccp.init(Cipher.DECRYPT_MODE, KEY, new IvParameterSpec(getRandBuf(12)));
testOutLen(ccp, 0, 0);
testOutLen(ccp, 5, 0);
testOutLen(ccp, 16, 0);
testOutLen(ccp, 5120, 5104);
// Perform an update, then test with the length of a final chunk
// of data.
updateOut = ccp.update(input);
testOutLen(ccp, 1024, 6128 - (updateOut == null? 0 : updateOut.length));
}
private static void testMultiPartAEADDec(Provider p)
throws GeneralSecurityException {
IvParameterSpec ivps = new IvParameterSpec(getRandBuf(12));
// Encrypt some data so we can test decryption.
byte[] pText = getRandBuf(2048);
ByteBuffer pTextBase = ByteBuffer.wrap(pText);
Cipher enc = Cipher.getInstance(ALGO, p);
enc.init(Cipher.ENCRYPT_MODE, KEY, ivps);
ByteBuffer ctBuf = ByteBuffer.allocateDirect(
enc.getOutputSize(pText.length));
enc.doFinal(pTextBase, ctBuf);
// Create a new direct plain text ByteBuffer which will catch the
// decrypted data.
ByteBuffer ptBuf = ByteBuffer.allocateDirect(pText.length);
// Set the cipher text buffer limit to roughly half the data so we can
// do an update/final sequence.
ctBuf.position(0).limit(1024);
Cipher dec = Cipher.getInstance(ALGO, p);
dec.init(Cipher.DECRYPT_MODE, KEY, ivps);
dec.update(ctBuf, ptBuf);
System.out.println("CTBuf: " + ctBuf);
System.out.println("PTBuf: " + ptBuf);
ctBuf.limit(ctBuf.capacity());
dec.doFinal(ctBuf, ptBuf);
// NOTE: do not use flip() which will set limit based on current
// position. ptBuf curr pos = 2048 vs pTextBase pos = 0
ptBuf.flip();
pTextBase.flip();
System.out.println("PT Base:" + pTextBase);
System.out.println("PT Actual:" + ptBuf);
if (pTextBase.compareTo(ptBuf) != 0) {
StringBuilder sb = new StringBuilder();
sb.append("Plaintext mismatch: Original: ").
append(pTextBase.toString()).append("\nActual :").
append(ptBuf);
throw new RuntimeException(sb.toString());
}
}
private static void testOutLen(Cipher c, int inLen, int expOut) {
int actualOut = c.getOutputSize(inLen);
if (actualOut != expOut) {
throw new RuntimeException("Cipher " + c + ", in: " + inLen +
", expOut: " + expOut + ", actual: " + actualOut);
}
}
private static byte[] getRandBuf(int len) {
byte[] buf = new byte[len];
SR.nextBytes(buf);
return buf;
}
}

View File

@ -0,0 +1,92 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8255410
* @modules jdk.crypto.cryptoki
* @summary Check ChaCha20 key generator.
* @library /test/lib ..
* @run main/othervm TestChaCha20
*/
import java.security.Provider;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidParameterException;
import java.security.NoSuchAlgorithmException;
import java.util.HexFormat;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.ChaCha20ParameterSpec;
public class TestChaCha20 extends PKCS11Test {
private static final String ALGO = "ChaCha20";
public static void main(String[] args) throws Exception {
main(new TestChaCha20(), args);
}
@Override
public void main(Provider p) throws Exception {
System.out.println("Testing " + p.getName());
KeyGenerator kg;
try {
kg = KeyGenerator.getInstance(ALGO, p);
} catch (NoSuchAlgorithmException nsae) {
System.out.println("Skip; no support for " + ALGO);
return;
}
try {
kg.init(new ChaCha20ParameterSpec(new byte[12], 0));
throw new RuntimeException(
"ChaCha20 key generation should not need any paramSpec");
} catch (InvalidAlgorithmParameterException e) {
System.out.println("Expected IAPE: " + e.getMessage());
}
for (int keySize : new int[] { 32, 64, 128, 256, 512, 1024 }) {
try {
kg.init(keySize);
if (keySize != 256) {
throw new RuntimeException(keySize + " is invalid keysize");
}
} catch (InvalidParameterException e) {
if (keySize == 256) {
throw new RuntimeException("IPE thrown for valid keySize");
} else {
System.out.println("Expected IPE thrown for " + keySize);
}
}
}
//kg.init(256);
SecretKey key = kg.generateKey();
byte[] keyValue = key.getEncoded();
System.out.println("Key: " + HexFormat.of().formatHex(keyValue));
if (keyValue.length != 32) {
throw new RuntimeException("The size of generated key must be 256");
}
}
}

View File

@ -0,0 +1,119 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8255410
* @summary test the general SecretKeyFactory functionality
* @library /test/lib ..
* @modules jdk.crypto.cryptoki
* @run main/othervm TestGeneral
*/
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.SecureRandom;
import java.util.Arrays;
import javax.crypto.SecretKeyFactory;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class TestGeneral extends PKCS11Test {
private enum TestResult {
PASS,
FAIL,
TBD // unused for now
}
public static void main(String[] args) throws Exception {
main(new TestGeneral(), args);
}
private void test(String algorithm, SecretKey key, Provider p,
TestResult expected) throws RuntimeException {
System.out.println("Testing " + algorithm + " SKF from " + p.getName());
SecretKeyFactory skf;
try {
skf = SecretKeyFactory.getInstance(algorithm, p);
} catch (NoSuchAlgorithmException e) {
System.out.println("Not supported, skipping: " + e);
return;
}
try {
SecretKey key2 = skf.translateKey(key);
if (expected == TestResult.FAIL) {
throw new RuntimeException("translateKey() should FAIL");
}
System.out.println("=> translated key");
if (!key2.getAlgorithm().equalsIgnoreCase(algorithm)) {
throw new RuntimeException("translated key algorithm mismatch");
}
System.out.println("=> checked key algorithm");
// proceed to check encodings if format match
if (key2.getFormat().equalsIgnoreCase(key.getFormat())) {
if (key2.getEncoded() != null &&
!Arrays.equals(key.getEncoded(), key2.getEncoded())) {
throw new RuntimeException(
"translated key encoding mismatch");
}
System.out.println("=> checked key format and encoding");
}
} catch (Exception e) {
if (expected == TestResult.PASS) {
e.printStackTrace();
throw new RuntimeException("translateKey() should pass", e);
}
}
}
@Override
public void main(Provider p) throws Exception {
byte[] rawBytes = new byte[32];
new SecureRandom().nextBytes(rawBytes);
SecretKey aes_128Key = new SecretKeySpec(rawBytes, 0, 16, "AES");
SecretKey aes_256Key = new SecretKeySpec(rawBytes, 0, 32, "AES");
SecretKey bf_128Key = new SecretKeySpec(rawBytes, 0, 16, "Blowfish");
SecretKey cc20Key = new SecretKeySpec(rawBytes, 0, 32, "ChaCha20");
// fixed key length
test("AES", aes_128Key, p, TestResult.PASS);
test("AES", aes_256Key, p, TestResult.PASS);
test("AES", cc20Key, p, TestResult.FAIL);
test("ChaCha20", aes_128Key, p, TestResult.FAIL);
test("ChaCha20", aes_256Key, p, TestResult.FAIL);
test("ChaCha20", cc20Key, p, TestResult.PASS);
// variable key length
// Different PKCS11 impls may have different ranges
// of supported key sizes for variable-key-length
// algorithms.
test("Blowfish", aes_128Key, p, TestResult.FAIL);
test("Blowfish", cc20Key, p, TestResult.FAIL);
test("Blowfish", bf_128Key, p, TestResult.PASS);
}
}