aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGoetz Lindenmaier <goetz@openjdk.org>2022-03-01 16:58:10 +0000
committerGoetz Lindenmaier <goetz@openjdk.org>2022-03-01 16:58:10 +0000
commit5cad68f586b1d75403ba7339828221ff6f167c53 (patch)
treed5c2ae2bc9f667469b7fa122322938fb3c80cb00
parentc0effc24b7c68834a804dd1494a658e674494f11 (diff)
downloadlibcore-5cad68f586b1d75403ba7339828221ff6f167c53.tar.gz
8255410: Add ChaCha20 and Poly1305 support to SunPKCS11 providerjdk11u/jdk-11.0.16+0jdk11u/jdk-11.0.15+4
Reviewed-by: mdoerr Backport-of: 5d8c1cc8a05e0d9aedd6d54b8147d374c2290024
-rw-r--r--src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11AEADCipher.java288
-rw-r--r--src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyGenerator.java6
-rw-r--r--src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java5
-rw-r--r--src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java22
-rw-r--r--src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java7
-rw-r--r--src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_SALSA20_CHACHA20_POLY1305_PARAMS.java80
-rw-r--r--src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c74
-rw-r--r--src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c7
-rw-r--r--src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11wrapper.h4
-rw-r--r--test/jdk/sun/security/pkcs11/Cipher/TestChaChaPoly.java323
-rw-r--r--test/jdk/sun/security/pkcs11/Cipher/TestChaChaPolyKAT.java233
-rw-r--r--test/jdk/sun/security/pkcs11/Cipher/TestChaChaPolyNoReuse.java267
-rw-r--r--test/jdk/sun/security/pkcs11/Cipher/TestChaChaPolyOutputSize.java164
-rw-r--r--test/jdk/sun/security/pkcs11/Cipher/TestKATForGCM.java12
-rw-r--r--test/jdk/sun/security/pkcs11/KeyGenerator/TestChaCha20.java91
-rw-r--r--test/jdk/sun/security/pkcs11/PKCS11Test.java34
-rw-r--r--test/jdk/sun/security/pkcs11/SecretKeyFactory/TestGeneral.java119
-rw-r--r--test/jdk/sun/security/pkcs11/tls/TestLeadingZeroesP11.java29
18 files changed, 1608 insertions, 157 deletions
diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11AEADCipher.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11AEADCipher.java
index 7913d755d4e..fa8fb3f7ed7 100644
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11AEADCipher.java
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11AEADCipher.java
@@ -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
+ switch (type) {
+ case AES_GCM: return 16;
+ case CHACHA20_POLY1305: return 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");
+ }
+ 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);
}
- 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 = type.defTagLen;
+ break;
+ case CHACHA20_POLY1305:
+ iv = new byte[type.defIvLen];
this.random.nextBytes(iv);
- tagLen = GCM_DEFAULT_TAG_LEN;
+ 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;
}
}
diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyGenerator.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyGenerator.java
index 4df62de6db1..926414608cb 100644
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyGenerator.java
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyGenerator.java
@@ -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.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -194,6 +194,10 @@ final class P11KeyGenerator extends KeyGeneratorSpi {
keySize = 128;
keyType = CKK_BLOWFISH;
break;
+ case (int)CKM_CHACHA20_KEY_GEN:
+ keySize = 256;
+ keyType = CKK_CHACHA20;
+ break;
default:
throw new ProviderException("Unknown mechanism " + mechanism);
}
diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java
index 68a3a0665c0..c98960f7fcc 100644
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java
@@ -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:
diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java
index 5e227f45315..099caac605f 100644
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java
@@ -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.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -40,6 +40,8 @@ import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.PasswordCallback;
+import com.sun.crypto.provider.ChaCha20Poly1305Parameters;
+
import jdk.internal.misc.InnocuousThread;
import sun.security.util.Debug;
import sun.security.util.ResourcesMgr;
@@ -600,6 +602,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));
// register (Secret)KeyFactories if there are any mechanisms
// for a particular algorithm that we support
@@ -626,6 +630,11 @@ public final class SunPKCS11 extends AuthProvider {
d(AGP, "GCM", "sun.security.util.GCMParameters",
m(CKM_AES_GCM));
+ d(AGP, "ChaCha20-Poly1305",
+ "com.sun.crypto.provider.ChaCha20Poly1305Parameters",
+ s("1.2.840.113549.1.9.16.3.18", "OID.1.2.840.113549.1.9.16.3.18"),
+ m(CKM_CHACHA20_POLY1305));
+
d(KA, "DH", P11KeyAgreement, s("DiffieHellman"),
m(CKM_DH_PKCS_DERIVE));
d(KA, "ECDH", "sun.security.pkcs11.P11ECDHKeyAgreement",
@@ -642,6 +651,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)
d(CIP, "ARCFOUR", P11Cipher, s("RC4"),
@@ -709,6 +720,10 @@ public final class SunPKCS11 extends AuthProvider {
d(CIP, "Blowfish/CBC/PKCS5Padding", P11Cipher,
m(CKM_BLOWFISH_CBC));
+ d(CIP, "ChaCha20-Poly1305", P11AEADCipher,
+ s("1.2.840.113549.1.9.16.3.18", "OID.1.2.840.113549.1.9.16.3.18"),
+ m(CKM_CHACHA20_POLY1305));
+
d(CIP, "RSA/ECB/PKCS1Padding", P11RSACipher, s("RSA"),
m(CKM_RSA_PKCS));
d(CIP, "RSA/ECB/NoPadding", P11RSACipher,
@@ -1165,7 +1180,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);
@@ -1218,6 +1234,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);
diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java
index 1e546462163..db5474fa888 100644
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java
@@ -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
diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_SALSA20_CHACHA20_POLY1305_PARAMS.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_SALSA20_CHACHA20_POLY1305_PARAMS.java
new file mode 100644
index 00000000000..280279ba821
--- /dev/null
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_SALSA20_CHACHA20_POLY1305_PARAMS.java
@@ -0,0 +1,80 @@
+/*
+ * 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;
+
+/**
+ * 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: ");
+ if (nonce == null) {
+ sb.append("null");
+ } else {
+ sb.append("0x");
+ for (byte b: nonce) {
+ sb.append(String.format("%02x", b));
+ }
+ }
+ sb.append(Constants.NEWLINE);
+ sb.append(Constants.INDENT);
+ sb.append("AAD: ");
+ if (aad == null) {
+ sb.append("null");
+ } else {
+ sb.append("0x");
+ for (byte b: aad) {
+ sb.append(String.format("%02x", b));
+ }
+ }
+ return sb.toString();
+ }
+}
diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c
index 59901210471..666c5eb9b3b 100644
--- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c
+++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c
@@ -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.
@@ -1160,6 +1160,73 @@ cleanup:
}
/*
+ * 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;
diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c
index cba1bf309b7..27006b9da0e 100644
--- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c
+++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c
@@ -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");
diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11wrapper.h b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11wrapper.h
index 513810d10a0..7159fc982a2 100644
--- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11wrapper.h
+++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11wrapper.h
@@ -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"
diff --git a/test/jdk/sun/security/pkcs11/Cipher/TestChaChaPoly.java b/test/jdk/sun/security/pkcs11/Cipher/TestChaChaPoly.java
new file mode 100644
index 00000000000..076660b3923
--- /dev/null
+++ b/test/jdk/sun/security/pkcs11/Cipher/TestChaChaPoly.java
@@ -0,0 +1,323 @@
+/*
+ * 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 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
+ = HexToBytes("012345670123456701234567");
+ private static final SecretKeySpec KEY = new SecretKeySpec(
+ HexToBytes("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 = HexToBytes("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 = HexToBytes("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
+ break;
+ }
+ case 1: {
+ c.init(opMode, KEY, RAND);
+ expectedIV = null; // randomly-generated
+ break;
+ }
+ case 2: {
+ c.init(opMode, KEY, IV_PARAM_SPEC);
+ params = c.getParameters();
+ if (params == null) {
+ throw new RuntimeException("Params should not be null");
+ }
+ break;
+ }
+ case 3: c.init(opMode, KEY, IV_PARAM_SPEC, RAND); break;
+ case 4: c.init(opMode, KEY, params); break;
+ case 5: c.init(opMode, KEY, params, RAND); break;
+ }
+ 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 = HexToBytes("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: " + toHexString(pt));
+ System.out.println("HexDump/pt2: " + toHexString(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);
+ }
+}
diff --git a/test/jdk/sun/security/pkcs11/Cipher/TestChaChaPolyKAT.java b/test/jdk/sun/security/pkcs11/Cipher/TestChaChaPolyKAT.java
new file mode 100644
index 00000000000..da1d91cc3de
--- /dev/null
+++ b/test/jdk/sun/security/pkcs11/Cipher/TestChaChaPolyKAT.java
@@ -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);
+ key = HexToBytes(Objects.requireNonNull(keyStr));
+ nonce = HexToBytes(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 = HexToBytes(Objects.requireNonNull(inputStr));
+ aad = (aadStr != null) ? HexToBytes(aadStr) : null;
+ expOutput = HexToBytes(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);
+ }
+}
diff --git a/test/jdk/sun/security/pkcs11/Cipher/TestChaChaPolyNoReuse.java b/test/jdk/sun/security/pkcs11/Cipher/TestChaChaPolyNoReuse.java
new file mode 100644
index 00000000000..0a760535631
--- /dev/null
+++ b/test/jdk/sun/security/pkcs11/Cipher/TestChaChaPolyNoReuse.java
@@ -0,0 +1,267 @@
+/*
+ * 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);
+ key = HexToBytes(keyStr);
+ nonce = HexToBytes(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 = HexToBytes(inputStr);
+ aad = (aadStr != null) ? HexToBytes(aadStr) : null;
+ expOutput = HexToBytes(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);
+ }
+}
diff --git a/test/jdk/sun/security/pkcs11/Cipher/TestChaChaPolyOutputSize.java b/test/jdk/sun/security/pkcs11/Cipher/TestChaChaPolyOutputSize.java
new file mode 100644
index 00000000000..57a7b9a4606
--- /dev/null
+++ b/test/jdk/sun/security/pkcs11/Cipher/TestChaChaPolyOutputSize.java
@@ -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;
+ }
+}
diff --git a/test/jdk/sun/security/pkcs11/Cipher/TestKATForGCM.java b/test/jdk/sun/security/pkcs11/Cipher/TestKATForGCM.java
index 67e94cecf68..55806f7723d 100644
--- a/test/jdk/sun/security/pkcs11/Cipher/TestKATForGCM.java
+++ b/test/jdk/sun/security/pkcs11/Cipher/TestKATForGCM.java
@@ -39,18 +39,6 @@ import java.util.*;
public class TestKATForGCM extends PKCS11Test {
- // Utility methods
- private static byte[] HexToBytes(String hexVal) {
- if (hexVal == null) return new byte[0];
- byte[] result = new byte[hexVal.length()/2];
- for (int i = 0; i < result.length; i++) {
- // 2 characters at a time
- String byteVal = hexVal.substring(2*i, 2*i +2);
- result[i] = Integer.valueOf(byteVal, 16).byteValue();
- }
- return result;
- }
-
private static class TestVector {
SecretKey key;
byte[] plainText;
diff --git a/test/jdk/sun/security/pkcs11/KeyGenerator/TestChaCha20.java b/test/jdk/sun/security/pkcs11/KeyGenerator/TestChaCha20.java
new file mode 100644
index 00000000000..cdb18dcee61
--- /dev/null
+++ b/test/jdk/sun/security/pkcs11/KeyGenerator/TestChaCha20.java
@@ -0,0 +1,91 @@
+/*
+ * 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 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: " + toHexString(keyValue));
+ if (keyValue.length != 32) {
+ throw new RuntimeException("The size of generated key must be 256");
+ }
+ }
+}
diff --git a/test/jdk/sun/security/pkcs11/PKCS11Test.java b/test/jdk/sun/security/pkcs11/PKCS11Test.java
index b914c80581a..7ae0b684017 100644
--- a/test/jdk/sun/security/pkcs11/PKCS11Test.java
+++ b/test/jdk/sun/security/pkcs11/PKCS11Test.java
@@ -111,6 +111,40 @@ public abstract class PKCS11Test {
static double nss3_version = -1;
static Provider pkcs11 = newPKCS11Provider();
+ // Utility methods
+ // Used to backport HexFormat from 17.
+ protected static byte[] HexToBytes(String hexVal) {
+ if (hexVal == null) return new byte[0];
+ byte[] result = new byte[hexVal.length()/2];
+ for (int i = 0; i < result.length; i++) {
+ // 2 characters at a time
+ String byteVal = hexVal.substring(2*i, 2*i +2);
+ result[i] = Integer.valueOf(byteVal, 16).byteValue();
+ }
+ return result;
+ }
+ private static void byte2hex(byte b, StringBuffer buf) {
+ char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
+ '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+ int high = ((b & 0xf0) >> 4);
+ int low = (b & 0x0f);
+ buf.append(hexChars[high]);
+ buf.append(hexChars[low]);
+ }
+ protected static String toHexString(byte[] block) {
+ StringBuffer buf = new StringBuffer();
+
+ int len = block.length;
+
+ for (int i = 0; i < len; i++) {
+ byte2hex(block[i], buf);
+ if (i < len-1) {
+ buf.append(":");
+ }
+ }
+ return buf.toString();
+ }
+
public static Provider newPKCS11Provider() {
ServiceLoader sl = ServiceLoader.load(java.security.Provider.class);
Iterator<Provider> iter = sl.iterator();
diff --git a/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestGeneral.java b/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestGeneral.java
new file mode 100644
index 00000000000..bca594c66bd
--- /dev/null
+++ b/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestGeneral.java
@@ -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);
+ }
+}
diff --git a/test/jdk/sun/security/pkcs11/tls/TestLeadingZeroesP11.java b/test/jdk/sun/security/pkcs11/tls/TestLeadingZeroesP11.java
index beae7253df3..12e06b19c4b 100644
--- a/test/jdk/sun/security/pkcs11/tls/TestLeadingZeroesP11.java
+++ b/test/jdk/sun/security/pkcs11/tls/TestLeadingZeroesP11.java
@@ -107,35 +107,6 @@ public class TestLeadingZeroesP11 extends PKCS11Test {
}
- /*
- * Converts a byte to hex digit and writes to the supplied buffer
- */
- private void byte2hex(byte b, StringBuffer buf) {
- char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
- '9', 'A', 'B', 'C', 'D', 'E', 'F' };
- int high = ((b & 0xf0) >> 4);
- int low = (b & 0x0f);
- buf.append(hexChars[high]);
- buf.append(hexChars[low]);
- }
-
- /*
- * Converts a byte array to hex string
- */
- private String toHexString(byte[] block) {
- StringBuffer buf = new StringBuffer();
-
- int len = block.length;
-
- for (int i = 0; i < len; i++) {
- byte2hex(block[i], buf);
- if (i < len-1) {
- buf.append(":");
- }
- }
- return buf.toString();
- }
-
private static final byte alicePubKeyEnc[] = {
(byte)0x30, (byte)0x82, (byte)0x01, (byte)0x24,
(byte)0x30, (byte)0x81, (byte)0x99, (byte)0x06,