/*
*
* Copyright (c) 2013 - 2017 Lijun Liao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3
* as published by the Free Software Foundation with the addition of the
* following permission added to Section 15 as permitted in Section 7(a):
*
* FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
* THE AUTHOR LIJUN LIAO. LIJUN LIAO DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
* OF THIRD PARTY RIGHTS.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License.
*
* You can be released from the requirements of the license by purchasing
* a commercial license. Buying such a license is mandatory as soon as you
* develop commercial activities involving the XiPKI software without
* disclosing the source code of your own applications.
*
* For more information, please contact Lijun Liao at this
* address: lijun.liao@gmail.com
*/
package org.xipki.commons.security.pkcs11.emulator;
import java.io.File;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xipki.commons.common.util.IoUtil;
import org.xipki.commons.common.util.ParamUtil;
import org.xipki.commons.common.util.StringUtil;
import org.xipki.commons.password.PasswordResolverException;
import org.xipki.commons.security.exception.P11TokenException;
import org.xipki.commons.security.pkcs11.AbstractP11Module;
import org.xipki.commons.security.pkcs11.P11Module;
import org.xipki.commons.security.pkcs11.P11ModuleConf;
import org.xipki.commons.security.pkcs11.P11Slot;
import org.xipki.commons.security.pkcs11.P11SlotIdentifier;
/**
* @author Lijun Liao
* @since 2.0.0
*/
public class EmulatorP11Module extends AbstractP11Module {
public static final String PREFIX = "emulator:";
private static final Logger LOG = LoggerFactory.getLogger(EmulatorP11Module.class);
private EmulatorP11Module(final P11ModuleConf moduleConf) throws P11TokenException {
super(moduleConf);
final String modulePath = moduleConf.getNativeLibrary();
if (!StringUtil.startsWithIgnoreCase(modulePath, PREFIX)) {
throw new IllegalArgumentException("the module path does not starts with " + PREFIX
+ ": " + modulePath);
}
File baseDir = new File(IoUtil.expandFilepath(modulePath.substring(PREFIX.length())));
File[] children = baseDir.listFiles();
if (children == null || children.length == 0) {
LOG.error("found no slots");
setSlots(Collections.emptySet());
return;
}
Set<Integer> allSlotIndexes = new HashSet<>();
Set<Long> allSlotIdentifiers = new HashSet<>();
List<P11SlotIdentifier> slotIds = new LinkedList<>();
for (File child : children) {
if ((child.isDirectory() && child.canRead() && !child.exists())) {
LOG.warn("ignore path {}, it does not point to a readable exist directory",
child.getPath());
continue;
}
String filename = child.getName();
String[] tokens = filename.split("-");
if (tokens == null || tokens.length != 2) {
LOG.warn("ignore dir {}, invalid filename syntax", child.getPath());
continue;
}
int slotIndex;
long slotId;
try {
slotIndex = Integer.parseInt(tokens[0]);
slotId = Long.parseLong(tokens[1]);
} catch (NumberFormatException ex) {
LOG.warn("ignore dir {}, invalid filename syntax", child.getPath());
continue;
}
if (allSlotIndexes.contains(slotIndex)) {
LOG.error("ignore slot dir, the same slot index has been assigned", filename);
continue;
}
if (allSlotIdentifiers.contains(slotId)) {
LOG.error("ignore slot dir, the same slot identifier has been assigned", filename);
continue;
}
allSlotIndexes.add(slotIndex);
allSlotIdentifiers.add(slotId);
P11SlotIdentifier slotIdentifier = new P11SlotIdentifier(slotIndex, slotId);
if (!moduleConf.isSlotIncluded(slotIdentifier)) {
LOG.info("skipped slot {}", slotId);
continue;
}
slotIds.add(slotIdentifier);
} // end for
Set<P11Slot> slots = new HashSet<>();
for (P11SlotIdentifier slotId : slotIds) {
List<char[]> pwd;
try {
pwd = moduleConf.getPasswordRetriever().getPassword(slotId);
} catch (PasswordResolverException ex) {
throw new P11TokenException("PasswordResolverException: " + ex.getMessage(), ex);
}
File slotDir = new File(baseDir, slotId.getIndex() + "-" + slotId.getId());
if (pwd == null) {
throw new P11TokenException("no password is configured");
}
if (pwd.size() != 1) {
throw new P11TokenException(pwd.size()
+ " passwords are configured, but 1 is permitted");
}
PrivateKeyCryptor privateKeyCryptor = new PrivateKeyCryptor(pwd.get(0));
int maxSessions = 20;
P11Slot slot = new EmulatorP11Slot(moduleConf.getName(), slotDir, slotId,
moduleConf.isReadOnly(), privateKeyCryptor,
moduleConf.getP11MechanismFilter(), maxSessions);
slots.add(slot);
}
setSlots(slots);
} // constructor
public static P11Module getInstance(final P11ModuleConf moduleConf) throws P11TokenException {
ParamUtil.requireNonNull("moduleConf", moduleConf);
return new EmulatorP11Module(moduleConf);
}
@Override
public void close() {
LOG.info("close", "close pkcs11 module: {}", getName());
}
}