8255410: Add ChaCha20 and Poly1305 support to SunPKCS11 provider
Reviewed-by: jnimeh
This commit is contained in:
parent
46b4a145a6
commit
5d8c1cc8a0
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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"
|
||||
|
||||
|
321
test/jdk/sun/security/pkcs11/Cipher/TestChaChaPoly.java
Normal file
321
test/jdk/sun/security/pkcs11/Cipher/TestChaChaPoly.java
Normal 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);
|
||||
}
|
||||
}
|
233
test/jdk/sun/security/pkcs11/Cipher/TestChaChaPolyKAT.java
Normal file
233
test/jdk/sun/security/pkcs11/Cipher/TestChaChaPolyKAT.java
Normal 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);
|
||||
}
|
||||
}
|
268
test/jdk/sun/security/pkcs11/Cipher/TestChaChaPolyNoReuse.java
Normal file
268
test/jdk/sun/security/pkcs11/Cipher/TestChaChaPolyNoReuse.java
Normal 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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
92
test/jdk/sun/security/pkcs11/KeyGenerator/TestChaCha20.java
Normal file
92
test/jdk/sun/security/pkcs11/KeyGenerator/TestChaCha20.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
119
test/jdk/sun/security/pkcs11/SecretKeyFactory/TestGeneral.java
Normal file
119
test/jdk/sun/security/pkcs11/SecretKeyFactory/TestGeneral.java
Normal 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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user