//* Licensed Materials - Property of *
//* IBM *
//* Alexandra Instituttet A/S *
//* *
//* eu.abc4trust.pabce.1.34 *
//* *
//* (C) Copyright IBM Corp. 2014. All Rights Reserved. *
//* (C) Copyright Alexandra Instituttet A/S, Denmark. 2014. All *
//* Rights Reserved. *
//* US Government Users Restricted Rights - Use, duplication or *
//* disclosure restricted by GSA ADP Schedule Contract with IBM Corp. *
//* *
//* This file is licensed under the Apache License, Version 2.0 (the *
//* "License"); you may not use this file except in compliance with *
//* the License. You may obtain a copy of the License at: *
//* http://www.apache.org/licenses/LICENSE-2.0 *
//* Unless required by applicable law or agreed to in writing, *
//* software distributed under the License is distributed on an *
//* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
//* KIND, either express or implied. See the License for the *
//* specific language governing permissions and limitations *
//* under the License. *
//*/**/****************************************************************
package eu.abc4trust.smartcardManager;
import java.math.BigInteger;
import java.net.URI;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.Map.Entry;
import com.google.inject.Inject;
import com.ibm.zurich.idmx.interfaces.device.DeviceProofCommitment;
import com.ibm.zurich.idmx.interfaces.device.DeviceProofResponse;
import com.ibm.zurich.idmx.interfaces.device.DeviceProofSpecification;
import com.ibm.zurich.idmx.interfaces.device.ExternalSecretsManager;
import eu.abc4trust.abce.internal.user.credentialManager.CredentialManager;
import eu.abc4trust.abce.internal.user.credentialManager.CredentialManagerException;
import eu.abc4trust.abce.internal.user.credentialManager.SecretNotInStorageException;
import eu.abc4trust.keyManager.KeyManager;
import eu.abc4trust.smartcard.BasicSmartcard;
import eu.abc4trust.smartcard.CardStorage;
import eu.abc4trust.smartcard.GroupParameters;
import eu.abc4trust.smartcard.HardwareSmartcard;
import eu.abc4trust.smartcard.InsufficientStorageException;
import eu.abc4trust.smartcard.SecretBasedSmartcard;
import eu.abc4trust.smartcard.SmartcardParameters;
import eu.abc4trust.smartcard.SmartcardStatusCode;
import eu.abc4trust.smartcard.SystemParameters;
import eu.abc4trust.smartcard.Utils;
import eu.abc4trust.smartcard.ZkProofCommitment;
import eu.abc4trust.smartcard.ZkProofResponse;
import eu.abc4trust.util.TimingsLogger;
import eu.abc4trust.xml.Secret;
public class ExternalSecretsManagerImpl implements ExternalSecretsManager{
private final CredentialManager credManager;
private final KeyManager keyManager;
private final CardStorage storage;
@Inject
public ExternalSecretsManagerImpl(CredentialManager credManager, KeyManager keyManager, CardStorage storage) {
this.keyManager = keyManager;
this.credManager = credManager;
this.storage = storage;
}
private void addSecret(String username, Secret s) {
// If secret is a real smartcard
// - Add one of Pascal's Smartcard interface
// - Where do we get the PIN from?
// If it's a "software" smartcard:
// - Restore state, add a new Software smartcard
if (s.getSecretDescription().isDeviceBoundSecret()) {
throw new UnsupportedOperationException("addSecret not implemented for device bound secrets");
} else {
SecretBasedSmartcard sc = new SecretBasedSmartcard(username, this.credManager, this.keyManager);
sc.initFromSecret(s);
this.storage.addSmartcard(sc, 0);
}
}
private void tryToLoadSecretBasedSmartcard(String username, URI smartcardUri) {
try {
Secret s = this.credManager.getSecret(username, smartcardUri);
if (s != null) {
this.addSecret(username, s);
}
} catch(SecretNotInStorageException ex) {
return;
} catch(CredentialManagerException ex) {
throw new RuntimeException(ex);
}
}
private BasicSmartcard getSmartcard(String username, URI smartcardUri) {
BasicSmartcard sc = this.storage.getSmartcard(smartcardUri);
if (sc == null) {
this.tryToLoadSecretBasedSmartcard(username, smartcardUri);
sc = this.storage.getSmartcard(smartcardUri);
}
return sc;
}
@Override
public void allocateCredential(String username, URI deviceUid, URI newCredentialUri,
URI issuerUri, boolean overwrite) {
BasicSmartcard s = this.getSmartcard(username, deviceUid);
if(s == null) {
throw new RuntimeException("Unknown smartcard: " + deviceUid);
}
Integer pin = this.storage.getPin(deviceUid);
if(this.doesCredentialExists(username, deviceUid, newCredentialUri)) {
if(overwrite) {
SmartcardStatusCode ret = s.deleteCredential(pin, newCredentialUri);
if (ret != SmartcardStatusCode.OK) {
throw new RuntimeException("Credential " + newCredentialUri + " already exists on card "
+ deviceUid + ". Could not delete it, status code: " + ret.ordinal() + " " +
ret.name());
}
} else {
throw new RuntimeException("Credential " + newCredentialUri + " already exists on card "
+ deviceUid + " (set overwrite=true to overwrite)");
}
}
SmartcardStatusCode ret = s.allocateCredential(pin, newCredentialUri, issuerUri);
if (ret != SmartcardStatusCode.OK) {
throw new InsufficientStorageException("Credential " + newCredentialUri + " could not be created on card "
+ deviceUid + ". Status code: " + ret.ordinal() + " " + ret.name());
}
}
@Override
public boolean isDeviceLoaded(String username, URI deviceUid) {
BasicSmartcard sc = this.getSmartcard(username, deviceUid);
return sc != null;
}
@Override
public boolean doesCredentialExists(String username, URI deviceUid, URI credentialUri) {
System.out.println("sc uri: " + deviceUid + "\n credUri: " + credentialUri);
BasicSmartcard s = this.getSmartcard(username, deviceUid);
if(s == null) {
return false;
}
Integer pin = this.storage.getPin(deviceUid);
return s.credentialExists(pin, credentialUri);
}
@Override
public BigInteger getPublicKeyBase(String username, URI deviceUid) {
BasicSmartcard s = this.getSmartcard(username, deviceUid);
if(s == null) {
throw new RuntimeException("Unknown smartcard: " + deviceUid);
}
Integer pin = this.storage.getPin(deviceUid);
return s.getSystemParameters(pin).g;
}
@Override
public BigInteger getPseudonymModulus(String username, URI deviceUid) {
BasicSmartcard s = this.getSmartcard(username, deviceUid);
if(s == null) {
throw new RuntimeException("Unknown smartcard: " + deviceUid);
}
Integer pin = this.storage.getPin(deviceUid);
return s.getSystemParameters(pin).p;
}
@Override
public BigInteger getPseudonymSubgroupOrder(String username, URI deviceUid) {
BasicSmartcard s = this.getSmartcard(username, deviceUid);
if(s == null) {
throw new RuntimeException("Unknown smartcard: " + deviceUid);
}
Integer pin = this.storage.getPin(deviceUid);
return s.getSystemParameters(pin).subgroupOrder;
}
@Override
public BigInteger getBaseForDeviceSecret(String username, URI deviceUid, URI credentialUri) {
BasicSmartcard s = this.getSmartcard(username, deviceUid);
if(s == null) {
throw new RuntimeException("Unknown smartcard: " + deviceUid);
}
Integer pin = this.storage.getPin(deviceUid);
SmartcardParameters groupParams = s.getIssuerParametersOfCredential(pin, credentialUri).groupParams;
return groupParams.getBaseForDeviceSecret();
}
@Override
public BigInteger getBaseForCredentialSecret(String username, URI deviceUid,
URI credentialUri) {
BasicSmartcard s = this.getSmartcard(username, deviceUid);
if(s == null) {
throw new RuntimeException("Unknown smartcard: " + deviceUid);
}
Integer pin = this.storage.getPin(deviceUid);
SmartcardParameters groupParams = s.getIssuerParametersOfCredential(pin, credentialUri).groupParams;
BigInteger ret = groupParams.getBaseForCredentialSecretOrNull();
if(ret == null) {
return BigInteger.ONE;
} else {
return ret;
}
}
@Override
public BigInteger getModulus(String username, URI deviceUid, URI credentialUri) {
BasicSmartcard s = this.getSmartcard(username, deviceUid);
if(s == null) {
throw new RuntimeException("Unknown smartcard: " + deviceUid);
}
Integer pin = this.storage.getPin(deviceUid);
return s.getIssuerParametersOfCredential(pin, credentialUri).groupParams.getModulus();
}
@Override
public int getChallengeSizeBytes(String username, URI deviceUid) {
Set<Integer> challengeSizes = new HashSet<Integer>();
for(Entry<URI, BasicSmartcard> s: this.storage.getSmartcards().entrySet()) {
URI key = s.getKey();
Integer pin = this.storage.getPin(key);
BasicSmartcard value = s.getValue();
SystemParameters systemParameters = value.getSystemParameters(pin);
challengeSizes.add(systemParameters.zkChallengeSizeBytes);
}
if(challengeSizes.size() == 1) {
return challengeSizes.iterator().next();
} else if (challengeSizes.size() == 0) {
throw new RuntimeException("No cards loaded");
} else {
throw new RuntimeException("Incompatible cards: challenge size is different");
}
}
@Override
public int getRandomizerSizeBytes(String username, URI deviceUid) {
Set<Integer> randSizes = new HashSet<Integer>();
for(Entry<URI, BasicSmartcard> s: this.storage.getSmartcards().entrySet()) {
Integer pin = this.storage.getPin(s.getKey());
SystemParameters systemParameters = s.getValue().getSystemParameters(pin);
int randValue = systemParameters.zkChallengeSizeBytes+systemParameters.zkStatisticalHidingSizeBytes+
systemParameters.deviceSecretSizeBytes;
randSizes.add(randValue);
}
if(randSizes.size() == 1) {
return randSizes.iterator().next();
} else if (randSizes.size() == 0) {
throw new RuntimeException("No cards loaded");
} else {
throw new RuntimeException("Incompatible cards: statistical hiding size is different");
}
}
@Override
public int getAttributeSizeBytes(String username, URI deviceUid) {
Set<Integer> attrSizes = new HashSet<Integer>();
for(Entry<URI, BasicSmartcard> s: this.storage.getSmartcards().entrySet()) {
Integer pin = this.storage.getPin(s.getKey());
SystemParameters systemParameters = s.getValue().getSystemParameters(pin);
int randValue = systemParameters.deviceSecretSizeBytes;
attrSizes.add(randValue);
}
if(attrSizes.size() == 1) {
return attrSizes.iterator().next();
} else if (attrSizes.size() == 0) {
throw new RuntimeException("No cards loaded");
} else {
throw new RuntimeException("Incompatible cards: statistical hiding size is different");
}
}
@Override
public BigInteger getDevicePublicKey(String username, URI deviceUid) {
BasicSmartcard s = this.getSmartcard(username, deviceUid);
if(s == null) {
throw new RuntimeException("Unknown smartcard: " + deviceUid);
}
Integer pin = this.storage.getPin(deviceUid);
return s.computeDevicePublicKey(pin);
}
@Override
public BigInteger getCredentialPublicKey(String username, URI deviceUid, URI credentialUri) {
BasicSmartcard s = this.getSmartcard(username, deviceUid);
if(s == null) {
throw new RuntimeException("Unknown smartcard: " + deviceUid);
}
Integer pin = this.storage.getPin(deviceUid);
return s.computeCredentialFragment(pin, credentialUri);
}
@Override
public BigInteger getScopeExclusivePseudonym(String username, URI deviceUid, URI scope) {
BasicSmartcard s = this.getSmartcard(username, deviceUid);
if(s == null) {
throw new RuntimeException("Unknown smartcard: " + deviceUid);
}
Integer pin = this.storage.getPin(deviceUid);
return s.computeScopeExclusivePseudonym(pin, scope);
}
@Override
public BigInteger getBaseForScopeExclusivePseudonym(String username, URI scope,
BigInteger modulus, BigInteger subgroupOrder) {
return Utils.baseForScopeExclusivePseudonym(scope, modulus, subgroupOrder);
}
@Override
public BigInteger getBaseForScopeExclusivePseudonym(String username, URI deviceUid, URI scope) {
return this.getBaseForScopeExclusivePseudonym(username, scope, this.getPseudonymModulus(username, deviceUid),
this.getPseudonymSubgroupOrder(username, deviceUid));
}
@Override
public DeviceProofCommitment getPresentationCommitment(
DeviceProofSpecification spec) {
if(spec instanceof DeviceProofSpecificationImpl) {
TimingsLogger.logTiming("ExternalSecretsManager.getPresentationCommitment", true);
DeviceProofSpecificationImpl proofSpec = (DeviceProofSpecificationImpl) spec;
DeviceProofCommitmentImpl com = new DeviceProofCommitmentImpl(proofSpec);
Set<URI> involvedSmartcards = proofSpec.computeListOfInvolvedSmartcards();
for(URI sc: involvedSmartcards) {
BasicSmartcard s = this.getSmartcard(((DeviceProofSpecificationImpl) spec).getUsername(), sc);
if(s == null) {
throw new RuntimeException("Unknown smartcard: " + sc);
}
Integer pin = this.storage.getPin(sc);
Set<URI> creds = proofSpec.getListOfInvolvedCredentials(sc);
Set<URI> sep = proofSpec.getListOfScopeExclusivePseudonyms(sc);
boolean pkProof = proofSpec.isProofOfPublicKey(sc);
ZkProofCommitment zkCom = s.prepareZkProof(pin, creds, sep, pkProof);
if (zkCom == null) {
throw new RuntimeException("Cannot do proof with smartcard " + sc);
}
for(Entry<URI, BigInteger> e: zkCom.commitmentForCreds.entrySet()) {
com.setCommitmentForCredential(sc, e.getKey(), e.getValue());
}
for(Entry<URI, BigInteger> e: zkCom.commitmentForScopeExclusivePseudonyms.entrySet()) {
com.setCommitmentForScopeExclusivePseudonym(sc, e.getKey(), e.getValue());
}
if (zkCom.commitmentForDevicePublicKey != null) {
com.setCommitmentForPublicKey(sc, zkCom.commitmentForDevicePublicKey);
}
}
TimingsLogger.logTiming("ExternalSecretsManager.getPresentationCommitment", false);
return com;
} else {
throw new RuntimeException("Incompatible proof spec");
}
}
@Override
public DeviceProofResponse getPresentationResponse(
DeviceProofCommitment com, BigInteger challenge) {
if(com instanceof DeviceProofCommitmentImpl) {
DeviceProofCommitmentImpl commitment = (DeviceProofCommitmentImpl) com;
DeviceProofSpecificationImpl spec = commitment.getProofSpec();
DeviceProofResponseImpl resp = new DeviceProofResponseImpl();
Set<URI> involvedSmartcards = spec.computeListOfInvolvedSmartcards();
for(URI sc: involvedSmartcards) {
BasicSmartcard s = this.getSmartcard(((DeviceProofCommitmentImpl) com).getProofSpec().getUsername(), sc);
if(s == null) {
throw new RuntimeException("Unknown smartcard: " + sc);
}
Integer pin = this.storage.getPin(sc);
ZkProofResponse zkResp = s.finalizeZkProof(pin, challenge, spec.getListOfInvolvedCredentials(sc), spec.getListOfScopeExclusivePseudonyms(sc));
if (zkResp == null) {
throw new RuntimeException("Cannot do proof with smartcard " + sc);
}
for(Entry<URI, BigInteger> e: zkResp.responseForCourses.entrySet()) {
resp.setResponseForCredentialRandomizer(sc, e.getKey(), e.getValue());
}
if (zkResp.responseForDeviceSecret != null) {
resp.setResponseForDeviceSecretKey(sc, zkResp.responseForDeviceSecret);
}
}
return resp;
} else {
throw new RuntimeException("Incompatible proof spec");
}
}
@Override
public DeviceProofSpecification newProofSpec(String username) {
return new DeviceProofSpecificationImpl(username);
}
@Override
public void associateIssuer(String username, URI deviceUid, URI credIdOnDevice,
URI issuerUriOnDevice) {
BasicSmartcard s = this.getSmartcard(username, deviceUid);
if(s instanceof HardwareSmartcard){
// Nothing to do for real smart cards
}else{
System.out.println("AssociateIssuer called.. What to do now?");
}
}
}