/* * Copyright 2011 Licel LLC. * * 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 com.licel.jcardsim.crypto; import javacard.framework.Util; import javacard.security.CryptoException; import javacard.security.KeyAgreement; import javacard.security.PrivateKey; import org.bouncycastle.crypto.BasicAgreement; import org.bouncycastle.crypto.agreement.ECDHBasicAgreement; import org.bouncycastle.crypto.agreement.ECDHCBasicAgreement; import org.bouncycastle.crypto.digests.SHA1Digest; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; /** * Implementation <code>KeyAgreement</code> based * on BouncyCastle CryptoAPI. * @see KeyAgreement * @see ECDHBasicAgreement * @see ECDHCBasicAgreement */ public class KeyAgreementImpl extends KeyAgreement { BasicAgreement engine; SHA1Digest digestEngine; byte algorithm; ECPrivateKeyImpl privateKey; public KeyAgreementImpl(byte algorithm) { this.algorithm = algorithm; switch (algorithm) { case ALG_EC_SVDP_DH: engine = new ECDHBasicAgreement(); break; case ALG_EC_SVDP_DHC: engine = new ECDHCBasicAgreement(); break; default: CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); break; } digestEngine = new SHA1Digest(); } public void init(PrivateKey privateKey) throws CryptoException { if (privateKey == null) { CryptoException.throwIt(CryptoException.UNINITIALIZED_KEY); } if (!(privateKey instanceof ECPrivateKeyImpl)) { CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } engine.init(((ECPrivateKeyImpl) privateKey).getParameters()); this.privateKey = (ECPrivateKeyImpl) privateKey; } public byte getAlgorithm() { return algorithm; } public short generateSecret(byte[] publicData, short publicOffset, short publicLength, byte[] secret, short secretOffset) throws CryptoException { byte[] publicKey = new byte[publicLength]; Util.arrayCopyNonAtomic(publicData, publicOffset, publicKey, (short) 0, publicLength); ECPublicKeyParameters ecp = new ECPublicKeyParameters( ((ECPrivateKeyParameters) privateKey.getParameters()).getParameters().getCurve().decodePoint(publicKey), ((ECPrivateKeyParameters) privateKey.getParameters()).getParameters()); byte[] num = engine.calculateAgreement(ecp).toByteArray(); // truncate/zero-pad to field size as per the spec: int fieldSize = privateKey.getDomainParameters().getCurve().getFieldSize(); byte[] result = new byte[(fieldSize + 7) / 8]; int numBytes = Math.min(num.length, result.length); Util.arrayCopyNonAtomic( num, (short)( num.length - numBytes), result, (short)(result.length - numBytes), (short)numBytes); Util.arrayFillNonAtomic(result, (short)0, (short)(result.length - numBytes), (byte)0); // apply SHA1-hash (see spec) byte[] hashResult = new byte[20]; digestEngine.update(result, 0, result.length); digestEngine.doFinal(hashResult, 0); Util.arrayCopyNonAtomic(hashResult, (short) 0, secret, secretOffset, (short) hashResult.length); return (short) hashResult.length; } }