mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
1072 lines
34 KiB
Java
1072 lines
34 KiB
Java
/*
|
|
* Copyright (c) 2003, 2022, 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;
|
|
|
|
import java.io.*;
|
|
import static java.io.StreamTokenizer.*;
|
|
import java.math.BigInteger;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.util.*;
|
|
|
|
import java.security.*;
|
|
|
|
import sun.security.util.PropertyExpander;
|
|
|
|
import sun.security.pkcs11.wrapper.*;
|
|
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
|
|
import static sun.security.pkcs11.wrapper.CK_ATTRIBUTE.*;
|
|
|
|
import static sun.security.pkcs11.TemplateManager.*;
|
|
|
|
/**
|
|
* Configuration container and file parsing.
|
|
*
|
|
* @author Andreas Sterbenz
|
|
* @since 1.5
|
|
*/
|
|
final class Config {
|
|
|
|
static final int ERR_HALT = 1;
|
|
static final int ERR_IGNORE_ALL = 2;
|
|
static final int ERR_IGNORE_LIB = 3;
|
|
|
|
// same as allowSingleThreadedModules but controlled via a system property
|
|
// and applied to all providers. if set to false, no SunPKCS11 instances
|
|
// will accept single threaded modules regardless of the setting in their
|
|
// config files.
|
|
private static final boolean staticAllowSingleThreadedModules;
|
|
private static final String osName;
|
|
private static final String osArch;
|
|
|
|
static {
|
|
@SuppressWarnings("removal")
|
|
List<String> props = AccessController.doPrivileged(
|
|
new PrivilegedAction<>() {
|
|
@Override
|
|
public List<String> run() {
|
|
return List.of(
|
|
System.getProperty(
|
|
"sun.security.pkcs11.allowSingleThreadedModules",
|
|
"true"),
|
|
System.getProperty("os.name"),
|
|
System.getProperty("os.arch"));
|
|
}
|
|
}
|
|
);
|
|
if ("false".equalsIgnoreCase(props.get(0))) {
|
|
staticAllowSingleThreadedModules = false;
|
|
} else {
|
|
staticAllowSingleThreadedModules = true;
|
|
}
|
|
osName = props.get(1);
|
|
osArch = props.get(2);
|
|
}
|
|
|
|
private static final boolean DEBUG = false;
|
|
|
|
// file name containing this configuration
|
|
private String filename;
|
|
|
|
// Reader and StringTokenizer used during parsing
|
|
private Reader reader;
|
|
|
|
private StreamTokenizer st;
|
|
|
|
private Set<String> parsedKeywords;
|
|
|
|
// name suffix of the provider
|
|
private String name;
|
|
|
|
// name of the PKCS#11 library
|
|
private String library;
|
|
|
|
// description to pass to the provider class
|
|
private String description;
|
|
|
|
// slotID of the slot to use
|
|
private int slotID = -1;
|
|
|
|
// slot to use, specified as index in the slotlist
|
|
private int slotListIndex = -1;
|
|
|
|
// set of enabled mechanisms (or null to use default)
|
|
private Set<Long> enabledMechanisms;
|
|
|
|
// set of disabled mechanisms
|
|
private Set<Long> disabledMechanisms;
|
|
|
|
// whether to print debug info during startup
|
|
private boolean showInfo = false;
|
|
|
|
// template manager, initialized from parsed attributes
|
|
private TemplateManager templateManager;
|
|
|
|
// how to handle error during startup, one of ERR_
|
|
private int handleStartupErrors = ERR_HALT;
|
|
|
|
// flag indicating whether the P11KeyStore should
|
|
// be more tolerant of input parameters
|
|
private boolean keyStoreCompatibilityMode = true;
|
|
|
|
// flag indicating whether we need to explicitly cancel operations
|
|
// see Token
|
|
private boolean explicitCancel = true;
|
|
|
|
// how often to test for token insertion, if no token is present
|
|
private int insertionCheckInterval = 2000;
|
|
|
|
// short ms value to indicate how often native cleaner thread is called
|
|
private int resourceCleanerShortInterval = 2_000;
|
|
// long ms value to indicate how often native cleaner thread is called
|
|
private int resourceCleanerLongInterval = 60_000;
|
|
|
|
// should Token be destroyed after logout()
|
|
private boolean destroyTokenAfterLogout;
|
|
|
|
// flag indicating whether to omit the call to C_Initialize()
|
|
// should be used only if we are running within a process that
|
|
// has already called it (e.g. Plugin inside of Mozilla/NSS)
|
|
private boolean omitInitialize = false;
|
|
|
|
// whether to allow modules that only support single threaded access.
|
|
// they cannot be used safely from multiple PKCS#11 consumers in the
|
|
// same process, for example NSS and SunPKCS11
|
|
private boolean allowSingleThreadedModules = true;
|
|
|
|
// name of the C function that returns the PKCS#11 functionlist
|
|
// This option primarily exists for the deprecated
|
|
// Secmod.Module.getProvider() method.
|
|
private String functionList = null;
|
|
|
|
// whether to use NSS secmod mode. Implicitly set if nssLibraryDirectory,
|
|
// nssSecmodDirectory, or nssModule is specified.
|
|
private boolean nssUseSecmod;
|
|
|
|
// location of the NSS library files (libnss3.so, etc.)
|
|
private String nssLibraryDirectory;
|
|
|
|
// location of secmod.db
|
|
private String nssSecmodDirectory;
|
|
|
|
// which NSS module to use
|
|
private String nssModule;
|
|
|
|
private Secmod.DbMode nssDbMode = Secmod.DbMode.READ_WRITE;
|
|
|
|
// Whether the P11KeyStore should specify the CKA_NETSCAPE_DB attribute
|
|
// when creating private keys. Only valid if nssUseSecmod is true.
|
|
private boolean nssNetscapeDbWorkaround = true;
|
|
|
|
// Special init argument string for the NSS softtoken.
|
|
// This is used when using the NSS softtoken directly without secmod mode.
|
|
private String nssArgs;
|
|
|
|
// whether to use NSS trust attributes for the KeyStore of this provider
|
|
// this option is for internal use by the SunPKCS11 code only and
|
|
// works only for NSS providers created via the Secmod API
|
|
private boolean nssUseSecmodTrust = false;
|
|
|
|
// Flag to indicate whether the X9.63 encoding for EC points shall be used
|
|
// (true) or whether that encoding shall be wrapped in an ASN.1 OctetString
|
|
// (false).
|
|
private boolean useEcX963Encoding = false;
|
|
|
|
// Flag to indicate whether NSS should favour performance (false) or
|
|
// memory footprint (true).
|
|
private boolean nssOptimizeSpace = false;
|
|
|
|
Config(String fn) throws IOException {
|
|
this.filename = fn;
|
|
if (filename.startsWith("--")) {
|
|
// inline config
|
|
String config = filename.substring(2).replace("\\n", "\n");
|
|
reader = new StringReader(config);
|
|
} else {
|
|
reader = new BufferedReader(new InputStreamReader
|
|
(new FileInputStream(expand(filename)),
|
|
StandardCharsets.ISO_8859_1));
|
|
}
|
|
parsedKeywords = new HashSet<String>();
|
|
st = new StreamTokenizer(reader);
|
|
setupTokenizer();
|
|
parse();
|
|
}
|
|
|
|
String getFileName() {
|
|
return filename;
|
|
}
|
|
|
|
String getName() {
|
|
return name;
|
|
}
|
|
|
|
String getLibrary() {
|
|
return library;
|
|
}
|
|
|
|
String getDescription() {
|
|
if (description != null) {
|
|
return description;
|
|
}
|
|
return "SunPKCS11-" + name + " using library " + library;
|
|
}
|
|
|
|
int getSlotID() {
|
|
return slotID;
|
|
}
|
|
|
|
int getSlotListIndex() {
|
|
if ((slotID == -1) && (slotListIndex == -1)) {
|
|
// if neither is set, default to first slot
|
|
return 0;
|
|
} else {
|
|
return slotListIndex;
|
|
}
|
|
}
|
|
|
|
boolean getShowInfo() {
|
|
return (SunPKCS11.debug != null) || showInfo;
|
|
}
|
|
|
|
TemplateManager getTemplateManager() {
|
|
if (templateManager == null) {
|
|
templateManager = new TemplateManager();
|
|
}
|
|
return templateManager;
|
|
}
|
|
|
|
boolean isEnabled(long m) {
|
|
if (enabledMechanisms != null) {
|
|
return enabledMechanisms.contains(Long.valueOf(m));
|
|
}
|
|
if (disabledMechanisms != null) {
|
|
return !disabledMechanisms.contains(Long.valueOf(m));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int getHandleStartupErrors() {
|
|
return handleStartupErrors;
|
|
}
|
|
|
|
boolean getKeyStoreCompatibilityMode() {
|
|
return keyStoreCompatibilityMode;
|
|
}
|
|
|
|
boolean getExplicitCancel() {
|
|
return explicitCancel;
|
|
}
|
|
|
|
boolean getDestroyTokenAfterLogout() {
|
|
return destroyTokenAfterLogout;
|
|
}
|
|
|
|
int getResourceCleanerShortInterval() {
|
|
return resourceCleanerShortInterval;
|
|
}
|
|
|
|
int getResourceCleanerLongInterval() {
|
|
return resourceCleanerLongInterval;
|
|
}
|
|
|
|
int getInsertionCheckInterval() {
|
|
return insertionCheckInterval;
|
|
}
|
|
|
|
boolean getOmitInitialize() {
|
|
return omitInitialize;
|
|
}
|
|
|
|
boolean getAllowSingleThreadedModules() {
|
|
return staticAllowSingleThreadedModules && allowSingleThreadedModules;
|
|
}
|
|
|
|
String getFunctionList() {
|
|
if (functionList == null) {
|
|
// defaults to "C_GetFunctionList" for NSS secmod
|
|
if (nssUseSecmod || nssUseSecmodTrust) {
|
|
return "C_GetFunctionList";
|
|
}
|
|
}
|
|
return functionList;
|
|
}
|
|
|
|
boolean getNssUseSecmod() {
|
|
return nssUseSecmod;
|
|
}
|
|
|
|
String getNssLibraryDirectory() {
|
|
return nssLibraryDirectory;
|
|
}
|
|
|
|
String getNssSecmodDirectory() {
|
|
return nssSecmodDirectory;
|
|
}
|
|
|
|
String getNssModule() {
|
|
return nssModule;
|
|
}
|
|
|
|
Secmod.DbMode getNssDbMode() {
|
|
return nssDbMode;
|
|
}
|
|
|
|
public boolean getNssNetscapeDbWorkaround() {
|
|
return nssUseSecmod && nssNetscapeDbWorkaround;
|
|
}
|
|
|
|
String getNssArgs() {
|
|
return nssArgs;
|
|
}
|
|
|
|
boolean getNssUseSecmodTrust() {
|
|
return nssUseSecmodTrust;
|
|
}
|
|
|
|
boolean getUseEcX963Encoding() {
|
|
return useEcX963Encoding;
|
|
}
|
|
|
|
boolean getNssOptimizeSpace() {
|
|
return nssOptimizeSpace;
|
|
}
|
|
|
|
private static String expand(final String s) throws IOException {
|
|
try {
|
|
return PropertyExpander.expand(s);
|
|
} catch (Exception e) {
|
|
throw new RuntimeException(e.getMessage(), e);
|
|
}
|
|
}
|
|
|
|
private void setupTokenizer() {
|
|
st.resetSyntax();
|
|
st.wordChars('a', 'z');
|
|
st.wordChars('A', 'Z');
|
|
st.wordChars('0', '9');
|
|
st.wordChars(':', ':');
|
|
st.wordChars('.', '.');
|
|
st.wordChars('_', '_');
|
|
st.wordChars('-', '-');
|
|
st.wordChars('/', '/');
|
|
st.wordChars('\\', '\\');
|
|
st.wordChars('$', '$');
|
|
st.wordChars('{', '{'); // need {} for property subst
|
|
st.wordChars('}', '}');
|
|
st.wordChars('*', '*');
|
|
st.wordChars('+', '+');
|
|
st.wordChars('~', '~');
|
|
// XXX check ASCII table and add all other characters except special
|
|
|
|
// special: #="(),
|
|
st.whitespaceChars(0, ' ');
|
|
st.commentChar('#');
|
|
st.eolIsSignificant(true);
|
|
st.quoteChar('\"');
|
|
}
|
|
|
|
private ConfigurationException excToken(String msg) {
|
|
return new ConfigurationException(msg + " " + st);
|
|
}
|
|
|
|
private ConfigurationException excLine(String msg) {
|
|
return new ConfigurationException(msg + ", line " + st.lineno());
|
|
}
|
|
|
|
private ConfigurationException excLine(String msg, Throwable e) {
|
|
return new ConfigurationException(msg + ", line " + st.lineno(), e);
|
|
}
|
|
|
|
private void parse() throws IOException {
|
|
while (true) {
|
|
int token = nextToken();
|
|
if (token == TT_EOF) {
|
|
break;
|
|
}
|
|
if (token == TT_EOL) {
|
|
continue;
|
|
}
|
|
if (token != TT_WORD) {
|
|
throw excToken("Unexpected token:");
|
|
}
|
|
switch (st.sval) {
|
|
case "name"->
|
|
name = parseStringEntry(st.sval);
|
|
case "library"->
|
|
library = parseLibrary(st.sval);
|
|
case "description"->
|
|
parseDescription(st.sval);
|
|
case "slot"->
|
|
parseSlotID(st.sval);
|
|
case "slotListIndex"->
|
|
parseSlotListIndex(st.sval);
|
|
case "enabledMechanisms"->
|
|
parseEnabledMechanisms(st.sval);
|
|
case "disabledMechanisms"->
|
|
parseDisabledMechanisms(st.sval);
|
|
case "attributes"->
|
|
parseAttributes(st.sval);
|
|
case "handleStartupErrors"->
|
|
parseHandleStartupErrors(st.sval);
|
|
case "insertionCheckInterval"-> {
|
|
insertionCheckInterval = parseIntegerEntry(st.sval);
|
|
if (insertionCheckInterval < 100) {
|
|
throw excLine(st.sval + " must be at least 100 ms");
|
|
}
|
|
}
|
|
case "cleaner.shortInterval"-> {
|
|
resourceCleanerShortInterval = parseIntegerEntry(st.sval);
|
|
if (resourceCleanerShortInterval < 1_000) {
|
|
throw excLine(st.sval + " must be at least 1000 ms");
|
|
}
|
|
}
|
|
case "cleaner.longInterval"-> {
|
|
resourceCleanerLongInterval = parseIntegerEntry(st.sval);
|
|
if (resourceCleanerLongInterval < 1_000) {
|
|
throw excLine(st.sval + " must be at least 1000 ms");
|
|
}
|
|
}
|
|
case "destroyTokenAfterLogout"->
|
|
destroyTokenAfterLogout = parseBooleanEntry(st.sval);
|
|
case "showInfo"->
|
|
showInfo = parseBooleanEntry(st.sval);
|
|
case "keyStoreCompatibilityMode"->
|
|
keyStoreCompatibilityMode = parseBooleanEntry(st.sval);
|
|
case "explicitCancel"->
|
|
explicitCancel = parseBooleanEntry(st.sval);
|
|
case "omitInitialize"->
|
|
omitInitialize = parseBooleanEntry(st.sval);
|
|
case "allowSingleThreadedModules"->
|
|
allowSingleThreadedModules = parseBooleanEntry(st.sval);
|
|
case "functionList"->
|
|
functionList = parseStringEntry(st.sval);
|
|
case "nssUseSecmod"->
|
|
nssUseSecmod = parseBooleanEntry(st.sval);
|
|
case "nssLibraryDirectory"-> {
|
|
nssLibraryDirectory = parseLibrary(st.sval);
|
|
nssUseSecmod = true;
|
|
}
|
|
case "nssSecmodDirectory"-> {
|
|
nssSecmodDirectory = expand(parseStringEntry(st.sval));
|
|
nssUseSecmod = true;
|
|
}
|
|
case "nssModule"-> {
|
|
nssModule = parseStringEntry(st.sval);
|
|
nssUseSecmod = true;
|
|
}
|
|
case "nssDbMode"-> {
|
|
String mode = parseStringEntry(st.sval);
|
|
if (mode.equals("readWrite")) {
|
|
nssDbMode = Secmod.DbMode.READ_WRITE;
|
|
} else if (mode.equals("readOnly")) {
|
|
nssDbMode = Secmod.DbMode.READ_ONLY;
|
|
} else if (mode.equals("noDb")) {
|
|
nssDbMode = Secmod.DbMode.NO_DB;
|
|
} else {
|
|
throw excToken("nssDbMode must be one of readWrite, readOnly, and noDb:");
|
|
}
|
|
nssUseSecmod = true;
|
|
}
|
|
case "nssNetscapeDbWorkaround"-> {
|
|
nssNetscapeDbWorkaround = parseBooleanEntry(st.sval);
|
|
nssUseSecmod = true;
|
|
}
|
|
case "nssArgs"->
|
|
parseNSSArgs(st.sval);
|
|
case "nssUseSecmodTrust"->
|
|
nssUseSecmodTrust = parseBooleanEntry(st.sval);
|
|
case "useEcX963Encoding"->
|
|
useEcX963Encoding = parseBooleanEntry(st.sval);
|
|
case "nssOptimizeSpace"->
|
|
nssOptimizeSpace = parseBooleanEntry(st.sval);
|
|
default->
|
|
throw new ConfigurationException
|
|
("Unknown keyword '" + st.sval + "', line " +
|
|
st.lineno());
|
|
}
|
|
parsedKeywords.add(st.sval);
|
|
}
|
|
reader.close();
|
|
reader = null;
|
|
st = null;
|
|
parsedKeywords = null;
|
|
if (name == null) {
|
|
throw new ConfigurationException("name must be specified");
|
|
}
|
|
if (nssUseSecmod == false) {
|
|
if (library == null) {
|
|
throw new ConfigurationException("library must be specified");
|
|
}
|
|
} else {
|
|
if (library != null) {
|
|
throw new ConfigurationException
|
|
("library must not be specified in NSS mode");
|
|
}
|
|
if ((slotID != -1) || (slotListIndex != -1)) {
|
|
throw new ConfigurationException
|
|
("slot and slotListIndex must not be specified in NSS mode");
|
|
}
|
|
if (nssArgs != null) {
|
|
throw new ConfigurationException
|
|
("nssArgs must not be specified in NSS mode");
|
|
}
|
|
if (nssUseSecmodTrust != false) {
|
|
throw new ConfigurationException("nssUseSecmodTrust is an "
|
|
+ "internal option and must not be specified in NSS mode");
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Parsing helper methods
|
|
//
|
|
|
|
private int nextToken() throws IOException {
|
|
int token = st.nextToken();
|
|
if (DEBUG) {
|
|
System.out.println(st);
|
|
}
|
|
return token;
|
|
}
|
|
|
|
private void parseEquals() throws IOException {
|
|
int token = nextToken();
|
|
if (token != '=') {
|
|
throw excToken("Expected '=', read");
|
|
}
|
|
}
|
|
|
|
private void parseOpenBraces() throws IOException {
|
|
while (true) {
|
|
int token = nextToken();
|
|
if (token == TT_EOL) {
|
|
continue;
|
|
}
|
|
if ((token == TT_WORD) && st.sval.equals("{")) {
|
|
return;
|
|
}
|
|
throw excToken("Expected '{', read");
|
|
}
|
|
}
|
|
|
|
private boolean isCloseBraces(int token) {
|
|
return (token == TT_WORD) && st.sval.equals("}");
|
|
}
|
|
|
|
private String parseWord() throws IOException {
|
|
int token = nextToken();
|
|
if (token != TT_WORD) {
|
|
throw excToken("Unexpected value:");
|
|
}
|
|
return st.sval;
|
|
}
|
|
|
|
private String parseStringEntry(String keyword) throws IOException {
|
|
checkDup(keyword);
|
|
parseEquals();
|
|
|
|
int token = nextToken();
|
|
if (token != TT_WORD && token != '\"') {
|
|
// not a word token nor a string enclosed by double quotes
|
|
throw excToken("Unexpected value:");
|
|
}
|
|
String value = st.sval;
|
|
|
|
if (DEBUG) {
|
|
System.out.println(keyword + ": " + value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
private boolean parseBooleanEntry(String keyword) throws IOException {
|
|
checkDup(keyword);
|
|
parseEquals();
|
|
boolean value = parseBoolean();
|
|
if (DEBUG) {
|
|
System.out.println(keyword + ": " + value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
private int parseIntegerEntry(String keyword) throws IOException {
|
|
checkDup(keyword);
|
|
parseEquals();
|
|
int value = decodeNumber(parseWord());
|
|
if (DEBUG) {
|
|
System.out.println(keyword + ": " + value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
private boolean parseBoolean() throws IOException {
|
|
String val = parseWord();
|
|
switch (val) {
|
|
case "true":
|
|
return true;
|
|
case "false":
|
|
return false;
|
|
default:
|
|
throw excToken("Expected boolean value, read:");
|
|
}
|
|
}
|
|
|
|
private String parseLine() throws IOException {
|
|
// allow quoted string as part of line
|
|
String s = null;
|
|
while (true) {
|
|
int token = nextToken();
|
|
if ((token == TT_EOL) || (token == TT_EOF)) {
|
|
break;
|
|
}
|
|
if (token != TT_WORD && token != '\"') {
|
|
throw excToken("Unexpected value");
|
|
}
|
|
if (s == null) {
|
|
s = st.sval;
|
|
} else {
|
|
s = s + " " + st.sval;
|
|
}
|
|
}
|
|
if (s == null) {
|
|
throw excToken("Unexpected empty line");
|
|
}
|
|
return s;
|
|
}
|
|
|
|
private int decodeNumber(String str) throws IOException {
|
|
try {
|
|
if (str.startsWith("0x") || str.startsWith("0X")) {
|
|
return Integer.parseInt(str.substring(2), 16);
|
|
} else {
|
|
return Integer.parseInt(str);
|
|
}
|
|
} catch (NumberFormatException e) {
|
|
throw excToken("Expected number, read");
|
|
}
|
|
}
|
|
|
|
private static boolean isNumber(String s) {
|
|
if (s.length() == 0) {
|
|
return false;
|
|
}
|
|
char ch = s.charAt(0);
|
|
return ((ch >= '0') && (ch <= '9'));
|
|
}
|
|
|
|
private void parseComma() throws IOException {
|
|
int token = nextToken();
|
|
if (token != ',') {
|
|
throw excToken("Expected ',', read");
|
|
}
|
|
}
|
|
|
|
private static boolean isByteArray(String val) {
|
|
return val.startsWith("0h");
|
|
}
|
|
|
|
private byte[] decodeByteArray(String str) throws IOException {
|
|
if (str.startsWith("0h") == false) {
|
|
throw excToken("Expected byte array value, read");
|
|
}
|
|
str = str.substring(2);
|
|
// XXX proper hex parsing
|
|
try {
|
|
return new BigInteger(str, 16).toByteArray();
|
|
} catch (NumberFormatException e) {
|
|
throw excToken("Expected byte array value, read");
|
|
}
|
|
}
|
|
|
|
private void checkDup(String keyword) throws IOException {
|
|
if (parsedKeywords.contains(keyword)) {
|
|
throw excLine(keyword + " must only be specified once");
|
|
}
|
|
}
|
|
|
|
//
|
|
// individual entry parsing methods
|
|
//
|
|
|
|
private String parseLibrary(String keyword) throws IOException {
|
|
checkDup(keyword);
|
|
parseEquals();
|
|
String lib = parseLine();
|
|
lib = expand(lib);
|
|
int i = lib.indexOf("/$ISA/");
|
|
if (i != -1) {
|
|
// replace "/$ISA/" with "/"
|
|
String prefix = lib.substring(0, i);
|
|
String suffix = lib.substring(i + 5);
|
|
lib = prefix + suffix;
|
|
}
|
|
if (DEBUG) {
|
|
System.out.println(keyword + ": " + lib);
|
|
}
|
|
|
|
// Check to see if full path is specified to prevent the DLL
|
|
// preloading attack
|
|
if (!(new File(lib)).isAbsolute()) {
|
|
throw new ConfigurationException(
|
|
"Absolute path required for library value: " + lib);
|
|
}
|
|
return lib;
|
|
}
|
|
|
|
private void parseDescription(String keyword) throws IOException {
|
|
checkDup(keyword);
|
|
parseEquals();
|
|
description = parseLine();
|
|
if (DEBUG) {
|
|
System.out.println("description: " + description);
|
|
}
|
|
}
|
|
|
|
private void parseSlotID(String keyword) throws IOException {
|
|
if (slotID >= 0) {
|
|
throw excLine("Duplicate slot definition");
|
|
}
|
|
if (slotListIndex >= 0) {
|
|
throw excLine
|
|
("Only one of slot and slotListIndex must be specified");
|
|
}
|
|
parseEquals();
|
|
String slotString = parseWord();
|
|
slotID = decodeNumber(slotString);
|
|
if (DEBUG) {
|
|
System.out.println("slot: " + slotID);
|
|
}
|
|
}
|
|
|
|
private void parseSlotListIndex(String keyword) throws IOException {
|
|
if (slotListIndex >= 0) {
|
|
throw excLine("Duplicate slotListIndex definition");
|
|
}
|
|
if (slotID >= 0) {
|
|
throw excLine
|
|
("Only one of slot and slotListIndex must be specified");
|
|
}
|
|
parseEquals();
|
|
String slotString = parseWord();
|
|
slotListIndex = decodeNumber(slotString);
|
|
if (DEBUG) {
|
|
System.out.println("slotListIndex: " + slotListIndex);
|
|
}
|
|
}
|
|
|
|
private void parseEnabledMechanisms(String keyword) throws IOException {
|
|
enabledMechanisms = parseMechanisms(keyword);
|
|
}
|
|
|
|
private void parseDisabledMechanisms(String keyword) throws IOException {
|
|
disabledMechanisms = parseMechanisms(keyword);
|
|
}
|
|
|
|
private Set<Long> parseMechanisms(String keyword) throws IOException {
|
|
checkDup(keyword);
|
|
Set<Long> mechs = new HashSet<Long>();
|
|
parseEquals();
|
|
parseOpenBraces();
|
|
while (true) {
|
|
int token = nextToken();
|
|
if (isCloseBraces(token)) {
|
|
break;
|
|
}
|
|
if (token == TT_EOL) {
|
|
continue;
|
|
}
|
|
if (token != TT_WORD) {
|
|
throw excToken("Expected mechanism, read");
|
|
}
|
|
long mech = parseMechanism(st.sval);
|
|
mechs.add(Long.valueOf(mech));
|
|
}
|
|
if (DEBUG) {
|
|
System.out.print("mechanisms: [");
|
|
for (Long mech : mechs) {
|
|
System.out.print(Functions.getMechanismName(mech));
|
|
System.out.print(", ");
|
|
}
|
|
System.out.println("]");
|
|
}
|
|
return mechs;
|
|
}
|
|
|
|
private long parseMechanism(String mech) throws IOException {
|
|
if (isNumber(mech)) {
|
|
return decodeNumber(mech);
|
|
} else {
|
|
try {
|
|
return Functions.getMechanismId(mech);
|
|
} catch (IllegalArgumentException e) {
|
|
throw excLine("Unknown mechanism: " + mech, e);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void parseAttributes(String keyword) throws IOException {
|
|
if (templateManager == null) {
|
|
templateManager = new TemplateManager();
|
|
}
|
|
int token = nextToken();
|
|
if (token == '=') {
|
|
String s = parseWord();
|
|
if (s.equals("compatibility") == false) {
|
|
throw excLine("Expected 'compatibility', read " + s);
|
|
}
|
|
setCompatibilityAttributes();
|
|
return;
|
|
}
|
|
if (token != '(') {
|
|
throw excToken("Expected '(' or '=', read");
|
|
}
|
|
String op = parseOperation();
|
|
parseComma();
|
|
long objectClass = parseObjectClass();
|
|
parseComma();
|
|
long keyAlg = parseKeyAlgorithm();
|
|
token = nextToken();
|
|
if (token != ')') {
|
|
throw excToken("Expected ')', read");
|
|
}
|
|
parseEquals();
|
|
parseOpenBraces();
|
|
List<CK_ATTRIBUTE> attributes = new ArrayList<CK_ATTRIBUTE>();
|
|
while (true) {
|
|
token = nextToken();
|
|
if (isCloseBraces(token)) {
|
|
break;
|
|
}
|
|
if (token == TT_EOL) {
|
|
continue;
|
|
}
|
|
if (token != TT_WORD) {
|
|
throw excToken("Expected mechanism, read");
|
|
}
|
|
String attributeName = st.sval;
|
|
long attributeId = decodeAttributeName(attributeName);
|
|
parseEquals();
|
|
String attributeValue = parseWord();
|
|
attributes.add(decodeAttributeValue(attributeId, attributeValue));
|
|
}
|
|
templateManager.addTemplate
|
|
(op, objectClass, keyAlg, attributes.toArray(CK_A0));
|
|
}
|
|
|
|
private void setCompatibilityAttributes() {
|
|
// all secret keys
|
|
templateManager.addTemplate(O_ANY, CKO_SECRET_KEY, PCKK_ANY,
|
|
new CK_ATTRIBUTE[] {
|
|
TOKEN_FALSE,
|
|
SENSITIVE_FALSE,
|
|
EXTRACTABLE_TRUE,
|
|
ENCRYPT_TRUE,
|
|
DECRYPT_TRUE,
|
|
WRAP_TRUE,
|
|
UNWRAP_TRUE,
|
|
});
|
|
|
|
// generic secret keys are special
|
|
// They are used as MAC keys plus for the SSL/TLS (pre)master secrets
|
|
templateManager.addTemplate(O_ANY, CKO_SECRET_KEY, CKK_GENERIC_SECRET,
|
|
new CK_ATTRIBUTE[] {
|
|
SIGN_TRUE,
|
|
VERIFY_TRUE,
|
|
ENCRYPT_NULL,
|
|
DECRYPT_NULL,
|
|
WRAP_NULL,
|
|
UNWRAP_NULL,
|
|
DERIVE_TRUE,
|
|
});
|
|
|
|
// all private and public keys
|
|
templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, PCKK_ANY,
|
|
new CK_ATTRIBUTE[] {
|
|
TOKEN_FALSE,
|
|
SENSITIVE_FALSE,
|
|
EXTRACTABLE_TRUE,
|
|
});
|
|
templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, PCKK_ANY,
|
|
new CK_ATTRIBUTE[] {
|
|
TOKEN_FALSE,
|
|
});
|
|
|
|
// additional attributes for RSA private keys
|
|
templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_RSA,
|
|
new CK_ATTRIBUTE[] {
|
|
DECRYPT_TRUE,
|
|
SIGN_TRUE,
|
|
SIGN_RECOVER_TRUE,
|
|
UNWRAP_TRUE,
|
|
});
|
|
// additional attributes for RSA public keys
|
|
templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, CKK_RSA,
|
|
new CK_ATTRIBUTE[] {
|
|
ENCRYPT_TRUE,
|
|
VERIFY_TRUE,
|
|
VERIFY_RECOVER_TRUE,
|
|
WRAP_TRUE,
|
|
});
|
|
|
|
// additional attributes for DSA private keys
|
|
templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_DSA,
|
|
new CK_ATTRIBUTE[] {
|
|
SIGN_TRUE,
|
|
});
|
|
// additional attributes for DSA public keys
|
|
templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, CKK_DSA,
|
|
new CK_ATTRIBUTE[] {
|
|
VERIFY_TRUE,
|
|
});
|
|
|
|
// additional attributes for DH private keys
|
|
templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_DH,
|
|
new CK_ATTRIBUTE[] {
|
|
DERIVE_TRUE,
|
|
});
|
|
|
|
// additional attributes for EC private keys
|
|
templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_EC,
|
|
new CK_ATTRIBUTE[] {
|
|
SIGN_TRUE,
|
|
DERIVE_TRUE,
|
|
});
|
|
// additional attributes for EC public keys
|
|
templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, CKK_EC,
|
|
new CK_ATTRIBUTE[] {
|
|
VERIFY_TRUE,
|
|
});
|
|
}
|
|
|
|
private static final CK_ATTRIBUTE[] CK_A0 = new CK_ATTRIBUTE[0];
|
|
|
|
private String parseOperation() throws IOException {
|
|
String op = parseWord();
|
|
switch (op) {
|
|
case "*":
|
|
return TemplateManager.O_ANY;
|
|
case "generate":
|
|
return TemplateManager.O_GENERATE;
|
|
case "import":
|
|
return TemplateManager.O_IMPORT;
|
|
default:
|
|
throw excLine("Unknown operation " + op);
|
|
}
|
|
}
|
|
|
|
private long parseObjectClass() throws IOException {
|
|
String name = parseWord();
|
|
try {
|
|
return Functions.getObjectClassId(name);
|
|
} catch (IllegalArgumentException e) {
|
|
throw excLine("Unknown object class " + name, e);
|
|
}
|
|
}
|
|
|
|
private long parseKeyAlgorithm() throws IOException {
|
|
String name = parseWord();
|
|
if (isNumber(name)) {
|
|
return decodeNumber(name);
|
|
} else {
|
|
try {
|
|
return Functions.getKeyId(name);
|
|
} catch (IllegalArgumentException e) {
|
|
throw excLine("Unknown key algorithm " + name, e);
|
|
}
|
|
}
|
|
}
|
|
|
|
private long decodeAttributeName(String name) throws IOException {
|
|
if (isNumber(name)) {
|
|
return decodeNumber(name);
|
|
} else {
|
|
try {
|
|
return Functions.getAttributeId(name);
|
|
} catch (IllegalArgumentException e) {
|
|
throw excLine("Unknown attribute name " + name, e);
|
|
}
|
|
}
|
|
}
|
|
|
|
private CK_ATTRIBUTE decodeAttributeValue(long id, String value)
|
|
throws IOException {
|
|
if (value.equals("null")) {
|
|
return new CK_ATTRIBUTE(id);
|
|
} else if (value.equals("true")) {
|
|
return new CK_ATTRIBUTE(id, true);
|
|
} else if (value.equals("false")) {
|
|
return new CK_ATTRIBUTE(id, false);
|
|
} else if (isByteArray(value)) {
|
|
return new CK_ATTRIBUTE(id, decodeByteArray(value));
|
|
} else if (isNumber(value)) {
|
|
return new CK_ATTRIBUTE(id, Integer.valueOf(decodeNumber(value)));
|
|
} else {
|
|
throw excLine("Unknown attribute value " + value);
|
|
}
|
|
}
|
|
|
|
private void parseNSSArgs(String keyword) throws IOException {
|
|
checkDup(keyword);
|
|
parseEquals();
|
|
int token = nextToken();
|
|
if (token != '"') {
|
|
throw excToken("Expected quoted string");
|
|
}
|
|
nssArgs = expand(st.sval);
|
|
if (DEBUG) {
|
|
System.out.println("nssArgs: " + nssArgs);
|
|
}
|
|
}
|
|
|
|
private void parseHandleStartupErrors(String keyword) throws IOException {
|
|
checkDup(keyword);
|
|
parseEquals();
|
|
String val = parseWord();
|
|
if (val.equals("ignoreAll")) {
|
|
handleStartupErrors = ERR_IGNORE_ALL;
|
|
} else if (val.equals("ignoreMissingLibrary")) {
|
|
handleStartupErrors = ERR_IGNORE_LIB;
|
|
} else if (val.equals("halt")) {
|
|
handleStartupErrors = ERR_HALT;
|
|
} else {
|
|
throw excToken("Invalid value for handleStartupErrors:");
|
|
}
|
|
if (DEBUG) {
|
|
System.out.println("handleStartupErrors: " + handleStartupErrors);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
class ConfigurationException extends IOException {
|
|
private static final long serialVersionUID = 254492758807673194L;
|
|
ConfigurationException(String msg) {
|
|
super(msg);
|
|
}
|
|
|
|
ConfigurationException(String msg, Throwable e) {
|
|
super(msg, e);
|
|
}
|
|
}
|